🔍 파이썬 레포지토리 자동 분석 스크립트: 실행 예시 + 상세 해설 + 전체 코드
키워드: 파이썬 코드 분석, 레포지토리 헬스체크, LOC, 의존성 분석, 복잡도, 유지보수성, pytest, AST, 그래프 시각화
파이썬 프로젝트의 상태를 한 번에 파악하고 싶을 때가 많죠. 이 글은 그런 순간을 위해 준비한 **자동 분석 스크립트(analyze_repo.sh)**의 실행 예시, 상세 동작 해설, 그리고 전체 코드까지 한 번에 제공합니다. 스크립트는 pygount/radon/pydeps/pipreqs/pytest/ripgrep 등 도구를 조합해 레포지토리 헬스 리포트를 생성합니다.

🚀 한눈에 보는 기능 요약
- 디렉터리 트리 & 파일 크기: tree / find, du
- LOC(코드 라인) 통계: pygount (언어별/주석/빈 줄)
- 의존성 분석: pipreqs(추정), pipdeptree(실제 설치 트리)
- 복잡도/유지보수성: radon cc, radon mi
- 임포트 그래프: pydeps → Graphviz SVG
- 테스트 수집: pytest --collect-only
- TODO/FIXME 스캔: ripgrep(또는 grep)
- AST 인덱스: 함수/클래스를 JSON으로 색인
- Fallback 의존성 수집기: 문법 오류 파일도 관대하게 import 추출
⚙️ 설치 (1회)
python -m venv .venv && source .venv/bin/activate
pip install --upgrade pip
pip install pipreqs pipdeptree radon pydeps pygount pytest
# 의존(권장)
# - graphviz: pydeps가 SVG/DOT 출력에 필요
# - tree: 디렉터리 맵 생성
# - ripgrep: TODO/FIXME 검색 가속(없으면 grep로 대체)
# Fedora/RHEL/CentOS:
# sudo dnf install graphviz tree ripgrep -y
# Ubuntu/Debian:
# sudo apt-get update && sudo apt-get install graphviz tree ripgrep -y
▶️ 사용 방법
chmod +x analyze_repo.sh
./analyze_repo.sh
업로드 권장 세트(요약 리포트)
- analysis_out/repo_map.txt, file_sizes.txt, loc_summary.txt
- pipdeptree.txt, complexity.json, maintainability.json
- imports.svg(생성 시), test_collection.txt, todos.txt, ast_index.json
- requirements_inferred.txt 또는 requirements_inferred_fallback.txt
🧩 실행 예시 & 결과 해설 (요점)
- 레포 트리/파일 크기 → 구조 파악 & 용량 상위 파일 확인
- LOC 통계 → 코드/주석/빈 줄 밸런스 진단
- 의존성 → “실제 설치 트리” vs “코드가 요구하는 패키지” 비교
- 복잡도/유지보수성 → 리팩터링 우선순위 선정 근거
- 임포트 그래프 → 결합도 높은 모듈 식별
- 테스트 수집 → 테스트 존재/명명 규칙 파악
- TODO/FIXME → 작업 백로그 추출
- AST 인덱스 → 함수/클래스 탐색 및 문서화 자동화 토대
💡 실전 팁
- IGNORE_PIPREQS에 문법 오류/실험용 스크립트를 추가하면 pipreqs 실패를 줄일 수 있습니다.
- --max-bacon=2로 import 그래프 깊이 제한 → 복잡한 그래프 가독성 개선.
- CI 파이프라인에 이 스크립트를 연결해 정기 리포트를 만들면 팀 생산성이 올라갑니다.
📜 analyze_repo.sh 전체 코드
아래 코드를 레포지토리 루트에 analyze_repo.sh로 저장하세요.
#!/usr/bin/env bash
set -euo pipefail
# === 설정 ===
OUT="analysis_out"
IGNORE_PIPREQS="main_bat.py" # pipreqs가 깨지는 파일/폴더를 ,로 추가(예: "main_bat.py,build,dist")
SKIP_DIRS=".git,.venv,__pycache__,.mypy_cache,.pytest_cache,${OUT}"
PY_EXT=".py"
mkdir -p "$OUT"
# === 유틸 ===
have() { command -v "$1" >/dev/null 2>&1; }
note() { echo -e "\n[INFO] $*"; }
warn() { echo -e "\n[WARN] $*" >&2; }
fail() { echo -e "\n[ERROR] $*" >&2; exit 1; }
# === 1) 레포 트리 & 파일 크기 ===
note "레포 맵/파일 크기 수집..."
{
echo "### REPO TREE"
if have tree; then
tree -a -I "$(echo "$SKIP_DIRS" | tr , '|')" || true
else
warn "'tree'가 없어 'find'로 대체합니다(간략 출력). 'sudo dnf/apt install tree' 권장."
find . -path "./.git" -prune -o -path "./${OUT}" -prune -o -print
fi
} > "$OUT/repo_map.txt"
du -ah --max-depth=2 2>/dev/null | sort -h > "$OUT/file_sizes.txt" || {
warn "du 옵션 미지원 환경. 대체 모드로 파일 크기 집계."
find . -type f -not -path "./${OUT}/*" -not -path "./.git/*" -exec du -ah {} + | sort -h > "$OUT/file_sizes.txt" || true
}
# === 2) LOC/언어 통계 (pygount) ===
note "LOC/언어 통계(pygount)..."
pygount . \
--format=summary \
--folders-to-skip "$SKIP_DIRS" \
> "$OUT/loc_summary.txt"
# (선택) 파일별 상세 필요 시 주석 해제
# pygount . \
# --format=cloc-xml \
# --folders-to-skip "$SKIP_DIRS" \
# > "$OUT/loc_by_file.cloc.xml"
# === 3) 의존성(추정 + 실제 트리) ===
note "의존성 추정(pipreqs) 및 설치 트리(pipdeptree)..."
# pipreqs는 문법 에러 파일에서 중단될 수 있어 --ignore 사용, 실패해도 계속
pipreqs . \
--force \
--encoding=utf-8 \
--ignore "$IGNORE_PIPREQS" \
--savepath "$OUT/requirements_inferred.txt" || warn "pipreqs 실패(무시하고 계속). --ignore 목록을 늘리거나 fallback 사용 권장."
# requirements.txt가 있다면 설치 후 의존 트리 출력(실패 허용)
if [[ -f requirements.txt ]]; then
pip install -r requirements.txt || warn "requirements 설치 중 경고 발생(계속 진행)."
fi
pipdeptree > "$OUT/pipdeptree.txt" || warn "pipdeptree 실패(계속)."
# === 4) 복잡도/유지보수성 (radon) ===
note "복잡도/유지보수성(radon)..."
radon cc -s -a -j . > "$OUT/complexity.json" || warn "radon cc 실패(계속)."
radon mi -s -j . > "$OUT/maintainability.json" || warn "radon mi 실패(계속)."
# === 5) import 그래프 (pydeps) ===
note "import 그래프(pydeps → SVG)..."
if have dot; then
:
else
warn "graphviz(dot) 미설치. 'sudo dnf/apt install graphviz' 권장. 시도는 해봅니다."
fi
# 최신 pydeps: -T svg -o 파일
if have pydeps; then
pydeps . --noshow --max-bacon=2 -T svg -o "$OUT/imports.svg" || warn "pydeps 그래프 생성 실패(계속)."
else
warn "pydeps 미설치. 'pip install pydeps' 후 재시도 가능."
fi
# === 6) 테스트 수집 (pytest) ===
note "pytest 테스트 수집..."
pytest --collect-only -q > "$OUT/test_collection.txt" || warn "pytest 수집 실패(계속)."
# === 7) TODO/FIXME/HACK/XXX 검색 ===
note "TODO/FIXME/HACK/XXX 키워드 검색..."
if have rg; then
rg -n "TODO|FIXME|HACK|XXX" --glob "!${OUT}/**" --glob "!.git/**" > "$OUT/todos.txt" || true
else
grep -RInE "TODO|FIXME|HACK|XXX" . \
--exclude-dir="$OUT" --exclude-dir=".git" --exclude-dir=".venv" \
> "$OUT/todos.txt" || true
fi
# === 8) AST 인덱스(함수/클래스) ===
note "AST 인덱스 생성(함수/클래스)..."
python - << 'PY' || warn "AST 인덱스 생성 중 문제(계속)."
import os, ast, json
skip = set(s.strip() for s in ".git,.venv,__pycache__,.mypy_cache,.pytest_cache,analysis_out".split(","))
index=[]
for root,_,files in os.walk("."):
if any(k in root for k in skip):
continue
for f in files:
if f.endswith(".py"):
p=os.path.join(root,f)
try:
with open(p,"r",encoding="utf-8",errors="ignore") as fh:
src = fh.read()
try:
m=ast.parse(src, filename=p)
except SyntaxError as e:
# 문법 에러는 기록만 남기고 건너뜀
index.append({"path":p,"error":f"SyntaxError: {e}"})
continue
funcs=[n.name for n in ast.walk(m) if isinstance(n,ast.FunctionDef)]
clss=[n.name for n in ast.walk(m) if isinstance(n,ast.ClassDef)]
index.append({"path":p,"functions":funcs,"classes":clss})
except Exception as e:
index.append({"path":p,"error":str(e)})
open("analysis_out/ast_index.json","w",encoding="utf-8").write(json.dumps(index,ensure_ascii=False,indent=2))
PY
# === 9) (옵션) pipreqs Fallback: 관대한 import 수집기 ===
note "pipreqs Fallback(관대한 import 수집기) 실행..."
python - << 'PY' || warn "Fallback import 수집 실패(계속)."
import os, ast, tokenize, io
root="."
skip=set(s.strip() for s in ".git,.venv,__pycache__,.mypy_cache,.pytest_cache,analysis_out".split(","))
imports=set()
stdlib={"os","sys","json","re","subprocess","pathlib","typing","logging","datetime","time","math","itertools","functools","collections","dataclasses","ast","tokenize","io","enum","statistics","decimal","random","uuid"}
def tolerant_parse(text):
try:
return ast.parse(text)
except SyntaxError:
return None
for r,_,fs in os.walk(root):
if any(k in r for k in skip):
continue
for f in fs:
if f.endswith(".py"):
p=os.path.join(r,f)
try:
txt=open(p,"r",encoding="utf-8",errors="ignore").read()
except Exception:
continue
m=tolerant_parse(txt)
if m:
for n in ast.walk(m):
if isinstance(n, ast.Import):
for a in n.names:
imports.add(a.name.split(".")[0])
elif isinstance(n, ast.ImportFrom) and n.module:
imports.add(n.module.split(".")[0])
else:
# 간단 라인 스캔(문법 에러 파일용)
for line in txt.splitlines():
line=line.strip()
if line.startswith("import "):
for part in line[len("import "):].split(","):
imports.add(part.strip().split(".")[0])
elif line.startswith("from ") and " import " in line:
mod=line.split()[1]
imports.add(mod.split(".")[0])
third=sorted(m for m in imports if m and m not in stdlib)
open("analysis_out/requirements_inferred_fallback.txt","w",encoding="utf-8").write("\n".join(third))
print(f"[fallback] third-party candidates: {len(third)} → analysis_out/requirements_inferred_fallback.txt")
PY
# === 완료 요약 ===
note "완료! 산출물 목록:"
ls -lh "$OUT" || true
echo
echo "업로드 권장 파일:"
echo "- $OUT/repo_map.txt"
echo "- $OUT/file_sizes.txt"
echo "- $OUT/loc_summary.txt"
echo "- $OUT/pipdeptree.txt"
echo "- $OUT/complexity.json"
echo "- $OUT/maintainability.json"
echo "- $OUT/imports.svg (있으면)"
echo "- $OUT/test_collection.txt"
echo "- $OUT/todos.txt"
echo "- $OUT/ast_index.json"
echo "- $OUT/requirements_inferred.txt (있으면)"
echo "- $OUT/requirements_inferred_fallback.txt (보조)"
✅ 요약 & 결론
- 이 스크립트 하나로 프로젝트 상태 진단을 자동화할 수 있습니다.
- CI에 붙이면 정기 건강 리포트를 자동 생성할 수 있고, 신규 인수/레거시 관리/리팩터링 기획에 탁월합니다.
- 필요시 파일별 LOC CSV나 호출 그래프 생성기 등 확장도 손쉽게 추가 가능합니다.
본 글은 chatgpt를 활용하여 작성되었습니다.
'etc' 카테고리의 다른 글
| ssh key로 서버 접속 (0) | 2020.05.18 |
|---|---|
| bash Prompt 설정 (0) | 2020.05.18 |
| ubuntu user 생성 (0) | 2020.04.20 |
| git command (0) | 2020.03.16 |
| ansible로 ubuntu user 생성 (0) | 2020.02.19 |


