Why NextWhy Next
목록으로
💻 개발4분 읽기

AI가 고친 코드를 다른 AI에게 반증시키는 파이프라인

같은 모델에게 자기 코드를 리뷰시키면 자기가 낸 결론을 다시 승인한다. 거의 모든 코드를 AI와 함께 쓰는 1인 개발자가, 한 AI의 작업을 다른 AI에게 '반박해보라'고 시키는 교차검증 루프를 상시 파이프라인으로 굳힌 이야기. 셀프 리뷰가 놓친 버그를 세 라운드 연속으로 잡아낸 실제 사례와 프롬프트 설계.

핵심 요약

자기 작업을 자기가 심사하는 AI는 관대하다. 다른 모델에게 '이 수정이 틀렸다고 가정하고 반박하라'고 시키면 셀프 리뷰가 놓친 결함이 나온다. 로그아웃 버그 하나에서 세 라운드 연속 더 깊은 결함이 나온 사례, 반증 프레이밍과 증거 형식 등 프롬프트 설계, 그리고 이 루프를 언제 돌릴지에 대한 기준.

목차

AI 에이전트에게 코드 리뷰를 시켜본 사람은 안다. 남의 코드는 꽤 잘 잡는다. 그런데 자기가 방금 쓴 코드를 리뷰시키면 이상하리만치 관대해진다. "구현이 의도에 부합합니다"라는 답이 돌아오고, 실제로 돌려보면 버그가 그대로 있다.

당연한 일이다. 같은 모델, 같은 컨텍스트 안에서는 코드를 쓸 때 저지른 착각을 리뷰할 때도 똑같이 저지른다. 자기가 낸 결론을 자기가 다시 승인하는 구조라서, 리뷰가 아니라 복명복창에 가깝다.

그래서 나는 심사를 밖으로 뺐다. Claude가 고친 코드를 Codex에게 넘기고, 딱 하나를 주문한다. "이 수정이 틀렸다고 가정하고, 반박해보라."

세 라운드 연속으로 더 깊은 버그가 나왔다

이 루프의 가치를 처음 실감한 건 로그아웃 버그였다. 증상은 단순했다. 로그아웃 버튼을 눌러도 가끔 로그아웃이 안 된다. Claude가 원인을 찾아 고쳤고, 코드만 읽으면 그럴듯했다. 나도 머지할 뻔했다.

Codex에게 반증을 시켰더니 1라운드에서 이런 답이 왔다. 수정 자체는 맞는데, 인증 페이지의 리다이렉트가 그 코드 경로에 도달하기 전에 흐름을 가로채서 수정이 실행될 기회가 없다. 고친 코드가 죽은 코드였던 거다.

리다이렉트까지 고치고 다시 반증을 시켰다. 2라운드에서 또 나왔다. 이번에는 중복 실행을 막으려고 걸어둔 처리 중 플래그가 문제였다. 특정 타이밍에는 이 가드 때문에 로그아웃 요청 자체가 아무 일도 하지 않는 no-op이 된다. 3라운드에서야 "반박할 거리를 찾지 못했다"는 답이 나왔고, 그제야 머지했다.

세 라운드 모두 셀프 리뷰는 통과했던 코드다. 그리고 세 라운드 모두, 반증자는 "코드가 좋아 보이는가"가 아니라 "이 주장이 무너지는 시나리오가 있는가"를 찾았기 때문에 잡았다.

이후로도 패턴은 반복됐다. 서버 수정에 셀프 리뷰를 두 번 돌리고도 남아 있던 DB 제약 조건의 누락 값 하나를 교차검증이 잡아서 마이그레이션을 추가한 적이 있다. 재시도 로직 수정에서는 특정 타임아웃 응답을 영구 실패로 오분류하는 경계 케이스를 반증자가 차단 판정으로 돌려보냈다. 하나같이 "그럴듯해서 통과할 뻔한" 것들이었다.

리뷰가 아니라 반증을 시킨다

프롬프트 설계에서 제일 중요한 건 프레이밍이다. "리뷰해줘"라고 하면 모델은 칭찬 반, 사소한 지적 반의 무난한 답을 준다. "이 수정이 틀렸다고 가정하고 반박하라. 반박에 실패하면 그때만 통과시켜라"라고 하면 태도가 달라진다. 통과가 기본값이 아니라 반박 실패의 결과가 되기 때문이다.

여기에 세 가지를 더 건다.

  1. 증거 형식을 강제한다. 모든 지적은 파일과 줄 번호, 그리고 그 결함이 실제로 터지는 재현 시나리오를 달아야 한다. "이 부분이 위험해 보입니다"는 받지 않는다. 시나리오를 못 만들면 그 의심은 지적이 아니라 기각이다. 실제로 반증자가 자기 의심을 스스로 반증해서 접는 경우가 꽤 많은데, 이게 오탐을 걸러주는 핵심 장치다.
  2. 판정을 등급으로 받는다. 통과 / 사소한 수정 후 통과 / 차단, 세 단계면 충분하다. 등급이 있으면 "지적은 있지만 머지해도 되는" 상태와 "머지하면 안 되는" 상태가 섞이지 않는다.
  3. 독립 컨텍스트에서 돌린다. 반증자에게는 diff와 관련 코드만 준다. 작업 과정의 대화나 "왜 이렇게 고쳤는지"의 서사는 주지 않는다. 서사를 공유하는 순간 반증자도 같은 착각에 물든다.

운영 팁도 하나. 반증자는 읽기 전용으로 돌린다. 코드를 고치는 권한을 주면 지적하는 대신 자기가 고치려 들고, 그러면 이번엔 그 수정을 누가 검증하느냐는 문제가 다시 생긴다. 역할은 심사로 고정하고, 수정은 원래 작업자에게 돌려보낸다.

언제 돌릴 것인가

모든 변경에 이 루프를 돌리지는 않는다. 오타 수정에 반증자를 부르는 건 낭비다. 무조건 돌리는 건 되돌리기 어려운 변경이다. DB 마이그레이션, 데이터 삭제, 결제나 인증 경로가 여기 든다. "고쳤다"는 주장이 걸린 변경도 대상이다. 버그 수정은 원래 버그가 재현됐고 수정 후 사라졌다는 짝이 필요한데, 반증자는 이 짝의 빈틈을 잘 찾는다. 나머지 하나는 내가 코드를 다 읽지 않을 변경이다. AI가 만든 큰 diff를 사람이 전부 읽는 건 현실적으로 무너진다. 안 읽을 거라면 최소한 다른 모델이라도 적대적으로 읽게 한다.

비용 이야기를 안 할 수 없는데, 반증 한 라운드는 모델 호출 몇 번 값이다. 프로덕션에 나간 버그 하나를 사람이 추적하는 비용과 비교하면 고민할 거리가 아니었다. 위의 로그아웃 버그가 배포까지 나갔다면, 나는 "가끔 로그아웃이 안 된다"는 제보만 들고 리다이렉트와 플래그 가드 두 층을 스스로 파야 했을 거다.

다른 모델이어야 하는 이유

같은 모델의 새 세션으로도 어느 정도 효과는 있다. 컨텍스트가 분리되는 것만으로 서사에 물드는 문제는 사라지니까. 그런데 몇 달 굴려본 결론은, 모델 자체가 다를 때 잡히는 게 확실히 더 많다는 것이다.

모델마다 주의가 가는 지점이 다르다. 한쪽이 상태 관리의 타이밍 문제에 민감하면 다른 쪽은 계약 위반과 경계값에 민감한 식이다. 같은 모델 두 세션은 같은 사각지대를 공유하지만, 다른 모델은 사각지대가 어긋난다. 교차검증의 값어치는 정확히 그 어긋남에서 나온다.

이 구조가 낯설지 않을 거다. 사람 팀이 코드 리뷰를 작성자 아닌 사람에게 맡기는 이유와 같다. AI와 일한다고 그 원칙이 사라지는 게 아니라, 오히려 싸게 상시 적용할 수 있게 됐을 뿐이다. 작성자가 아닌 심사자, 서사에 물들지 않은 눈. 그리고 반박에 실패해야 비로소 나오는 승인이다. 사람 조직이 비싸서 가끔만 하던 걸, 이제는 머지 앞에 항상 세워둘 수 있다.

관련 글

댓글 0