Ch.9 CS 관점 코드 리뷰 체크리스트¶
< AI가 자주 틀리는 패턴 | 유사 사례와 키워드 정리 >
앞에서 AI가 자주 틀리는 6가지 패턴을 봤다. 이번에는 AI가 만든 코드를 받았을 때 체계적으로 검토할 수 있는 체크리스트를 정리한다. 전부 다 확인할 필요는 없다. 코드의 성격에 따라 해당되는 항목만 확인하면 된다.
체크리스트 1: 동시성 (Ch.5 관련)¶
"이 코드가 동시에 여러 번 실행되면 괜찮은가?"
- [ ] 공유 자원(전역 변수, DB 레코드, 파일)에 대한 읽기-수정-쓰기 패턴이 보호되고 있는가?
- [ ] DB 작업이라면
SELECT ... FOR UPDATE또는 Optimistic Locking이 적용되어 있는가? - [ ] 여러 Lock을 사용한다면 Lock Ordering이 통일되어 있는가? (Deadlock 방지)
해당하는 코드: 재고 차감, 잔액 차감, 카운터 증감, 상태 변경 등 "읽고 바꾸고 쓰는" 모든 코드
체크리스트 2: 성능 / 복잡도 (Ch.10 관련)¶
"데이터가 늘어나면 이 코드가 느려지지 않는가?"
- [ ] 리스트를 전체 순회하는 검색이 있는가? → Set/Dict로 바꿀 수 있는가?
- [ ] 중첩 반복문(O(n^2))이 있는가? → 정렬 후 이분 탐색, 또는 Hash 기반으로 바꿀 수 있는가?
- [ ] 메모리에 전체 데이터를 올리고 있는가? → DB 쿼리에서 필터링하거나 pagination할 수 있는가?
- [ ] 같은 DB 쿼리를 반복 호출하는가? → 캐싱이나 JOIN으로 합칠 수 있는가?
해당하는 코드: 목록 조회, 검색, 필터링, 집계 등 데이터 크기에 영향받는 코드
체크리스트 3: I/O 패턴 (Ch.3 관련)¶
"이 코드의 I/O 패턴이 적절한가?"
- [ ] CPU Bound 작업을 async로 처리하고 있지 않은가?
- [ ] I/O Bound 작업을 동기로 직렬 처리하고 있지 않은가?
- [ ] FastAPI에서 동기 라이브러리를
async def로 감싸고 있지 않은가? - [ ] 외부 API를 여러 개 호출한다면 병렬(asyncio.gather)로 바꿀 수 있는가?
해당하는 코드: 외부 API 호출, 파일 I/O, 이미지/비디오 처리, DB 쿼리
체크리스트 4: DB / 쿼리 (Ch.13~16 관련)¶
"이 DB 코드가 운영 환경에서 문제가 되지 않는가?"
- [ ] ORM이 N+1 쿼리를 발생시키지 않는가? (로그 확인)
- [ ] WHERE 절에 인덱스가 걸린 컬럼을 쓰고 있는가?
- [ ] Transaction 범위가 적절한가? (너무 넓으면 Lock 경합, 너무 좁으면 정합성 문제)
- [ ] Connection을 제대로 반환(close)하고 있는가?
해당하는 코드: ORM 쿼리, Raw SQL, Transaction 처리, Connection 관리
체크리스트 5: 에러 핸들링¶
"에러가 났을 때 이 코드가 안전하게 동작하는가?"
- [ ]
except Exception으로 모든 에러를 뭉뚱그려 잡고 있지 않은가? - [ ] 에러 발생 시 Transaction이 롤백되는가?
- [ ] 재시도 가능한 에러(네트워크 일시 장애)에 대한 retry 로직이 있는가?
- [ ] 에러 메시지에 민감한 정보(DB 정보, 스택 트레이스)가 노출되지 않는가?
체크리스트 6: 설계 적절성 (Ch.20 관련)¶
"이 코드의 구조가 적절한가?"
- [ ] 구체 구현이 하나뿐인 불필요한 추상화(인터페이스, 팩토리)가 있는가?
- [ ] 하나의 함수/클래스가 너무 많은 일을 하고 있는가?
- [ ] 테스트하기 어려운 구조인가? (외부 의존성이 함수 내부에 하드코딩)
- [ ] 설정 값이 코드에 하드코딩되어 있는가?
체크리스트 7: 보안 (Ch.23 관련)¶
"이 코드가 공격 시나리오에서 안전한가?"
- [ ] 사용자 입력이 검증되는가? (타입, 길이, 형식, 화이트리스트)
- [ ] SQL 쿼리에 사용자 입력이 문자열 포맷으로 들어가지 않는가? (Parameterized Query)
- [ ] 비밀번호가 bcrypt/scrypt/Argon2 같은 비밀번호 전용 해시로 저장되는가?
- [ ] 시크릿(API Key, DB 비밀번호)이 코드에 하드코딩되어 있지 않은가?
- [ ] 에러 메시지나 응답에 내부 정보(스택 트레이스, DB 구조)가 노출되지 않는가?
체크리스트 8: 테스트 가능성¶
"이 코드를 테스트할 수 있는가?"
- [ ] 외부 의존성(DB, 외부 API, 파일 시스템)이 주입 가능한 구조인가?
- [ ] 시간/난수 같은 비결정적 요소가 주입 가능한가? (
datetime.now()를 함수 내부에서 직접 호출하면 테스트 어려움) - [ ] 부수효과(side effect)와 순수 로직이 분리되어 있는가?
- [ ] 함수가 너무 많은 것을 하지 않아서, 작은 단위로 테스트할 수 있는가?
CS 영역별 체크 항목 요약¶
지금까지 항목을 한 표로 정리하면 다음과 같다. 리뷰할 때 빠르게 훑는 용도다.
| 영역 | 핵심 질문 | 키워드 / 안티패턴 | 관련 챕터 |
|---|---|---|---|
| 복잡도 (시간/공간) | 데이터가 늘어나면 어디서 터질까? | O(n^2), Nested Loop, 전체 메모리 적재 | Ch.10 |
| I/O 패턴 | sync/async/batch가 적절한가? | CPU Bound를 async, I/O 직렬 호출 | Ch.3 |
| 동시성 | 동시에 실행되면 깨지지 않나? | Race Condition, Lock 누락, Deadlock | Ch.5 |
| DB / 쿼리 | 운영 데이터에서도 빠른가? | N+1, OFFSET pagination, Index 미사용 | Ch.13~16 |
| 에러 핸들링 | 실패 시에도 안전한가? | except Exception, 트랜잭션 미롤백 | Ch.5, Ch.15 |
| 트랜잭션 경계 | 비즈니스 단위가 원자적인가? | 트랜잭션 쪼개기, 트랜잭션 안 외부 API | Ch.15, Ch.16 |
| 보안 | 공격자 입장에서 뚫을 데가 있나? | SQL Injection, XSS, 평문 비밀번호 | Ch.23 |
| 멀티 프로세스 | 멀티 워커에서도 정합성이 유지되나? | 전역 dict 캐시, 프로세스 내 카운터 | Ch.5, Ch.17 |
| 관측성 | 장애 시 추적할 단서가 있나? | 로그/메트릭 누락, request_id 부재 | Ch.19 |
| 설계 적절성 | 지금 필요한 만큼만 추상화했나? | YAGNI 위반, God Class | Ch.20 |
| 테스트 가능성 | 단위로 검증할 수 있나? | 하드코딩된 의존성, 시간/난수 직접 호출 | Ch.21 |
코드 한 줄을 모든 영역에서 검토할 필요는 없다. 코드의 성격이 결정한다. 예를 들어 결제/재고 같은 트랜잭션 코드는 동시성·트랜잭션 경계·보안이 우선이다. 목록 조회 API는 복잡도·DB 쿼리·페이지네이션이 우선이다. 리뷰의 첫 단계는 "이 코드의 성격이 뭔지 정하고, 어느 영역에 가중치를 둘지 결정"하는 거다.
이 체크리스트를 AI에게 주는 것도 방법이다¶
재미있는 점은, 이 체크리스트 자체를 AI 프롬프트에 넣을 수 있다는 거다.
이 코드를 아래 관점에서 리뷰해줘:
1. 동시성: Race Condition 가능성이 있는가?
2. 성능: O(n^2) 이상의 복잡도를 가진 로직이 있는가?
3. I/O: CPU Bound와 I/O Bound가 구분되어 처리되는가?
4. DB: N+1 쿼리나 인덱스 미활용이 있는가?
5. 보안: SQL Injection, XSS 취약점이 있는가?
이렇게 하면 AI가 자기가 만든 코드를 스스로 검토한다. 물론 100% 정확하진 않지만, 체크리스트 없이 "코드 리뷰해줘"라고 하는 것보다 훨씬 정확한 피드백이 나온다.
이것도 결국 Ch.7에서 말한 "CS 키워드가 프롬프트의 방향을 결정한다"의 연장선이다. 리뷰에서도 키워드가 방향을 잡아준다.
좀 더 강력한 메타 프롬프트 예시¶
위의 5줄짜리 체크리스트도 효과적이지만, 운영 코드를 리뷰할 때는 더 구체적인 프롬프트를 쓰면 정확도가 올라간다. 두 가지 패턴을 소개한다.
메타 프롬프트 A: 시나리오 기반¶
아래 코드를 리뷰해줘. "동작하는가"가 아니라 "운영에서 안전한가"를 본다.
다음 시나리오를 가정하고 각각의 문제를 지적해줘:
- 동시 요청 100건이 같은 자원에 들어올 때
- 데이터가 100만 건 쌓였을 때
- 외부 API가 일시적으로 실패할 때
- 멀티 프로세스(uvicorn --workers 4) 환경일 때
- 악의적인 사용자가 입력을 조작할 때
각 시나리오마다:
- 이 코드가 어떻게 깨지는가
- 어느 줄이 문제인가
- 어떻게 고쳐야 하는가
순서로 답해줘.
기능 리뷰가 아니라 시나리오 기반으로 묻기 때문에 AI가 "그냥 동작한다"는 답을 내놓지 않게 된다.
메타 프롬프트 B: 역할 기반¶
너는 시니어 백엔드 엔지니어다. 이 코드를 PR 리뷰한다고 생각하고,
LGTM을 줄 수 없는 이유를 모두 찾아줘.
체크 영역:
- 동시성 (Race Condition, Lock, Transaction Isolation)
- 복잡도 (Time/Space Complexity, N+1)
- I/O 패턴 (CPU Bound vs I/O Bound, blocking)
- 보안 (SQL Injection, XSS, 시크릿 노출, 비밀번호 해싱)
- 관측성 (로그, 메트릭, 추적 가능한 식별자)
- 설계 (YAGNI, 단일 책임, 테스트 가능성)
각 지적사항은 다음 형식으로:
[심각도: blocker/major/minor] [영역] 문제 설명
근거: 어떤 CS 개념 때문에 문제가 되는가
제안: 구체적인 수정 방향
심각도와 근거를 강제하면 AI가 "그냥 의견"을 내놓지 못한다. 모든 지적이 CS 개념과 연결되기 때문에 사람이 검증하기도 쉽다.
한계와 주의점¶
메타 프롬프트가 만능은 아니다. 두 가지를 기억해야 한다.
첫째, AI가 잘못 만든 코드를 AI가 리뷰하면 같은 사각지대를 공유한다. 예를 들어 AI가 OFFSET 페이지네이션을 자연스럽게 생성했다면, AI 리뷰도 그걸 자연스럽게 통과시킬 가능성이 있다. AI 리뷰는 1차 필터로 쓰고, 사람의 CS 시각으로 마지막을 본다.
둘째, AI 리뷰가 거짓 양성(false positive)을 내는 경우도 흔하다. "Race Condition 가능성이 있다"고 했지만 실제로는 단일 스레드 컨텍스트라 문제가 안 되는 경우 같은 거다. 지적을 그대로 받아들이지 말고 근거를 검토하는 습관이 필요하다.