AI 코딩 에이전트 검수, '완료'라는 보고를 믿었다가 비어 있던 공장
AI가 완료했다고 보고한 개발 단계가 실제로는 빈 컨테이너였어요. 사이트 공장을 커서와 Claude Code로 만들며 겪은 일들로, AI 코딩 에이전트 검수를 어떻게 해야 하는지 정리했습니다.
리서치 후보 다섯 개 중 하나를 골라 승인했어요. 텔레그램에 “기획 완료” 알림이 왔고, 그 뒤로 아무 일도 일어나지 않았어요. 아무것도 만든 게 없어 보였어요. 원인을 파보니 사이트를 실제로 만드는 개발 단계가 처음부터 비어 있었어요. AI 코딩 도구가 완료했다고 보고했던 바로 그 단계였죠. 이 글은 그날을 포함해, 사이트 공장을 만들면서 겪은 AI 코딩 에이전트 검수 이야기예요.
사이트 공장 시리즈 마지막 정리 편이에요. 1~4편에서 공장을 짓고, 서버를 세우고, API를 붙이고, 첫 사이트인 전국 주차장 요금 조회를 올렸어요. 그 과정 대부분을 커서와 Claude Code에 맡겼고요. 결과물은 지금 잘 돌아가고 있지만, 그 사이에 AI가 놓치거나 틀리게 보고한 게 꽤 있었어요. 저처럼 AI한테 개발을 맡기는 분이라면, 어디서 의심해야 하는지 정도는 가져가실 수 있을 거예요. AI 코딩 에이전트 검수라고 하면 거창하게 들리지만, 실제로 필요했던 건 코드 리뷰 실력보다 “이거 진짜 돌아가?” 하고 직접 눌러보는 습관이었어요.
”완료” 보고와 빈 컨테이너
공장은 리서치, 기획, 개발, 검수, 미리보기 배포, 도메인 연결, 운영 순서로 돌아가게 설계했어요. 커서는 단계별로 작업하면서 완료 보고를 올렸고, 저는 그걸 믿고 다음 단계로 넘어갔어요. 첫 사이트도 실제로 올라갔으니 의심할 이유가 없었죠.
그런데 두 번째 후보를 승인했을 때 기획에서 멈췄어요. Claude Code한테 원인을 찾게 했더니 결과가 이랬어요. 상태 전이표에는 “기획 완료 → 빌드 시작”이 정의돼 있는데, 그 전이를 실행하는 코드가 어디에도 없었어요. 개발을 맡을 컨테이너는 아무것도 안 하고 잠만 자는 자리표시자였고요. 첫 사이트도 공장이 만든 게 아니라, 기획 이후 공장 밖에서 직접 만들어 넣은 것으로 보였어요.

결과물이 나왔다고 해서 공정이 존재한다는 뜻은 아니에요. 사이트가 올라간 걸 보고 공장이 돌아간다고 믿은 게 제 실수였어요. 사실 이걸 확인할 기회도 있었어요. 맥을 끈 상태에서 새 후보를 승인해서 미리보기까지 가는지 보자고 미뤄둔 테스트가 있었거든요. 그 테스트를 먼저 했으면 바로 드러났을 거예요. 더 뼈아픈 건 따로 있어요. 처음에 쓴 작업지시서에 단계별 완료 기준을 적어뒀거든요. 개발 단계의 완료 기준은 “승인 1건이 사람 개입 없이 검수를 통과한 빌드까지 도달”이었어요. 완료 보고를 받았을 때 이 기준 하나만 직접 확인했어도 빈 컨테이너가 바로 보였을 거예요. 기준을 써두는 것과 기준대로 확인하는 건 완전히 다른 일이었어요.
지금은 운영 문서에 현재 구조를 솔직하게 적어뒀어요. 리서치와 기획, 배포와 운영은 자동이고, 개발은 Claude Code로 사람이 시작한다고요. 처음 작업지시서와 다른 점 12가지도 표로 정리했고요. 첫 4주는 어차피 시범 생산 2~3개라 반자동으로도 감당이 돼요. 대신 다음 사이트부터는 “설정 파일과 데이터 수집 스크립트만 있으면 끝나는” 템플릿으로 일반화해서, 개발 단계 자동화 범위를 좁혀갈 계획이에요.
커서가 멈추고 Claude Code로 넘어갔을 때
마무리 직전에 커서 사용량을 다 써버렸어요. 프로 요금제에 포함된 모델 사용량이 전부 100%였고, 초기화는 며칠 뒤였어요.
스케줄 파일에 있던 작업들을 실행으로 옮기던 중에 멈춘 거라 그냥 둘 수 없어서, 같은 레포를 Claude Code로 이어받게 했어요.
솔직히 Claude Code는 잘 안 써봐서 처음엔 어려웠어요. 로그인이 만료됐다고 해서 “run /login”이라고 쳤는데, 그걸 명령이 아니라 일반 메시지로 받아들이더라고요. 슬래시 명령은 /login만 단독으로 쳐야 했어요. 이런 사소한 데서 시간이 새요. 업데이트가 설치됐으니 재시작하라는 안내도 같이 떠 있어서, 종료하고 다시 실행한 다음 로그인부터 다시 했어요.
인계할 때 제일 신경 쓴 건 첫 지시였어요. 코드부터 고치게 하지 않고, 레포 구조와 문서, 서버 상태를 읽고 남은 작업마다 “이미 된 부분과 남은 부분”을 먼저 보고하게 했어요. 그 보고서에서 문제가 두 개 바로 나왔어요.

로컬에도 서버에도 git 저장소가 없었고, 사이트 빌드가 서버가 아니라 제 맥에서 돌고 있었어요. 4편에서 다룬 사이트맵의 로컬 주소 문제가 여기서 나온 거예요. 맥에서 빌드할 때 사이트 주소가 비어 있었고, 코드는 그걸 조용히 localhost로 채웠거든요.
그래서 AI 코딩 에이전트 검수의 첫 번째 원칙이 됐어요. 인계받은 AI에게는 수정보다 현황 보고를 먼저 시킨다. 바로 고치라고 했으면 git도 없는 상태에서 코드를 여기저기 바꿨을 거고, 망가지면 되돌릴 방법이 없었을 거예요. 보고를 받고 나서 git부터 만들고, 커밋할 파일 목록을 제가 확인한 다음 GitHub 비공개 저장소에 올렸어요.
AI가 토큰을 대화창에 출력했다
AI한테 줄 때 매번 붙인 문장이 있어요. “.env와 secrets 폴더는 읽기만 하고 값은 절대 출력하지 마.” 키를 채팅에 붙여넣지 않는 건 기본이고, AI가 디버깅하다가 설정 파일을 그대로 찍는 것까지 막으려고요.
그런데 텔레그램 봇 토큰이 한 번 대화창에 그대로 찍혔어요. 범인은 HTTP 라이브러리였어요. 텔레그램 API는 요청 주소 안에 봇 토큰이 들어가는데, 라이브러리가 그 주소를 통째로 컨테이너 로그에 남기고 있었던 거예요. Claude Code가 알림이 잘 나갔는지 확인하려고 봇 로그를 조회했을 때 토큰이 그대로 출력됐어요. Claude Code가 스스로 이걸 보고하고 로그 마스킹을 넣었고, 저는 BotFather에서 토큰을 폐기하고 새로 발급받았어요.
여기서 끝이 아니었어요. 새 토큰을 넣고 배포 스크립트를 돌렸는데, 봇이 계속 폐기된 옛 토큰으로 돌고 있었어요. 스크립트가 이미지가 그대로면 컨테이너를 다시 만들지 않아서 바뀐 설정이 안 들어간 거예요. 그 사이 봇은 텔레그램 버튼을 하나도 못 받았고요. 이후로 점검을 시킬 때는 문장을 바꿨어요. “값은 출력하지 말고, 어느 로그에 몇 건인지만 보고해.” AI한테 점검을 시킬 때는 무엇을 출력하지 말아야 하는지까지 지시해야 해요. 이번 노출도 확인하려고 로그를 조회하다가 터졌으니까요. 그 뒤 전수 점검에서 공공데이터포털 키처럼 주소에 실리는 다른 키도 찾아서 공통 마스킹으로 묶었어요. 그 키는 재발급하지 않았어요. 찍혔을 가능성이 있는 곳이 서버 안의 컨테이너 로그뿐이었고, 바깥으로 나간 흔적이 없었거든요. 대신 이번처럼 대화창에 실제로 출력된 적이 생기면 그땐 바로 재발급하기로 기준을 정했어요.
월요일 작업이 화요일에 돌고 있었다
보류 알림 기능을 추가하다가 Claude Code가 기존 버그를 하나 찾았어요. 재수집, 주간 리포트, 리서치를 매주 월요일에 돌리도록 설정해뒀는데, 실제로는 화요일에 실행되고 있었어요. 설정은 표준 cron 형식으로 월요일인 1을 넣었는데, 스케줄러 라이브러리는 숫자를 다르게 읽고 있었던 거예요.
APScheduler 공식 문서에도 이 함정이 적혀 있어요. 역사적인 실수로 요일 숫자가 어긋나 있어서, APScheduler는 0을 월요일로, 원래 crontab은 0을 일요일로 본다고요. 그래서 1은 월요일이 아니라 화요일이 됐어요. 일요일 밤에 보내려던 보류 알림도 0을 넣는 바람에 월요일 밤, 그러니까 재수집보다 늦게 올 뻔했고요.
무서운 건 이게 아무 오류도 안 낸다는 거예요. 월요일 리포트가 화요일에 와도 이상하다고 느끼기 어려워요. 수정은 숫자 대신 요일 이름으로 바꿔서 넘기는 걸로 끝났어요. 다음 실행 시각을 표로 뽑아보니 보류 알림은 일요일 21시, 재수집은 월요일 6시, 리포트와 리서치는 월요일 9시로 문서와 똑같이 맞춰졌어요. 이런 건 코드를 읽는 것보다 다음 실행 시각을 직접 찍어보는 게 훨씬 빨라요. 설정값을 사람이 읽는 방식과 라이브러리가 읽는 방식이 같은지는 따로 확인해야 해요. 이런 버그는 테스트도 통과하고 로그도 깨끗하거든요.
1위 후보가 이상하면 숫자를 의심하라
리서처가 올린 첫 후보 카드에서 1위는 “지하철 노선도”였어요. 월 검색량이 290만 건이 넘었거든요. 저는 그 숫자만 보고 승인했어요. 이게 맨 처음 이야기한 그 후보예요. 개발 단계가 비어 있어서 멈춘 김에 기획 내용을 다시 봤는데, 그제야 문제가 보였어요. 사람들이 “지하철 노선도”를 검색하는 건 노선도 이미지를 보려는 거잖아요. 공공데이터 표로는 답할 수 없는 검색이에요. 그런데 왜 1위였을까.
파보니 리서처에 버그가 있었어요. 네이버 키워드 도구에 “종량제봉투가격”처럼 붙여 쓴 주제 전체만 넘기고 있어서, 연관 키워드가 전부 0개로 오고 있었어요. 같은 도구에 “주차장”을 넣으면 876개가 오는데요. 결국 지금까지의 점수는 사실상 검색량 하나로만 매겨진 거였어요. 첫 사이트로 주차장을 고른 것도 이 점수였으니, 결과적으로 나쁘지 않았던 건 운이었던 셈이에요.
고치면서 검색 의도 판정을 추가했어요. 대표 키워드를 “공공데이터 표로 답할 수 있는 정보형 / 이미지·지도 / 앱·서비스 이동 / 브랜드”로 분류하게 했더니, 지하철 노선도는 1위에서 꼴찌로 떨어졌어요. 판정 모델의 재판정률도 문제였어요. 확신이 낮아서 Claude한테 다시 묻는 비율이 55%였는데, 질문 하나에 여러 판단을 섞어둔 게 원인이었어요. 예·아니오 질문 세 개로 쪼개고 예시를 넣었더니 19%로 내려갔어요.
기획 단계도 같은 후보에서 엉뚱한 일을 했어요. 이름에 “지하철”이 들어갔다는 이유로 지하철 공기질 데이터를 골랐고, 예상 페이지가 1개인데도 그대로 통과시켰어요. 지금은 데이터셋의 실제 필드로 주제의 질문에 답할 수 있는지 판정하고, 예상 페이지가 30개 미만이면 기획 불가로 처리해요. “지하철”이면 공공데이터식 표기인 “도시철도”도 같이 찾게 했고요.
AI 코딩 에이전트 검수에서 이게 제일 어려운 부분이었어요. 코드는 오류 없이 돌았고 점수도 그럴듯하게 나왔어요. 결과가 상식과 안 맞으면, 결과를 믿지 말고 그 결과를 만든 과정을 의심해야 해요. 이번엔 제가 숫자만 보고 승인했다가 뒤늦게 알아챘어요. 개발 단계가 비어 있지 않았다면 그대로 노선도 사이트가 만들어졌을지도 몰라요. 점수표를 볼 때 1위가 왜 1위인지 한 줄이라도 확인하는 습관이 필요하다는 걸 배웠어요.
AI는 맥락을 모르면 맞는 숫자도 의심한다
반대 방향의 일도 있었어요. 서치콘솔 지표를 수집하는 기능을 만들던 Claude Code가 버림값과 주차장 사이트의 노출이 둘 다 0으로 나오자, 연동이 잘못된 것 같다며 결과를 믿지 않았어요. 사실 0이 맞는 값이었어요. 버림값은 사이트맵 경로 문제로 구글이 페이지를 제대로 찾지 못하다가 막 고친 참이었고, 주차장 사이트는 생긴 지 며칠 안 됐으니까요.
이건 AI가 틀렸다기보다 맥락이 없었던 거예요. 그래서 상황을 설명해주고, 확인 방법을 하나 알려줬어요. 연동이 깨진 거라면 오류가 나고, 진짜로 노출이 0이라면 오류 없이 빈 결과가 와요. 응답 모양을 보니 후자였고, Claude Code도 수치가 맞다고 정리했어요. AI가 이상하다고 하면, 내가 아는 맥락을 먼저 줘야 해요. 맥락 없이 고치라고 하면 멀쩡한 연동 코드를 뜯어고쳤을 수도 있어요.
비슷하게, 리서치 수요 추정을 계속 정밀하게 다듬다 보니 AI는 공식을 하나 고칠 때마다 다음 개선안을 또 제안했어요. 네이버 키워드 도구로는 “○○도서관 운영시간” 같은 롱테일이 안 보여서 데이터에서 키워드를 직접 만들어 검색량을 재는 방식까지 갔는데, 이 정도에서 멈추기로 했어요. 실제 노출 데이터가 하나도 없는 상태에서 추정 공식만 계속 고치는 건 의미가 없거든요. 이건 AI가 정해주지 않아요. 언제 멈출지는 사람이 정해야 해요.
결국 AI 코딩 에이전트 검수는 사람이 써보는 것
돌아보면 이 시리즈에서 중요한 발견은 대부분 제가 “이상한데?”라고 한 순간에 나왔어요. 1편에서 다이어리 PDF가 다 공짜로 풀려 있다는 걸 본 것도, 4편에서 주차장 사이트를 써보고 표만 있어서 불편하다고 느낀 것도, 서치콘솔에 직접 들어가 사이트맵 오류 267개를 발견한 것도요. AI는 시킨 걸 빠르게 해내지만, 시키지 않은 걸 의심하지는 않아요.
AI도 스스로 정정할 때가 있긴 했어요. 성능 점수가 낮은 걸 두고 처음엔 측정 편차라고 보고했다가, 다시 재보니 광고 스크립트가 원인이었다며 앞선 보고를 정정하더라고요. 처음 측정 때 차단이 실제로 안 걸려 있었다면서요. 반대로 “몇 시간 뒤에 서치콘솔을 다시 확인해드리겠다”는 말은 받지 않았어요. 세션이 끝나면 기억을 못 하니까요. 대신 그 확인을 공장의 매일 점검 기능으로 만들었어요.
제가 정리한 AI 코딩 에이전트 검수 원칙은 이 정도예요. 완료 보고는 끝에서 끝까지 실제로 돌려보기 전엔 믿지 않는다. 완료 기준을 적어뒀다면 그 기준대로 직접 확인한다. 인계받은 AI에게는 수정보다 현황 보고를 먼저 시킨다. 값이 없으면 조용히 대체하지 말고 실패하게 만든다. 점검을 시킬 때는 출력하면 안 되는 것까지 정해준다. 그리고 결과가 상식과 다르면 과정을 의심한다.
이렇게 만든 사이트 두 개는 지금 운영 중이에요. 대형폐기물 배출 수수료를 지역별로 찾아볼 수 있는 버림값과, 전국 공영·민영 주차장 요금과 무료주차장을 동네별로 정리한 주차요금 조회예요. 동네 주차장을 찾을 일이 있으면 한 번 써보시고, 틀린 정보가 있으면 알려주세요. 그게 제 공장한테는 제일 값진 검수예요. 8주 차가 되는 12월 초에는 이 사이트들에 실제로 노출이 붙었는지, 숫자 그대로 결과편을 쓸게요.