코딩 에이전트가 코드를 쓰는 일은 이제 일상이 됐습니다. 문제는 그다음입니다. 그 코드를 누가 검토하는가. 가장 흔한 답은 “같은 에이전트에게 다시 시킨다"입니다. 방금 코드를 작성한 모델에게 “이거 리뷰해줘"라고 한 번 더 부탁하는 식입니다. 편하지만, 여기에는 구조적인 함정이 있습니다.

자기 리뷰의 맹점

같은 세션의 같은 모델이 자기가 쓴 코드를 검토하면, 작성할 때 놓친 가정을 리뷰할 때도 똑같이 놓칩니다. 모델이 어떤 엣지 케이스를 “이건 일어날 리 없지"라고 판단해서 코드에 반영하지 않았다면, 같은 모델은 리뷰 단계에서도 같은 판단을 반복할 가능성이 높습니다. 작성과 리뷰가 동일한 학습 분포에서 나오기 때문입니다. 같은 분포는 같은 사각지대를 만듭니다.

이건 추측이 아니라 모델 제조사 스스로 인정한 성질입니다. Anthropic은 Claude Opus 4.8(2026-05-28 출시) 발표에서 개선 포인트 중 하나로 “자기가 작성한 코드의 결함을 그냥 넘기는 비율이 직전 버전(Opus 4.7) 대비 약 4배 감소"를 들었습니다(Anthropic 발표). 이 문장은 두 가지를 동시에 말합니다. 첫째, 모델에게는 “자기 코드의 결함을 흘려보내는 경향"이 실제로 존재한다는 것. 둘째, 새 버전이 그 비율을 크게 줄였다는 것. 다만 4배 줄였다는 건 0이 됐다는 뜻이 아닙니다. 줄어든 채로 남아 있습니다.

운영 경험에서도 같은 패턴이 보입니다. 자동화 코드에서 회귀 버그를 되짚어보면, 거의 전부가 “사소해 보여서 그냥 넘긴” 변경에서 나왔습니다. 큰 결정이 아니라 “이 정도는 검증 안 해도 되겠지” 하고 넘어간 작은 분기에서 사고가 터집니다. 작성자 본인은 그게 사소하다고 믿었기 때문에, 사소하다고 믿은 그 시선으로 다시 봐도 똑같이 사소해 보입니다.

결론은 단순합니다. 작성자에게 리뷰를 맡기면 안 됩니다. 적어도 작성자만으로는 부족합니다.

교차 리뷰의 원칙

해법은 작성자와 다른 컨텍스트, 다른 모델로 리뷰하는 것입니다. 핵심은 “더 똑똑한 리뷰어"가 아니라 “실패 방식이 다른 리뷰어"입니다. 두 모델이 서로 다른 곳에서 틀린다면, 한쪽이 못 보는 곳을 다른 쪽이 봅니다. 정확도가 같아도 사각지대가 겹치지 않으면 합쳐서 더 넓은 영역을 커버합니다.

실무에서 검증된 출발점 하나는 작성과 리뷰의 모델 계열을 분리하는 것입니다. Claude로 코드를 작성하고, 머지 전에 GPT-5.5로 리뷰 게이트를 한 번 통과시키는 식입니다. 작성 분포와 리뷰 분포가 다르니 자기 리뷰의 맹점은 어느 정도 깨집니다.

다만 이 방식에는 한계가 있습니다. 리뷰어가 한 종류로 고정됩니다. 게이트를 GPT-5.5로 박아두면 모든 코드가 GPT-5.5의 시선 하나로만 검토됩니다. 작성 모델은 바뀌어도 리뷰 모델은 그대로입니다. 더 나아가, 어떤 코드는 동시성 결함을 잘 잡는 리뷰어가 필요하고 어떤 코드는 도메인 정밀도를 잘 보는 리뷰어가 필요한데, 리뷰어가 하나면 그때그때 골라 쓸 수 없습니다.

그래서 한 단계 더 추상화가 필요합니다.

리뷰어를 “스킬"로 만들기

아이디어는 이렇습니다. 리뷰라는 행위를 코딩 에이전트(Claude Code)의 스킬로 추상화하면, 리뷰의 계약은 고정한 채 리뷰어 모델만 파라미터로 교체할 수 있습니다.

여기서 “리뷰 계약"은 리뷰가 지켜야 할 규칙의 묶음입니다. 구체적으로는 네 가지입니다.

  • 적대적(adversarial)으로 본다. “괜찮아 보인다"가 아니라 “어디서 깨지는가"를 찾습니다.
  • 읽기 전용(read-only). 리뷰어는 코드를 고치지 않습니다. 찾기만 합니다.
  • 출력 형식 고정. 각 발견은 file:line + 심각도 + 수정안으로 적고, 마지막에 ship / no-ship 판정을 답니다.
  • 결과는 손대지 않고 그대로 반환. 메인 에이전트가 리뷰어의 출력을 요약하거나 각색하지 않습니다.

이 계약을 스킬 안에 박아두면, 리뷰어가 누구로 바뀌든 출력의 모양과 엄격함은 일정하게 유지됩니다. 바꾸는 건 리뷰어 모델 하나뿐입니다.

이 발상을 구현한 것이 kiro-cli(독립 CLI 에이전트)를 리뷰어로 위임하는 스킬입니다(kiro 스킬, oh-my-skills). 설계 포인트 몇 가지를 짚으면 다음과 같습니다.

구조적으로 읽기 전용(read-only by construction). 리뷰어에게 “고치지 말라"고 부탁하는 대신, 애초에 고칠 수 없게 만듭니다. 래퍼가 git diffgit status를 미리 계산해서 컨텍스트로 넘기고, 리뷰어에게는 fs_read 권한만 줍니다. 파일을 쓸 수단이 없으니 리뷰 중에 코드가 바뀔 일이 없습니다. 규율을 프롬프트의 선의에 맡기지 않고 권한으로 못 박는 방식입니다.

git scope 자동 감지. 리뷰 대상을 사람이 매번 지정하지 않아도 됩니다. 워킹 트리의 미커밋 변경인지, 브랜치와 base의 차이인지, 아니면 명시된 경로인지를 자동으로 판별해서 그 범위만 리뷰합니다.

자가검증 루프를 프롬프트로 재현. kiro-cli를 인터랙티브하게 돌리면 발견을 실제 코드로 확인하고 증거가 모일 때까지 도는 루프가 작동하는데, headless 모드에서는 이 루프가 돌지 않습니다. 그래서 그 가치를 프롬프트로 대신 강제합니다. 각 발견을 실제 코드 라인으로 확인하고, file:line을 인용한 뒤에야 보고하도록 지시합니다. “이런 버그가 있을 것 같다"가 아니라 “이 줄에 이 버그가 있다"로 적게 만드는 장치입니다.

verbatim 반환. 리뷰가 끝나면 메인 에이전트는 결과를 손대지 않고 그대로 사용자에게 돌려줍니다. 중간에서 “별거 아닌 것 같다"고 톤을 깎아내리는 일이 없습니다.

가장 중요한 차별점은 리뷰어 모델의 선택지입니다. Claude Code 자체 리뷰는 어차피 같은 패밀리 안에서 움직이고, GPT-5.5 게이트는 앞서 말했듯 한 종류로 고정됩니다. kiro-cli는 리뷰어 모델을 자유롭게 고를 수 있습니다. auto 라우팅, Claude 전 라인업은 물론이고 DeepSeek V3.2, Qwen3 Coder, GLM-5, MiniMax 같은 비-Anthropic 패밀리까지 노출됩니다. 즉 “작성자와 학습 분포가 가장 먼 모델"을 골라 리뷰를 돌릴 수 있습니다. 교차 리뷰의 효과는 분포가 멀수록 커지므로, 이 선택지가 곧 효과의 상한을 올립니다.

간단한 실증

원칙만으로는 와닿지 않으니 작게 실험했습니다. 의도적으로 결함 3개를 심은 28줄짜리 Python 계좌 모듈을 만들고, 동일한 리뷰 계약 아래 서로 다른 두 패밀리에게 리뷰를 맡겼습니다.

심은 결함은 다음과 같습니다.

class Account:
    def __init__(self, balance=0):
        self.balance = balance
        self._lock = threading.Lock()

    def withdraw(self, amount):
        # 결함 1: 락 없는 check-then-act 레이스 컨디션
        if self.balance >= amount:
            self.balance = self.balance - amount
            return True
        return False

    def apply_discount(self, price, rate):
        # 결함 2: rate 검증 없음 → rate==1이면 0 나눗셈, rate>1이면 음수
        discounted = price - (price * rate)
        return price / discounted


def transfer(accounts, src, dst, amount):
    # 결함 3: src==dst, 키 부재, 부분 실패 롤백 없음
    if accounts[src].withdraw(amount):
        accounts[dst].balance += amount
        return True
    return False

각 결함을 정리하면 이렇습니다.

  • withdraw: 잔액 확인과 차감 사이에 락이 없어, 동시 호출 시 잔액보다 많이 빠져나갈 수 있는 레이스 컨디션.
  • apply_discount: rate를 검증하지 않아 rate가 1이면 discounted가 0이 되어 ZeroDivisionError, 1보다 크면 음수 가격이 나옵니다.
  • transfer: src == dst일 때의 처리, 존재하지 않는 키 접근, 출금 뒤 입금이 실패해도 롤백이 없는 부분 실패가 모두 빠져 있습니다.

리뷰 계약은 두 모델에게 동일하게 줬습니다. adversarial / read-only / file:line+심각도+수정안 / ship·no-ship 판정. 결과는 다음과 같습니다.

항목DeepSeek V3.2Claude Sonnet 4.6
소요 시간약 53초약 23초
심은 결함 3개모두 검출모두 검출
추가로 짚은 점float 잔액의 금융 정밀도 문제, Decimal 사용 권고transfer 데드락 방지를 위한 락 획득 순서(id 기준 정렬) 권고
판정no-shipno-ship

같은 결함 코드에 대한 두 리뷰어의 findings 비교. DeepSeek V3.2와 Claude Sonnet 4.6 모두 심은 결함 3개를 검출했고, 추가로 짚은 부분이 도메인 정밀도와 동시성 정밀도로 갈렸다. 그림 1. 같은 코드, 다른 리뷰어. 두 패밀리 모두 핵심 결함 3개를 잡고 no-ship 판정을 냈지만, 추가 발견은 서로 다른 각도에서 나왔습니다.

두 모델 모두 의도한 결함 3개를 빠짐없이 찾았고, 둘 다 no-ship으로 결론 냈습니다. 흥미로운 건 “추가로” 본 부분이 갈렸다는 점입니다.

DeepSeek V3.2는 잔액을 float로 다루는 것 자체를 문제 삼았습니다. 부동소수점 오차가 금융 계산에서 누적되니 Decimal을 쓰라는 도메인 차원의 지적입니다. 심은 결함과는 별개로, 이 코드가 “돈을 다룬다"는 맥락에서 본 의견입니다.

Claude Sonnet 4.6은 transfer를 락으로 보호할 경우 발생할 수 있는 데드락을 짚었습니다. 두 계좌를 동시에 잠글 때 획득 순서를 정해두지 않으면 교착이 생기니, 계좌 id 기준으로 정렬해서 항상 같은 순서로 잠그라는 동시성 차원의 지적입니다.

같은 코드인데, 한쪽은 도메인 정밀도를, 다른 쪽은 동시성 정밀도를 추가로 봤습니다. 만약 이 코드를 Claude가 작성했다면, Claude 계열 리뷰는 작성 시의 일부 사각을 공유했을 수 있습니다. 분포가 먼 DeepSeek은 그 사각과 무관한 각도에서 코드를 봅니다. different failure modes가 실제로 작동한다는 작은 증거입니다. 물론 28줄짜리 한 번의 실험이고, 두 모델이 항상 이렇게 갈린다는 보장은 없습니다. 다만 “리뷰어를 바꾸면 보는 것도 바뀐다"는 명제는 충분히 관찰됐습니다.

도구를 스킬로 감쌀 때의 마찰

원칙과 별개로, CLI 도구를 스킬로 감싸 쓸 때는 현실적인 마찰이 하나 더 있습니다. 위 실측을 돌리는 과정에서, 환경에 설치된 kiro-cli 버전과 스킬이 기대하는 신버전 사이에 인자 차이가 있었습니다. 신버전에서 추가된 옵션을 구버전이 받지 못해 래퍼가 곧장 에러를 냈습니다. 리뷰 계약은 그대로 유지한 채 그 환경에 맞는 호출로 맞춰 돌리는 것으로 해결했습니다.

교훈은 명확합니다. CLI 도구를 스킬로 감쌀 때는 스킬이 가정하는 CLI 버전과 실제 설치본을 직접 맞춰봐야 합니다. 도구는 빠르게 바뀌고, 래퍼는 그 변화를 따라가야 합니다. 어긋나면 워크플로우 자체가 멈춥니다. 리뷰 게이트를 자동화할수록, 게이트가 조용히 깨져 있지 않은지 확인하는 절차도 같이 필요합니다.

정리

세 단계로 압축할 수 있습니다.

  1. 자기 리뷰 금지. 작성한 모델에게 리뷰까지 맡기면 같은 분포의 같은 사각지대를 두 번 통과합니다. 제조사조차 “자기 코드 결함을 흘려보내는 경향"을 개선 지표로 삼습니다.
  2. 다른 모델로 교차. 정확도보다 실패 방식의 차이가 중요합니다. 작성과 분포가 다른 리뷰어가 다른 곳을 봅니다.
  3. 리뷰어 자체를 스킬화. 리뷰 계약을 고정하고 리뷰어 모델을 파라미터로 빼면, 리뷰어가 교체 가능한 부품이 됩니다.

리뷰어를 스킬로 추상화하면, 리뷰의 엄격함은 그대로 둔 채 상황에 맞게 리뷰어 모델만 갈아 끼울 수 있습니다. 동시성이 걱정되는 코드는 그쪽을 잘 보는 모델로, 도메인 정밀도가 중요한 코드는 다른 모델로, 혹은 둘 이상을 병행해서 씁니다. 리뷰 계약이 고정되어 있으니 누구로 바꾸든 출력은 일관되고, 분포가 가장 먼 리뷰어를 고를 수 있으니 교차 효과는 최대가 됩니다. AI가 코드를 짜는 게 당연해진 만큼, 그 코드를 누가 보느냐도 설계의 대상이 되어야 합니다.


References