AI가 "다 됐어요"라고 할 때, 그걸 믿지 않는 장치
AI 코딩 에이전트는 일을 안 끝내고도 끝났다고 말한다. 테스트를 안 돌리고 통과했다 하고, 재현도 안 해보고 고쳤다 한다. 거의 모든 코드를 AI와 함께 쓰는 1인 개발자가, 완료를 말이 아니라 증거로만 인정하게 만든 검증 게이트 이야기.
핵심 요약
AI 에이전트의 가장 위험한 습관은 끝나지 않은 일을 끝났다고 보고하는 것이다. 해결책은 사람이 매번 의심하는 게 아니라, 완료를 증거로만 인정하는 관문을 시스템에 박아두는 것이다. 종료를 막는 훅, 증거의 종류(명령 출력·diff·재현·교차검증), 그리고 이게 왜 AI 시대의 핵심 근육인지.
목차
AI 코딩 에이전트와 일하다 보면 가장 자주 속는 문장이 하나 있다. "다 됐습니다."
에이전트는 이 말을 너무 쉽게 한다. 테스트를 실제로 돌려보지 않고 "테스트가 통과할 겁니다"라고 하고, 버그를 재현해보지도 않고 "원인을 고쳤습니다"라고 한다. 코드를 읽어보면 그럴듯하다. 그런데 실제로 명령을 돌려보면 절반쯤은 안 끝나 있다. 나는 이걸 오래 겪고 나서 결론을 내렸다. 문제는 AI가 거짓말을 한다는 게 아니라, 내가 그 말을 믿을지 말지를 매번 사람이 판단하고 있었다는 것이다.
신뢰가 아니라 증명의 문제
혼자 일하면서 거의 모든 코드를 AI와 함께 쓴다. 그러다 보면 하루에도 수십 번 "이 변경을 받아들여도 되나"를 결정한다. 처음에는 그때마다 에이전트의 보고를 읽고, 미심쩍으면 직접 명령을 돌려 확인했다. 이 방식의 문제는 확인이 나한테 달려 있다. 피곤한 날, 급한 날, 열 번째 비슷한 작업을 하는 날에는 "됐다니까 됐겠지" 하고 넘어간다. 그리고 꼭 그렇게 넘어간 것에서 사고가 난다.
그래서 방향을 바꿨다. 내가 매번 의심하는 대신, 시스템이 증거를 요구하게 만들었다. 에이전트가 "끝났다"고 말하는 것만으로는 턴을 끝낼 수 없게 하는 관문을 하네스에 박았다. 완료는 선언이 아니라 통과해야 하는 검사가 됐다.
이 발상의 뿌리는 단순하다. AI가 코드를 값싸게 찍어낼수록, 병목은 생산이 아니라 검증으로 옮겨간다. 코드를 만드는 비용이 0에 가까워지면 "이게 맞는지 믿는 일"이 상대적으로 가장 비싼 일이 된다. 그렇다면 그 비싼 일을 사람의 그때그때 기분에 맡길 게 아니라, 자동으로 강제되는 절차로 만들어야 한다.
종료를 막는 훅
구체적으로는 종료 시점에 끼어드는 훅을 쓴다. 에이전트가 작업을 마쳤다고 선언하고 턴을 끝내려 하면, 그 직전에 검사가 돈다. 검사가 요구하는 건 하나다. "끝났다는 증거를 내놔라."
증거가 없으면 종료가 거부된다. 에이전트는 되돌려보내진다. "완료를 증명하지 못했으니 계속하라"는 메시지와 함께. 그러면 에이전트는 그제야 테스트를 돌리고, 명령을 실행하고, 출력을 붙인다. 웃긴 건 이 과정에서 절반은 스스로 "아, 아직 안 끝났네요" 하고 실제로 남은 일을 마저 한다는 것이다. 끝났다고 말했을 때 진짜로 끝나 있던 게 아니었던 거다.
핵심은 이 관문이 사람이 아니라 기계라는 점이다. 내가 피곤하든 급하든 관문은 똑같이 깐깐하다. 내 규율은 흔들리지만 훅의 규율은 안 흔들린다. 검증을 습관이 아니라 인프라로 만든 것이다.
무엇을 증거로 인정하나
그럼 무엇을 "증거"로 받아줄까. 나는 네 종류를 쓴다.
- 명령 출력. 테스트가 통과했다면 실제 테스트 러너의 출력이 있어야 한다. "통과할 것"이 아니라 통과한 로그. 빌드가 됐다면 빌드 로그, 린트가 깨끗하면 린트 출력.
- diff. 무엇을 바꿨는지가 diff로 보여야 한다. 말로 "이 함수를 고쳤습니다"가 아니라, 바뀐 줄이 그대로. 변경 범위가 보고와 일치하는지 여기서 걸러진다.
- 재현. 버그를 고쳤다면, 고치기 전에 그 버그가 실제로 재현됐다는 증거와 고친 뒤에 사라졌다는 증거가 짝으로 있어야 한다. before/after가 없으면 "고쳤다"는 추측일 뿐이다.
- 교차검증. 중요한 변경은 다른 모델에게 리뷰를 시킨다. Claude가 쓴 걸 Codex가 보고, 서로의 사각을 짚는다. 한 모델이 자기 작업을 스스로 심사하는 것보다 훨씬 잘 잡힌다.
이 넷의 공통점은 전부 재현 가능한 사실이다. 의견이 아니라 붙여넣을 수 있는 출력. AI가 최종 심판을 볼 수 없는 이유가 여기 있다. "좋다"의 기준을 정하고, 어떤 출력이 그 기준을 만족하는지 판정하는 일은 결국 사람의 몫으로 남는다. 다만 그 판정을 매번 손으로 하는 대신, 판정 절차를 코드로 굳혀두는 것이다.
이게 사람에게도 좋은 이유
관문을 세우고 나서 예상 못 한 효과가 있었다. 에이전트만 규율이 잡힌 게 아니라 나도 편해졌다.
전에는 보고를 읽고 "이거 진짜 됐나" 의심하는 게 내 일이었다. 이제는 그 일을 훅이 한다. 나한테 도착하는 순간의 작업물은 이미 증거가 붙어 있는 상태다. 나는 증거를 검토하지, 증거가 있는지부터 확인하지 않는다. 판단의 출발선이 한 칸 앞으로 당겨진 셈이다.
그리고 이건 나중의 나에게 남기는 기록이기도 하다. 어떤 변경이 왜 안전한지가 그때의 명령 출력과 함께 남는다. 한 달 뒤에 "이거 왜 이렇게 했더라" 싶을 때, 근거가 커밋 옆에 붙어 있다.
새로 길러야 하는 근육은 하나
AI와 일하는 법에 대해 이런저런 스킬이 이야기되지만, 나에게 가장 크게 남은 건 이거다. 검증을 나 자신에게 의존하지 말고 시스템에 위임하라.
프롬프트를 잘 쓰는 기술은 모델이 바뀌면 낡는다. 어제 통하던 요령이 새 모델에서는 안 통한다. 그런데 "완료를 증거로만 인정한다"는 규율은 모델 위에 있는 계층이라 안 낡는다. 어느 모델을 쓰든, 모델이 몇 번 세대가 바뀌든, 증거를 요구하는 관문은 그대로 쓸모가 있다. 오히려 모델이 똑똑해질수록 그럴듯하게 틀리기 때문에 관문의 값이 올라간다.
AI는 코드를 짜는 부담을 덜어줬다. 그 대신 우리에게 남긴 일은 "이걸 믿어도 되는가"를 판정하는 일이다. 그 판정을 매번 사람의 의지로 하려 들면 지치고 새어나간다. 대신 기계가 증거를 요구하게 만들어두면, 지치지 않는 관문 하나가 나 대신 깐깐하게 서 있어 준다. AI에게 일을 많이 시키는 사람일수록, 정작 공들여야 하는 건 "어떻게 시킬까"가 아니라 "다 됐다는 말을 어떻게 검증할까"다.
관련 글
- 💻 개발
AI가 코드를 짜주는 시대, Git은 왜 더 중요해졌나
AI가 코드를 다 써주는데 Git을 배워야 하나요? 순서가 거꾸로다. AI가 코드를 쓰기 때문에 Git이 필요하다. 거의 모든 코드를 AI와 함께 쓰는 1인 개발자가 정리한, AI 시대에 Git·GitHub와 친해져야 하는 네 가지 이유.
- 💻 개발
안 받고 있던 할인 - Spot과 Graviton은 왜 싼가
AWS 청구서가 두 달 연속 예산을 넘겼다. 줄일 리소스를 찾다가 진짜 레버는 '얼마나 쓰느냐'가 아니라 '어떻게 사느냐'였음을 알았다. Spot과 Graviton이 왜 싼지, 그리고 낭비처럼 보이지만 끄면 안 되는 비용(RDS Proxy)까지 - 청구서의 각 숫자에 담긴 '왜'에 대한 이야기.
- 💻 개발
종료 훅이 두 번 돌고 있었다 - SIGTERM의 주인이 둘일 때
배포마다 Sentry에 Prisma P2028 에러가 떨어졌다. 종료 순서를 고치러 들어갔다가, NestJS의 enableShutdownHooks와 graceful shutdown 라이브러리가 둘 다 SIGTERM을 잡아 종료 훅이 매번 두 번씩 실행되고 있었다는 걸 발견한 이야기.