Claude Code 리뷰가 모두 통과했음에도 누락된 내용을 발견한 원인: 차이점(Diff) 때문이었다
요약
Claude Code의 코드 리뷰 시스템이 여러 겹으로 통과했음에도 불구하고, 후속 작업에 필요한 중요한 인수인계 내용이 누락된 사례를 분석했습니다. 이 결함은 변경된 파일(diff)만 검사하는 구조적 한계 때문에 발생했으며, 단순히 리뷰 과정을 강화해서는 해결할 수 없습니다.
핵심 포인트
- 결함은 '여기에 적어야 하는데 적지 않음'이라는 정보의 부재에서 기인합니다.
- 리뷰어는 오직 '차이점(diff)'에 포함된 변경 사항만 검사하도록 유도됩니다.
- 여러 겹의 리뷰를 거쳐도, 제공되는 컨텍스트가 부족하면 결함을 발견하기 어렵습니다.
- 진정한 해결책은 리뷰 과정 개선보다 정보 전달 구조 자체를 설계하는 데 있습니다.
Claude Code의 서브 에이전트에게 코드 리뷰를 시켰는데, 리뷰는 통과했지만 '적어야 할 곳에 적지 않은' 부분이 남아있는 경우가 있었습니다. 게다가 한 번뿐만이 아니라, 구현 단계별 리뷰와 마지막 브랜치 전체 리뷰까지 모두 통과했습니다.
왜 모든 것이 통과했는지, 제가 가진 장치를 이용해 어떻게 막았는지 설명하겠습니다. 얻을 수 있는 교훈은, 이러한 종류의 결함은 아무리 여러 겹으로 리뷰를 하더라도 발견하기 어렵다는 구조와, 이를 해결하는 방법이 하나라는 것입니다.
무슨 일이 일어났는가
제가 사업에서 운영하는 도구에 특정 작업 변경을 했습니다. 그 변경으로 인해 후속 작업이 주기적으로 교체하는 설정값이 1종류에서 2종류로 늘어났습니다. 후속 작업을 담당하는 에이전트는 이를 모르면 작업의 전제를 잘못 잡게 됩니다.
그래서 구현한 에이전트가 인수인계를 작성했습니다. 작업 기록(하네스(Harness)가 작업별로 남기는 로그), 해당 작업의 설계서, PR 본문 세 군데에 걸쳐요. 설계서에는 '후속 작업에 전달한다'는 내용이 어딘가에 나오고, PR 본문에도 '인수인계처'라는 줄이 있습니다.
하지만 후속 작업을 담당하는 에이전트가 가장 먼저 읽는 파일에는 작성되어 있지 않았습니다.
리뷰는 모두 통과했습니다. PR은 병합되었고, 후속 작업에 착수할 때 사람(저 자신)이 읽고 발견했습니다.
전제: 제 하네스의 형태
여기서부터 구성에 대해 이야기하겠습니다.
Claude Code에서 superpowers의 진행 방식(brainstorming → writing-plans → subagent-driven-development)을 척추뼈로 삼아, everything-claude-code (ECC)의 리뷰어를 추가하여 사용하고 있습니다. 이 조합을 어떻게 분담시키고 있는지에 대해서는 블로그에 글을 썼습니다.
- '작업(Task)'은 하나씩 Markdown 파일로 가지고 있습니다. 하나의 작업이 하나의 브랜치와 하나의 PR이 되며, 세션도 바뀝니다. 작업 담당 에이전트는 그 작업의 md 파일을 가장 먼저 읽고 작업을 시작합니다. 따라서 후속 작업으로의 인계는 후속 작업의 md에 작성하는 것이 올바른 장소입니다.
- 하나의 작업 안에서는 계획 단계에서 작은 '단계(Step)'로 나눕니다. 구현은 단계별로 다른 서브 에이전트가 수행합니다. 구현한 에이전트의 컨텍스트는 다음 단계나 다음 작업으로 전달되지 않습니다.
- 리뷰는 브랜치 전체를 보는 최종 리뷰를 반드시 거칩니다. 이때는 단계별 리뷰도 진행했으며, 그것 역시 통과했습니다.
- 리뷰어에게 제공하는 것은 superpowers가 만드는
review-package파일입니다. 그 안의 내용은 커밋 목록,git diff --stat, 그리고git diff -U10세 가지입니다.
요컨대, 리뷰어가 보고 있는 것은 차이점(diff)입니다.
왜 모두 통과했는가
차이점에는 변경된 파일만 나타납니다.
증거 로그, 설계서, PR 본문. 이 세 가지는 바뀌었기 때문에 차이점에 포함됩니다. 그곳에는 인계 내용이 있습니다. 리뷰어는 그것을 읽고 '인계가 작성되어 있으니 문제없음'이라고 판단합니다. 거짓말을 한 것은 아닙니다. 차이점 안에는 확실히 인계가 적혀 있습니다.
후속 작업의 md 파일은 바뀌지 않았습니다. 그래서 차이점에 나타나지 않습니다. 리뷰어에게 그 파일을 열 이유도 지시도 없습니다. 거기에 작성되어야 할 것이 있다는 것을 알게 해주는 재료가 전달되지 않은 것입니다.
여기가 핵심입니다. '여기에 적어야 하는데 적지 않음'은, 차이점 안에 존재하지 않는 정보입니다. 리뷰어의 능력 문제가 아니라, 제공하는 것의 문제입니다. 리뷰를 2번 하든 3번 하든, 다른 모델로 바꿔도, 차이점을 제공하고 있는 한 같은 곳에서 똑같이 통과합니다. 실제로 단계별 리뷰와 최종 리뷰 모두 통과했습니다.
차이점 리뷰가 검사하는 것은 '바뀐 것'입니다. '바뀌어야 할 것'은 차이점에 포함되어 있지 않습니다.
어떻게 막았는가
제공하는 것을 바꿨습니다. CLAUDE.md의 리뷰 섹션에 다음과 같이 추가했습니다.
예외: '작업 N에 인계했다', '파일 X에 반영했다'라는 주장이 차이점에 포함될 때는, 그 전달받는 파일도 패키지에 포함한다. 변경되지 않은 파일은 diff에 나타나지 않으므로, '바뀌어야 했는데 바뀌지 않았다'는 것은 원리적으로 검사할 수 없다.
차이점 안에 '◯에 적었다', '◯에 반영했다'라는 주장이 있을 때는, ◯ 파일을, 바뀌었는지 여부와 관계없이 리뷰 자료에 포함시킨다. 그러면 리뷰어는 그 파일을 열어서 '적혀 있어야 할 문구가 없음'을 확인할 수 있게 됩니다.
핵심은, 차이점(Diff) 속의 주장을 단서로 삼는 것입니다. 변하지 않은 파일은 셀 수 없이 많기 때문에 전부 전달할 수는 없습니다. 하지만 '어딘가에 반영했다'고 차이점이 말하고 있다면, 그 '어딘가'만 확인해 볼 가치가 있습니다. 주장은 있는데 전달받는 곳이 바뀌지 않았다면, 그것 자체가 결함입니다.
자료(Materials) 측에서 전달하는 이유는 이렇습니다. 이 규칙은 CLAUDE.md에 두고 오케스트레이터가 review-package를 만들 때 판단합니다. 리뷰어에게 '변하지 않은 파일도 의심해 달라'고 부탁하는 것이 아니라, 자료 측에서 직접 전달합니다. 만약 리뷰어에게 자유롭게 찾게 한다면, 이번에는 코드베이스를 뒤지기 시작해서 차이점에 집중하지 않게 되기 때문입니다.
같은 형태의 구멍이 또 하나 있었다
같은 사건으로, 또 하나 발견했습니다.
PR(Pull Request) 안에 사람에게 전달할 예정인 매뉴얼의 한 절이 포함되어 있었습니다. 아직 초안 단계의 것이었습니다. 차이점 리뷰는 그 절을 '문서로서 처음부터 읽는다'는 과정을 거치지 않습니다. 변경된 줄과 그 주변만 볼 뿐입니다. 결과적으로, 작업 중 작성한 메모가 그대로 확정된 문장 안에 남아 있었고, 용어 정의 부족과 함께 간과되었습니다.
이것 역시 같은 구조입니다. '문서로 읽으면 이상하다'는 것은 차이점 속에는 존재하지 않는 정보입니다. 막는 방법도 같아서, 외부로 나갈 문서를 포함하는 차이점에서는 그 절을 처음부터 끝까지 읽어보는 리뷰를 별도로 한 번 추가했습니다. 예상 독자(처음 읽는 사람, 설정을 모르는 사람, 개발 경위를 모르는 사람)를 명시하여 전달합니다.
맺으며
글을 써보니 당연한 이야기입니다. 차이점에 없는 것은 차이점 리뷰에서는 보이지 않을 수밖에 없습니다. 그럼에도 불구하고, 리뷰가 통과했을 때는 깨닫지 못했습니다. 통과했다는 것은, 전달된 범위 내에서 문제가 없었다는 의미일 뿐이고, 전달되지 않은 것은 아무도 보고 있지 않았기 때문입니다.
Discussion
AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기