Files
ExperionCrawler/diagnosis-checklist.md
2026-05-29 09:49:48 +09:00

8.1 KiB

코드 진단 규칙

코드 진단 요청 시 아래 8단계를 반드시 순서대로 실행한다.
순서를 건너뛰면 오진이 발생한다. 실제 오진 사례는 각 단계 하단에 기재.


STEP 1 — 맥락 파악

질문: 이 파일은 무엇을 하는 파일인가?

  • 파일명·디렉토리 위치로 역할 추정
  • 관련 문서 존재 확인: README, 계획서, CLAUDE.md, .roo.md
  • 아키텍처에서 어느 레이어인지 파악 (진입점 / 서비스 / 워커 / 유틸)

이 단계를 건너뛰면 "의도적 설계"를 "버그"로 오인한다.


STEP 2 — 구조 탐색

도구: find, ls

  • 디렉토리 전체 구조 확인
  • 진단 대상이 의존하는 모듈·파일 목록 파악
  • 설정 파일(config, .env, appsettings.json) 위치 확인

STEP 3 — 코드 읽기 ★ 가장 중요

도구: read_file — 전체 파일, 건너뛰기 금지
기억·요약·이전 대화에 의존하지 말 것

읽는 순서:

  1. 진입점(main, __init__, Program.cs, if __name__ == "__main__") 먼저
  2. 인터페이스·추상 레이어
  3. 구현체 (진단 대상 파일)
  4. 의존 모듈 (필요한 것만)

이 단계를 건너뛴 오진 사례:
pid_worker.py 보고서가 asyncio.to_thread 누락을 HIGH로 지적했으나
실제 파일엔 이미 적용되어 있었음. STEP 3을 건너뛰어 구버전 기준으로 진단한 결과.


STEP 4 — 호출 계층 지도 작성

코드를 읽으면서 다음 구조를 머릿속에(또는 메모로) 그린다:

HTTP 요청
  → endpoint 함수
      → _dispatch()          ← 여기서 try-catch?
          → _tool_a()        ← 여기도 try-catch?
              → 외부 I/O     ← blocking?

이 지도 없이 에러 처리·블로킹을 진단하면 반드시 오진한다.

이 단계를 건너뛴 오진 사례:
_dispatch()가 전체 예외를 일괄 처리하고 있었음에도
하위 함수에 try-catch가 없다는 이유로 "에러 핸들링 불균형(MED)"으로 지적.
계층 지도를 그렸다면 상위에서 잡힌다는 것을 바로 확인할 수 있었음.


STEP 5 — 패턴 매칭 (체크리스트 순회)

우선순위 순서로 확인한다.

🔴 런타임 즉시 실패

체크 항목 판단 기준
[ ] 미정의 변수·함수 참조 임포트 없이 사용하거나 정의 전에 호출
[ ] 잘못된 타입 FastAPI def f(body: dict) → 동작 안 함. Request.json() 또는 Pydantic 사용
[ ] 누락된 app = FastAPI() uvicorn.run(app, ...) 전에 app 미정의
[ ] SIGTERM이 응답보다 먼저 실행 os.killreturn result는 응답이 전달되지 않을 수 있음

🟠 동시성 / 비동기

체크 항목 판단 기준
[ ] async 함수 내 blocking 호출 asyncio.to_thread 없이 파일 I/O·HTTP·OCR 직접 호출 → 이벤트루프 블로킹
[ ] Race Condition if key not in dict → await start() 패턴에서 await 사이 다른 코루틴 진입 가능 → Lock 필요
[ ] one-shot + 동시 요청 종료 로직이 있을 때 동시 요청이 들어오면 진행 중인 요청이 강제 종료됨
[ ] asyncio.sleep 고정값으로 준비 확인 불안정 — 헬스체크 루프로 대체
[ ] asyncio.gather 병렬화 기회 독립적인 await가 순차 나열 → gather로 묶을 수 있는가?

🟠 프로세스 / 리소스

체크 항목 판단 기준
[ ] subprocess stdout=PIPE 데드락 대량 출력 시 파이프 버퍼 가득 참 → DEVNULL 또는 파일 리다이렉션
[ ] 고아 프로세스 메인 프로세스 종료 시 자식이 남는가? atexit 또는 signal 핸들러
[ ] DB 커넥션 누수 with 블록 또는 명시적 close() 없이 커넥션 획득

🟠 에러 처리

체크 항목 판단 기준
[ ] 예외가 사용자에게 노출 최상위 핸들러까지 예외 전파 시 500 + 스택 트레이스 노출 가능
[ ] 예외를 삼킴 except: pass → 디버깅 불가. 최소 logging.error 필요
[ ] 에러 응답 형식 불일치 일부 경로만 {"success": false, "error": "..."} 형식이 다르면 클라이언트 파싱 실패

🟡 보안

체크 항목 판단 기준
[ ] SQL Injection 쿼리를 f-string으로 조합 → parameterized query 사용
[ ] 경로 트래버설 사용자 입력 filepath에서 .. 검증 없음 → 임의 파일 접근
[ ] Command Injection shell=True + 사용자 입력 → shell=False + 리스트 인자
[ ] 민감 정보 로깅 비밀번호·토큰이 에러 메시지에 포함

🟢 코드 구조

체크 항목 판단 기준
[ ] 설정 하드코딩 URL·비밀번호·포트가 코드에 박혀 있음 → 환경 변수 또는 설정 파일
[ ] 미사용 import·변수 실행 경로에서 실제로 사용되지 않는 import

STEP 6 — 교차 검증 ★ 오진 방지 핵심

STEP 5에서 발견한 각 의심 항목마다 아래 4개 질문을 모두 통과해야 보고서에 올린다.

질문 확인 방법 "예"이면
Q1. 이미 수정된 문제인가? 파일 현재 상태 재확인 (grep) 보고서에서 제거
Q2. 다른 레이어에서 처리되고 있는가? STEP 4 호출 계층 지도 재참조 보고서에서 제거 또는 LOW 강등
Q3. 의도적 설계인가? 문서·주석·아키텍처 계획서 확인 보고서에서 제거
Q4. 실제 장애 시나리오가 있는가? 재현 경로를 구체적으로 서술할 수 있는가? 없으면 LOW 강등

이 단계를 건너뛴 오진 사례 (모두 pid_worker.py 보고서):

지적 사항 실제 건너뛴 질문
asyncio.to_thread 누락 (HIGH) 이미 적용되어 있었음 Q1
에러 핸들링 불균형 (MED) _dispatch가 전체 예외를 잡고 있었음 Q2
lru_cache 메모리 고정 (MED) one-shot 워커에서 의도적 싱글톤 패턴 Q3
max_tokens 차이가 중복 (LOW) 도구마다 의도적으로 다른 값 사용 Q3

STEP 7 — 심각도 분류

등급 기준
🔴 HIGH 런타임 즉시 오류, 데이터 손실, 보안 취약점 — 재현 가능한 시나리오 있음
🟠 MED 간헐적 오류, 성능 저하, 동시성 문제 — 특정 조건에서 발생
🟡 LOW 유지보수성, 하드코딩, 스타일 — 동작에는 영향 없음

심각도 결정 전 스스로 확인: "이 문제가 언제, 어떤 조건에서 실제 장애를 일으키는가?"


STEP 8 — 보고서 작성 및 자가 검증

보고서 형식 (항목당 4줄)

### [번호]. [제목] (HIGH / MED / LOW)

**문제**: 어떤 상황에서 무엇이 잘못되는가 (구체적으로)
**근거**: 파일명:줄번호 — 코드 인용 필수
**영향**: 실제로 어떤 장애가 발생하는가
**수정**: 구체적인 수정 코드 또는 방향

보고서에 포함하지 않는 것

  • 이미 수정된 문제 (Q1 탈락)
  • 다른 레이어에서 처리되어 실제 장애가 없는 문제 (Q2 탈락)
  • 의도적 설계를 버그로 지적한 사항 (Q3 탈락)
  • 재현 시나리오 없는 추정 (Q4 탈락)
  • 실측 없는 성능 수치 ("느릴 것이다", "메모리가 많이 든다")

제출 전 자가 검증

  • 각 지적 사항을 "현재 파일 몇 번 줄"로 직접 가리킬 수 있는가?
  • HIGH 항목은 재현 가능한 시나리오를 한 문장으로 말할 수 있는가?
  • 교차 검증 4개 질문을 모두 통과한 항목만 포함되어 있는가?
  • 보고서의 수정 예시가 현재 코드에 아직 적용되지 않은 내용인가?
  • "더 좋은 방법 제안"과 "현재 코드가 틀렸다"를 혼동하지 않았는가?