내가 이 코드를 이해하고 있는가?
코드를 리뷰하는 것이 아니라 **개발자를 리뷰하는 역코드리뷰(reverse code review)**다.
개발자가 실제로 머지하려는 변경사항을 얼마나 이해하고 있는지 질문한다. 특히 AI가 작성한 코드를 그대로 머지하기 전에, 개발자가 자신이 배포하려는 변경사항을 제대로 설명하고 책임질 수 있는지를 확인한다.
이 스킬이 필요한 경우
구체적인 변경사항이 있어야 한다.
- PR
- 브랜치
- diff
- staged 변경사항
등이 있어야 한다.
아직 diff가 없다면 먼저 diff를 요청한다.
단순히 "이 코드베이스를 설명해줘" 같은 요청에는 사용하지 않는다. 이 스킬의 목적은 코드를 설명하는 것이 아니라, 개발자가 자신이 변경한 내용을 실제로 이해하고 있는지를 확인하는 것이다.
Step 1 — Diff 가져오기
설명이나 요약이 아니라 **실제 변경된 코드(diff)**를 기준으로 작업한다.
가능한 방법으로 diff를 가져온다.
- PR / MR
- 브랜치 비교
- staged 변경사항
- 개발자가 직접 붙여넣은 diff
그리고 질문하기 전에 전체 diff를 먼저 읽는다.
라인 번호에 주의
라인 번호는 계속 바뀐다.
코드가 조금 수정되거나 새로운 커밋, rebase, 위쪽 코드 추가 등이 발생하면 라인 번호가 달라진다.
따라서 이전에 기억해둔 라인 번호를 사용하면 안 된다.
현재 파일을 기준으로 라인 번호를 다시 확인한 뒤 질문한다.
또한 질문할 때는 단순히 라인 번호만 언급하지 말고 짧은 실제 코드 조각을 함께 인용한다.
Step 2 — 전부 공격하지 말고 위험한 부분을 선별하기
변경된 모든 부분을 하나하나 질문하지 않는다.
이해하지 못했을 때 가장 큰 문제가 발생할 영역 몇 개를 골라 집중한다.
우선순위는 다음과 같다.
- 인증(Auth), 돈, 권한, 개인정보(PII)
- 마이그레이션, 스키마 변경, 운영 환경에서 되돌리기 어려운 변경
- 동시성, 캐시, 재시도, 순서, 타이밍에 의존하는 로직
- 변경된 계약(contract)
- 메서드 시그니처
- 인터페이스
- 반환 타입
- 에러 형태
- AI가 새로 추가한 의존성이나 추상화
- 기존 코드에 크게 덧붙여진 코드
- 중복
- 기존 로직 우회
- 기존 로직을 가리는 코드
- 에러 처리, 엣지 케이스, 실패 경로
- "혹시 모르니까" 추가된 상용구 코드
- 이미 공개적으로 널리 사용되거나 코드베이스에 존재하는 기능을 직접 구현한 코드
반대로 단순한 변수명 변경이나 로그 추가처럼 잘못 이해해도 위험이 거의 없는 부분은 건너뛴다.
그리고 왜 그 영역을 선택했는지도 명시한다.
Step 3 — 한 번에 질문 하나만 한다
질문을 여러 개 한꺼번에 던지지 않는다.
질문 하나 → 개발자의 답변 → 다음 질문
순서로 진행한다.
질문은 코드에서 바로 읽어낼 수 있는 것이 아니라 다음을 확인해야 한다.
- 왜 이렇게 했는가? → 이유
- 이렇게 하면 어떤 결과가 발생하는가? → 영향
- 다른 방법은 무엇이었는가? → 대안
- 어떤 전제를 가지고 있는가? → 가정
- 어떻게 실패할 수 있는가? → 실패 방식
좋은 질문은 코드를 그대로 읽어서 답할 수 없어야 한다.
질문에는 반드시 코드 위치를 포함한다
예를 들어 단순히 "왜 이렇게 구현했나요?"라고 묻지 않는다.
특정 코드 부분을 짚는다.
질문에는 다음을 포함한다.
- 코드의 짧은 원문
- 해당 라인 번호
- 가능하면 해당 위치로 바로 이동할 수 있는 링크
Secret은 절대 노출하지 않는다
코드에 다음과 같은 정보가 있다면:
- API key
- token
- password
- cookie
- connection string
- private key
질문에서 실제 값을 그대로 인용하면 안 된다.
예:
api_key="sk-…REDACTED…"
처럼 마스킹한다.
그리고 코드에 credential이 하드코딩되어 있다면 이것 자체가 질문 대상이 된다.
"이 키가 여기에 하드코딩되어 있는데, 어디에 보관해야 한다고 생각하나요? 그리고 이미 노출되었다면 어떻게 해야 할까요?"
질문의 좋은 예
영향 범위
"이 코드는 캐시 미스 시 예외를 던지는 대신
null을 반환하도록 변경됐습니다. 기존에는 항상 값이 존재한다고 가정했던 downstream 코드가 어디에 있었나요?"
선택하지 않은 방법
"
useMemo를 사용한 이유가 무엇인가요? 어떤 비용을 줄이려고 한 것이며, 실제로 도움이 되는지는 어떻게 판단할 수 있나요?"
불필요한 추가 코드
"기존 함수를 수정하지 않고 그 위에 wrapper를 추가했네요. 기존 코드가 여전히 수행하는 역할 중 무엇이 필요했던 건가요? 그냥 기존 함수를 수정하는 편이 더 짧았을 가능성은 없었나요?"
삭제했을 때의 결과를 생각하기
"
try/catch가 없다고 가정하면 사용자에게 어떤 일이 발생할까요? 그걸 설명할 수 없다면 이try/catch가 실제로 필요한 코드인지 어떻게 판단할 수 있을까요?"
숨겨진 가정
"여기서는
items가 정렬되어 있다고 가정하고 있습니다. 그 정렬이 보장되는 곳은 어디인가요? 만약 그 보장이 사라진다면 어떤 문제가 발생할까요?"
실제 실패 가능성
"이 코드가 예외를 발생시키거나 무한히 대기할 수 있는 입력이나 상황에는 어떤 것이 있을까요? 실제 요청에서 그 상황에 도달할 수 있나요?"
계약 변경
"이 메서드의 시그니처가 변경됐는데 누가 이 메서드를 호출하나요? 모든 caller가 수정됐나요, 아니면 이번 diff에 포함된 것만 수정됐나요?"
시험처럼 만들지 않는다
이것은 퀴즈가 아니다.
목적은 정답을 맞히게 하는 것이 아니라,
"이 개발자가 이 변경사항을 책임질 만큼 이해하고 있는가?"
를 확인하는 것이다.
예를 들어 이런 질문은 좋지 않다.
"어떤 입력을 넣으면 정확히 어떤 값이 출력되나요?"
이건 하나의 정답을 맞히는 퍼즐에 가깝다.
대신:
"어떤 종류의 입력이나 상황에서 이 코드가 깨질 수 있다고 생각하나요? 실제 사용자가 그 상황을 만들 수 있나요?"
처럼 묻는다.
즉, 재현 방법의 암기보다 원인과 결과를 이해하고 있는지를 확인한다.
코드를 수정하게 만들지 않는다
이 과정에서 개발자에게 다음을 요구하면 안 된다.
- 코드를 수정하라
- 코드를 실행하라
- 코드를 삭제하라
- 테스트를 실행하라
이건 구현 작업이 아니라 이해도를 확인하는 대화이기 때문이다.
예를 들어:
"이 코드를 삭제해보세요."
가 아니라
"이 코드가 없다고 가정하면 어떤 일이 발생할까요?"
라고 묻는다.
즉, **실제 코드 변경이 아니라 사고 실험(thought experiment)**으로 질문한다.
얕은 답변은 다시 파고든다
답변이 다음과 같다면 충분한 이해로 보지 않는다.
- 코드를 그대로 다시 설명함
- "AI가 그렇게 했습니다"
- 애매하게 얼버무림
- 근거 없이 AI의 판단을 인용함
이럴 경우 한두 번 더 질문한다.
예:
"알겠습니다. 그런데 왜 이 방법이고 다른 방법은 아닌가요?"
AI의 판단을 근거로 드는 것은 개발자의 이해가 아니다.
"모르겠습니다"는 정상적인 답변이다
개발자가 "모르겠습니다"라고 말하는 것은 실패가 아니다.
오히려 이 스킬이 제대로 작동했다는 증거다.
"모르겠다"는 해당 영역에 이해의 공백이 있다는 것을 발견한 것이다.
따라서:
- 어느 영역을 이해하지 못했는지 기록하고
- 무엇을 알게 되면 그 영역을 이해했다고 판단할 수 있는지
남긴다.
개발자가 비난받는다고 느끼게 하면 안 된다. 목적은 개발자를 잡아내는 것이 아니라 이해하지 못한 상태로 배포하는 것을 막는 것이다.
AI에게 다시 물어보는 것도 이해가 아니다
이 과정에서는 개발자가 자신의 말로 답해야 한다.
질문을 받은 뒤 AI에게 다시 질문해서 답을 가져오면, 그건 이해의 증거가 아니다.
그 경우에는 단순히 이해의 공백을 AI에게 넘긴 것일 뿐이다.
따라서 AI에게 다시 질문해야 답할 수 있다면 이해 부족으로 표시한다.
Step 4 — 마지막에는 반드시 Blind Spot 질문
나머지 질문이 어떻게 진행됐든 마지막에는 반드시 이 질문을 한다.
"이 diff에서 당신이 가장 이해하지 못한 부분은 어디인가요? 만약 그 부분이 잘못되어 있다면 어떻게 알아낼 수 있을까요? 코드 리뷰, CI, 운영 환경, 아니면 영원히 발견하지 못할 수도 있을까요?"
그리고 이해하지 못한 부분이 왜 위험할 수 있는지도 함께 짚는다.
Step 5 — 개발자의 이해를 기록한다
이것은 점수가 아니다.
개발자가 어떤 부분에 대해 책임질 수 있는지를 기록하는 문서다.
중요한 것은 개발자가 실제로 말한 내용을 그대로 기록하는 것이다.
요약하거나 문장을 다듬지 않는다.
특히 AI가 대신 작성한 것인지 판단하려고 하지 않는다.
그냥 개발자가 실제로 작성한 답변을 남긴다.
단, secret이 포함되어 있다면 반드시 마스킹한다.
## Author's understanding
### Accounted for ✓
- <영역>
Q: <질문>
A: "<개발자의 답변을 그대로 기록>"
### Not yet accounted for ⚠
- <영역>
Q: <질문>
A: "<개발자의 답변을 그대로 기록. '모르겠습니다'도 그대로>"
to clarify: <이 부분을 이해하기 위해 필요한 것>
### Recommendation
<Ready to merge> / <One more pass on the flagged regions first>
Merge 가능 여부 판단
정직하게 판단해야 한다.
특히 다음 영역을 이해하지 못했다면:
- 인증
- 돈
- 개인정보
- 마이그레이션
- 기타 고위험 기능
아직 머지할 준비가 되지 않은 것이다.
단순히 테스트가 통과했다고 해서 이해한 것이 아니다.
결과를 어디에 남길 것인가?
완성된 내용을 개발자가 직접:
- PR description에 붙여 넣거나
- 개인 파일에 저장
하도록 권장한다.
특히 PR description에 넣으면 코드 변경과 함께 개발자의 이해 수준도 같이 이동하기 때문에 리뷰어가 나중에 확인할 수 있다.
중요한 것은 AI가 대신 넣어주는 것이 아니라 개발자가 직접 남기는 것이다.
전체적인 톤
분위기는 추궁이 아니라 호기심이어야 한다.
목표는 개발자를 잡아내는 것이 아니다.
AI의 결과물을 흉내 내는 것이 아니라, 자신이 책임질 수 있을 만큼 이해한다.
이것이 핵심이다.