AIDive

Claude Code가 버그를 냈다, 나는 4가지 수정으로 막았다

AIDive · 게시

코딩 에이전트

버그를 출시한 세션, 그리고 순탄했다고 한 retro

Claude Code는 실제 리포지토리에서 채번 버그를 출시했고, 승인한 네 가지 변경 덕분에 그 버그는 다시는 출시될 수 없게 되었습니다. 이 세션은 작은 청구서 API에 크레딧 노트를 추가하는 일이었고, 테스트 스위트는 초록불로 통과했습니다. 그런데 나중에 손으로 돌린 검사에서, 거부된 요청이 청구서 번호를 태워 버렸다는 사실이 드러났습니다. 그래서 그 세션에 retro를 돌리고, 수정안을 하나씩 승인한 다음, 같은 작업을 다시 돌렸더니 이번에는 깨끗하게 나왔습니다.

retro는 사고 뒤에 팀이 여는 회고인데, 여기서는 Claude Code 자신의 세션 로그를 대상으로 합니다. 우리의 첫 retro는 "This session was smooth"로 시작했고, 찾아낸 항목 중 어느 것도 그 버그가 아니었습니다. 해법은 일곱 가지 동작입니다. 설치하기, 작업 돌리기, retro를 돌리고 무엇이 실패했는지 알리기, 후보마다 추적하기, 살아남은 후보를 담을 곳으로 승인하기, 커밋하고 감사하기, 그리고 작업을 다시 돌리기입니다.

retro란 무엇인가, 그리고 /retro 설치

/retro는 Matt Pocock이 만든 skill로, 코드가 아니라 코딩 세션을 리뷰합니다. 세션 로그, 즉 Claude Code가 모든 프롬프트와 도구 호출을 기록해 두는 파일을 읽고, 실수를 잡아낼 수 있었던 검사 같은 일곱 가지 범주에서 후보를 찾습니다. 후보는 심각도 순으로 제시되며, 문서에 따르면 제안만 할 뿐 여러분이 후보를 고르기 전에는 아무것도 바뀌지 않습니다.

설치가 첫 번째 동작입니다. 리포지토리에서 공식 마켓플레이스를 업데이트한 다음, skills 팩을 프로젝트 범위로 설치하면 이 리포지토리에만 적용됩니다. 우리 마켓플레이스는 retro가 없는 이전 릴리스에 고정되어 있어서 업데이트를 먼저 했습니다. 새 세션에서 슬래시를 입력하면 retro 명령이 목록에 나옵니다.

한 가지 제약이 있습니다. 클라우드 세션은 여러분 컴퓨터에 있는 로그를 읽을 수 없으므로, 세션이 진행된 곳에서 retro를 돌려야 합니다.

작업을 끝까지 돌리기: 우리 세션은 버그를 출시했다

두 번째 동작은 이미 매일 하는 일입니다. 실제 작업을 평범한 세션에 입력하고, 끝날 때까지 기다리고, 세션을 열어 둡니다. retro가 읽을 것이 바로 그 세션이기 때문입니다.

우리는 Node와 TypeScript로 만든 작은 청구서 API를 썼습니다. 자체 테스트 스위트는 있지만 지침 파일도, 파이프라인도, 커밋 hook도 없습니다. 작업은 직접 입력할 법하게 짧게 했습니다. 결제 완료된 청구서에 크레딧 노트를 추가하되 순서대로 번호를 매길 것, 그리고 결제 완료된 청구서는 남은 금액을 크레딧 처리해서 무효화할 수 있게 할 것입니다.

세션은 라우트, 마이그레이션, 그리고 자체 테스트를 작성하고, 멈추기 전에 스위트를 돌렸습니다. 그런 다음 경리팀이 손으로 돌릴 검사, 즉 미리 작성해 둔 숨은 테스트를 돌렸더니 요구 사항 하나가 실패했습니다.

신호 결과
테스트 스위트 42개 통과
타입 체크 깨끗함
숨은 경리 검사 실패: 예상보다 뒤의 크레딧 노트 번호가 나옴

원인은 이렇습니다. 핸들러가 요청을 검증하기 전에 카운터에서 다음 번호를 가져가고, 카운터는 읽는 순간 저장됩니다. 너무 큰 크레딧 노트는 거부되는데 이것은 올바른 동작이지만, 번호는 어쨌든 사라지고 이것은 올바르지 않습니다. 세션은 이 순서를 기존 발행 라우트에서 그대로 가져왔고, 그 라우트도 같은 식으로 청구서 번호를 흘립니다.

이 작업을 두 번 돌렸고 다른 한 번은 깨끗하게 나왔습니다. 그러니 평범한 세션에서는 이런 일이 가끔 일어난다는 뜻입니다.

/retro를 돌리고 무엇이 실패했는지 알리기

세 번째 동작은 같은 세션에서 retro 명령을 입력한 다음, 실패 내용을 뒤에 적어서 다시 돌리는 것입니다. 우리 첫 출력은 "This session was smooth."로 시작했습니다.

retro는 몇 가지 항목에 순위를 매겼지만 그중 어느 것도 버그가 아니었습니다. 가드레일 부재, 아무도 확인하지 않는 마이그레이션 규칙, 리뷰어가 강제할 것이 아무것도 없다는 내용이었습니다. retro가 읽는 것은 세션 로그이고, 세션은 자기가 실패했다는 사실을 몰랐던 것입니다.

그래서 여러분이 알려 줘야 합니다. 실패 내용을 인자, 즉 명령 뒤에 오는 텍스트로 붙여서 명령을 다시 실행합니다. 우리는 경리팀이 빌드를 테스트했다는 것, 너무 큰 크레딧 노트는 거부되었다는 것, 그다음 유효한 노트의 번호가 늦게 나왔다는 것, 그리고 retro는 아무것도 고치지 말라는 것을 적었습니다.

이번에는 자기 코드에서 원인을 찾아냈습니다. 번호를 먼저 가져가고 검증을 나중에 한다는 점, 그리고 그 순서를 기존 발행 핸들러에서 베꼈다는 점입니다. 새 후보도 이어졌습니다. 실패하는 순서를 돌리면서도 상태 코드만 확인한 자체 테스트, 순수해 보이지만 그렇지 않은 카운터 함수, 아무도 적어 두지 않은 관례입니다.

세션이 끝난 뒤에 알게 된 것이 있으면 무엇이든 인자에 입력하세요. 그렇지 않으면 retro는 잘 끝났다고 믿는 세션을 감사하게 됩니다.

승인하기 전에 모든 후보를 순간까지 추적하기

네 번째 동작은 skill이 스스로 하지 않는 일입니다. retro에게 각 후보의 근거가 된 트랜스크립트의 순간을 인용하게 하고, 보여 주지 못하는 후보는 모두 버립니다.

순위 목록만으로는 부족한 이유를 문서가 설명합니다. 작업이 끝나면 모델은 세션 중에 겪은 어려움을 잊고 일반적인 조언을 지어낸다는 것입니다. 그러니 특정한 순간까지 추적할 수 없는 후보는 버리세요. 각 후보마다 트랜스크립트를 인용해 달라고 retro에게 한 문장으로 요청하면 됩니다.

첫 번째 retro에서 돌아온 답은 단호했습니다. 대부분의 후보는 트랜스크립트에 근거가 없었습니다. 가드레일 부재는 남았는데, 훅 폴더를 직접 나열했을 때 아무것도 나오지 않았기 때문입니다. 마이그레이션 검사는 가정에 불과하다며 버려졌습니다.

두 번째 retro, 즉 버그를 알고 있던 쪽에서는 추적 결과 후보 네 개가 남고 하나가 빠졌습니다.

후보 추적 후
자기가 쓴 테스트(승인, 거부, 승인, 번호는 확인 안 함) 유지, 확실
경고가 없는 카운터 함수 유지, 확실
적히지 않은 관례 유지, 약함
가드레일 부재 유지. 다만 가드레일이 있어도 이 버그는 못 잡았을 것이라고 스스로 말함
마지막 후보 제외: 같은 순간을 두 번 셌음

skill 리포지토리에 열려 있는 이슈는 대부분의 retro 실행 뒤에 사용자가 증거를 요구한다고 집계합니다. 추적한 후보도 결국 모델이 자기 로그를 읽은 결과일 뿐이므로, "약함"으로 표시된 행은 판단이고, 어느 것을 가져갈지는 여러분이 정합니다.

살아남은 후보를 각자의 담을 곳으로 승인하기

다섯 번째 동작에서 루프의 가치가 나옵니다. 살아남은 후보를 하나씩, 각자의 담을 곳, 즉 규칙이 살게 될 곳으로 승인합니다. skill은 사다리를 제공합니다. 기계적인 위반에는 결정적인 검사를 두면 그만입니다. 린터 규칙, 커밋 hook, 파이프라인의 잡이 그런 것입니다. 판단이 필요한 것은 리뷰어가 읽는 코딩 표준 파일에 들어갑니다. 지침 파일은 문서의 표현을 빌리면 "incredibly sparingly", 즉 아주 드물게, 다른 파일을 가리키는 포인터에만 씁니다.

우리는 네 가지 변경을 승인했습니다.

  • 가드레일 부재는 타입 체크와 테스트를 돌려서 하나라도 실패하면 커밋을 막는, 추적되는 커밋 hook이 되었습니다. 하네스가 파일을 실행 가능하게 만드는 것을 거부해서, 그 명령은 우리가 직접 실행했습니다.
  • 테스트는 규칙을 고정하는 vitest 테스트 하나가 되었습니다. 크레딧 노트 승인, 크레딧 노트 거부, 그다음 유효한 노트는 다음 번호를 가져야 한다는 것입니다. 첫 실행에서 실패했고, 그것이 핵심입니다. 세션이 출시한 버그를 잡아낸 것입니다.
  • 판단이 필요한 부분은 새 CODING_STANDARDS.md의 한 줄이 되었습니다. 모든 검증이 통과한 뒤에만 일련번호를 가져가서, 거부된 요청이 번호 순서에 빈틈을 남기지 않게 하라는 것입니다.
  • 탐색의 틈은 카운터 함수 위의 주석 하나가 되었습니다. 이 함수는 카운터를 즉시 저장하므로 호출한 쪽이 나중에 실패해도 번호는 이미 소비됩니다. 그러니 검증 뒤에만 호출하라는 내용입니다.

바뀐 파일은 네 개이고 CLAUDE.md에 추가된 줄은 0줄입니다. 두 번의 retro 모두 쓰기를 거절했기 때문입니다. 첫 번째는 steering 파일 포인터가 매 턴 context를 쓰고 금방 낡는다고 했고, 두 번째는 함수의 주석으로 남기라고 했습니다. 문서도 이를 뒷받침합니다. Claude는 이런 파일을 context로 다루지 강제되는 설정으로 다루지 않으며, 어떤 동작을 막으려면 hook을 쓰라고 합니다.

제약이 두 가지 있습니다. hook은 커밋만 지킬 수 있다는 점, 그리고 주석은 retro 자신의 표현으로 다음 호출자에 거는 내기이지 입증된 수정이 아니라는 점입니다.

루프 닫기: hook이 커밋을 막고, 감사하고, 커밋하고, 다시 돌리기

여섯 번째 동작은 커밋이고, 마지막 동작은 같은 작업을 다시 돌리는 것입니다. 루프가 제대로 작동했는지 확인하는 곳이 바로 거기입니다.

승인한 파일을 스테이징하고 커밋하자 hook이 먼저 돌아서 커밋을 거부했습니다. 타입 체크는 통과했고, 그다음 새 채번 테스트가 실패하면서 예상보다 뒤의 번호를 받았습니다. hook은 디스크에 있는 파일을 그대로 검사하는데, 디스크에는 아직 버그가 있는 코드가 남아 있었던 것입니다.

그러니 세션으로 돌아가서 수정만 요청하세요. 검증이 통과한 뒤에 번호를 가져가고, 검사를 돌리고, 커밋은 하지 말라고요. 그런 다음 다시 커밋하면 hook이 초록불이 되고 커밋이 두 개 생깁니다. retro 변경과, 수정이 들어간 기능입니다. retro 자체는 아무것도 커밋하지 않습니다. 이 점은 리포지토리의 열린 이슈에 그대로 적혀 있으니, 커밋은 여러분이 합니다.

다음으로 /doctor prompt-audit을 돌립니다. 지침 파일에 낡은 줄이 없는지 보는 Claude Code 자체 점검입니다. 여기에 함정이 있습니다. 경로 없이 실행하면 리포지토리가 아니라 전역 파일을 감사합니다. 경로를 주자 새 표준 파일은 깨끗하다고 판정했고, 오래된 발행 라우트가 그 규칙을 어기고 있다는 지적이 따라왔습니다. 이것은 프롬프트 문제가 아니라 코드 버그입니다.

마지막 동작은 새 세션에서 같은 작업을 돌리는 것입니다. 우리 세션은 코드를 쓰기 전에 코딩 표준 파일, readme, hook, 그리고 빨간 채번 테스트를 읽었습니다. 그리고 자기 요약에, 그 검사들이 모두 통과한 뒤에만 번호를 가져가므로 거부된 요청이 번호 순서에 빈틈을 남기지 않는다고 적었습니다.

첫 세션 루프 이후의 새 세션
통과한 테스트 42 초록
숨은 경리 검사 실패 5개 중 5개
거부된 요청 뒤의 번호 소비됨, 빈틈 빈틈 없음

솔직한 단서가 하나 있습니다. 이 새 세션은 빨간 테스트와 표준 한 줄의 형태로 교훈을 받은 상태였습니다. 그러니 이것은 루프 이후의 실행이지 벤치마크가 아닙니다.

결론: retro를 돌릴 때, 돌리지 않을 때, 단 하나의 규칙

retro는 세션을 읽으므로, 세션이 보지 못한 것은 인자에 적어야 합니다. 여러분이 걸려 넘어질 부분이 바로 여기입니다.

retro는 잘못된 세션 뒤에, 세션을 지우기 전에, 또는 오래된 세션의 로그에 돌리세요. 자동 조종으로 돌리거나 hook에 연결하지 마세요. skill의 저자가 직접 이렇게 말합니다. 이것을 자동화하고 싶지는 않을 것이라고요. 문서는 한계도 덧붙입니다. 지난달에 제안한 검사를 감사해 주지는 않으므로, 가지치기는 여전히 직접 해야 합니다.

기억할 단 하나의 규칙은 문서 자신의 문장입니다. 지침 파일을 코드처럼 다루고, 문제가 생겼을 때 리뷰하고, 정기적으로 가지치기하고, Claude의 동작이 실제로 바뀌는지 관찰해서 변경을 테스트하라는 것입니다. 이 루프가 에이전트를 옳게 만들어 주지는 않습니다. 같은 잘못을 더 빨리 잡게 해 줄 뿐이고, 추가한 검사는 이제 여러분이 유지보수해야 할 코드입니다.

이 영상의 무료 팩에는 모든 동작, 추적 질문, 담을 곳을 정리한 retro 체크리스트와, 우리 실행에서 쓴 커밋 hook 스크립트 및 채번 테스트가 들어 있어서, 우리 것에서 출발할 수 있습니다.

무료 자료

출처

자주 묻는 질문

Matt Pocock의 /retro skill은 무엇을 하나요?
코드가 아니라 Claude Code 세션을 리뷰합니다. 모든 프롬프트와 도구 호출이 담긴 세션 로그를 읽고, 일곱 가지 범주에서 후보를 찾아 심각도 순으로 순위를 매깁니다. 제안만 할 뿐, 여러분이 후보를 고르기 전에는 아무것도 바뀌지 않습니다.
retro는 왜 세션이 순탄했다고 했나요?
retro가 읽는 것은 세션 로그이고, 우리 세션은 자기가 실패했다는 것을 몰랐기 때문입니다. 테스트 42개와 타입 체크는 모두 초록불이었습니다. 버그를 잡은 것은 숨은 경리 검사뿐이었고, 그래서 첫 retro는 'This session was smooth'로 시작하며 버그를 놓쳤습니다.
세션이 보지 못한 실패를 retro가 찾게 하려면 어떻게 하나요?
retro 명령을 한 번 더 실행하면서 실패 내용을 인자로 뒤에 적습니다. 우리는 경리팀이 거부된 크레딧 노트와 늦게 나온 다음 번호를 발견했다고 적었습니다. 그러자 이번에는 retro가 검증 전에 번호를 가져가는 자기 코드에서 원인을 찾아냈습니다.
retro가 자기 발견을 증명하게 하려면 어떻게 하나요?
각 후보의 근거가 된 트랜스크립트의 순간을 인용하게 하고, 보여 주지 못하는 후보는 버립니다. 우리 첫 retro에서는 대부분의 후보에 근거가 없었고, 두 번째에서는 추적 결과 네 개가 남고 하나가 빠졌습니다.
규칙을 CLAUDE.md에 넣어야 하나요?
기본적으로는 아닙니다. 우리는 네 가지 변경을 승인했고 CLAUDE.md에는 한 줄도 쓰지 않았습니다. 문서에 따르면 Claude는 CLAUDE.md를 강제되는 설정이 아니라 context로 다룹니다. 기계적인 규칙은 hook이나 테스트에, 판단이 필요한 것은 코딩 표준 파일에 둡니다.
Claude Code 세션에는 retro를 언제 돌려야 하나요?
잘못된 세션 뒤에, 세션을 지우기 전에, 또는 오래된 세션의 로그에 돌립니다. 자동화하거나 hook에 연결하지 마세요. 저자는 이것을 자동화하고 싶지는 않을 것이라고 말합니다.

관련 영상