AI가 코드를 작성하는 속도가 사람이 검토할 수 있는 속도를 초과한다면, 인간은 무엇을 확인해야 하는가?
요약
AI 에이전트가 생성하는 방대한 양의 코드를 인간이 검토하기 어려워지면서, 코드 리뷰 방식에 근본적인 변화가 필요하다는 논의입니다. 단순히 줄 단위로 읽기보다 '무엇이 올바른지'를 정의하는 명세서 작성과 강력한 통합 테스트(merge gate) 구축으로 초점을 옮겨야 한다고 주장합니다.
핵심 포인트
- 코드 생성 속도가 인간 검토 속도를 초과함에 따라 리뷰 방식의 변화가 필요하다.
- 단순 diff 읽기에서 '무엇이 올바른지' 정의하는 명세서 작성으로 전환해야 한다.
- 개별 테스트 통과 여부보다, 모든 병합 후 전체를 재실행하는 강력한 통합 게이트가 필수적이다.
현재 많은 팀들이 처한 상황이 있습니다. 에이전트(agent)가 600줄 분량의 PR(Pull Request)을 4분 만에 열어 놓습니다. 이것을 제대로 읽는 데는 40분이 걸립니다. 당신이 끝낼 무렵에는 이미 두 개가 더 대기열에 도착해 있습니다. 이제 아무도 '검토되었다'는 것이 정확히 무엇을 의미하는지 결정하지 못했기 때문에, 그 공백은 단순히 훑어보고 'lgtm'(Looks Good To Me)으로 채워집니다.
코드를 작성하는 것은 싸졌습니다. 하지만 그것을 읽는 것은 그렇지 않았습니다. 따라서 진짜 질문은 '우리가 에이전트를 사용해야 하는가'가 아닙니다. 바로 이것입니다: 코드가 인간이 읽을 수 있는 속도보다 빠르게 나타날 때, 인간은 실제로 무엇을 확인해야 할까요?
저는 아무도 확정적인 답을 가지고 있지 않다고 생각합니다. 아래는 제가 계속 목격하는 세 가지 진영과 각 진영이 주장하는 논거, 그리고 아무도 해결하지 못했다고 생각하는 부분입니다. 이 내용에 대해 반박해 주세요.
이 주제가 제기된 배경
최근 r/rust 스레드에서 하나의 레포지토리(repo)에 약 20개의 코딩 에이전트를 병렬로 실행하는 것에 대한 논의가 있었습니다. 원래는 빌드 캐싱(build caching)에 관한 것이었지만, 가장 많은 표를 받은 댓글은 캐싱을 완전히 무시했습니다:
도대체 어떻게 20개의 동시 프로세스가 만든 작업을 검토할 수 있습니까?
그 이후의 논의 대부분은 '검토'에 관한 것이었고, cargo에 관한 것은 아니었습니다. 증거로서, 추천으로서가 아닌, 이 상황이 시작된 배경을 알려드립니다:
- 각자 독립적인 git worktree를 가진 약 20개의 에이전트가 모두 동일한 Rust 크레이트(crate)에서 작업했습니다.
- 로컬 모델이 RAM에 상주하는 동안
rustc가 완전 병렬성으로 접근 위반(access violation)과 함께 충돌하여, 빌드는CARGO_BUILD_JOBS=4로 제한되었습니다. - 테스트 및 확인 결과는 worktree 전반에 걸쳐 콘텐츠 해시(content hash)로 캐싱되었습니다. 규모가 작은 스웜(swarms)의 경우, 80% 이상이 이 캐시에서 가져왔습니다.
- 테스트 점수가 올라가고 이전에 통과했던 테스트가 깨지지 않는 한 아무것도 병합되지 않았으며, 변경 사항은 하나씩 병합되었고, 각각에 대해 재평가가 이루어졌기 때문에, 개별적으로는 통과하지만 함께는 빌드를 망가뜨릴 수 있는 두 가지 변경 사항도 문제가 없었습니다. 최종 병합된 diff를 인간이 적용하기 전까지는 실제 레포지토리는 건드려지지 않았습니다.
다시 말해: 에이전트 결과물에 대한 줄 단위의 인간 검토는 없습니다. 이것은
논거: 코드를 20개 스트림으로 읽을 수는 없지만, 무엇이 '올바른지' 정의하고 기계가 모든 변경 사항에 대해 그것을 증명하게 할 수 있습니다. 이는 인간의 작업을 diff(차이점)를 읽는 것에서 명세서(specs)를 작성하는 것으로 옮기며, 확장성이 생깁니다.
작동하기 위해 필요한 것:
- 그 일을 맡기에 충분히 좋은 테스트. 위에 병합 게이트(merge gate)가 있는 얇은 테스트 스위트도 여전히 그저 얇은 테스트 스위트에 불과합니다.
- 각 브랜치별이 아니라, 모든 병합 후에 모든 것을 재실행하는 병합 게이트. 이것이 바로 '둘 다는 혼자서는 통과했지만, 함께는 실패하는' 경우를 잡아내는 방법입니다.
- 에이전트가 자신들을 판단하는 테스트를 편집할 수는 없습니다. 이건 지나치게 의심스러워 들릴 수 있습니다. 하지만 그렇지 않습니다.
마지막 요점은 저희가 진행한 실험에서 나온 것입니다. 작고 고장 난 레포지토리 5개, Claude Code headless를 Opus와 Sonnet, Haiku에 사용했으며, 의도적으로 서두른 프롬프트(
다운보트(downvoted)되었습니다. 그럼에도 불구하고, 현재 실무에서 가장 흔한 설정일 가능성이 높습니다. 왜냐하면 저렴하고 테스트가 잡아낼 수 없는 것들—명명 규칙(naming), 구조(structure), 명백한 보안 취약점 냄새(obvious security smells), '왜 이 파일이 900줄이나 돼 있는가' 같은 문제들을 포착하기 때문입니다.
작동에 필요한 요소:
- 저자가 가진 사각지대(blind spots)를 공유하지 않는, 실제로 다른 사람(다른 공급업체, 프롬프트 또는 컨텍스트)의 검토자.
- 병합(merge)을 막거나, 적어도 답글을 강제하는 발견 사항. 아무도 읽지 않는 댓글만 남기는 검토자는 그저 장식에 불과합니다.
이에 대한 가장 강력한 반론: 두 모델이 함께 확신을 가지고 틀릴 수 있으며, 모델 리뷰는 많은 노이즈를 생성한다는 것입니다. 모든 PR(Pull Request)마다 14개의 사소한 지적 사항(nitpicks)이 달리면 사람들은 그것들을 읽지 않게 되고, 당신은 'lgtm'(Looks Good To Me) 문제를 한 단계 높은 수준으로 재건축하게 됩니다.
캠프 3: 병합하고 기도하기 (그리고 나중에 읽기)
아무도 이렇게 말하지는 않지만, 많은 팀들이 이런 식으로 일합니다. 빠르게 병합하고, 운영 환경(production)을 지켜보며, 신속하게 되돌립니다(revert). 검토는 사후에, 사고 조사 과정이나 해당 파일을 변경해야 하는 다음 사람이 할 때 이루어집니다.
주장: 잘못된 병합의 비용은 얼마나 주의 깊게 읽느냐가 아니라, 얼마나 빨리 감지하고 롤백할 수 있느냐에 의해 결정됩니다. 좋은 관측 가능성(observability), 기능 플래그(feature flags) 및 쉬운 롤백 기능을 갖추고 있다면, 병합 전에 모든 줄을 읽는 것은 지불할 필요가 없는 세금입니다.
이에 대한 가장 강력한 반론은 같은 스레드에서 나왔으며, 에이전트들이 의존성(dependencies)을 변경하는 것에 관한 것이었습니다.
테스트가 검토자가 된다면, 모든 테스트 변경 사항은 레포지토리에서 가장 중요한 diff(차이점)이 됩니다. 하지만 에이전트 역시 테스트를 작성하므로, 버그 있는 동작을 주장하는 새로운 테스트는 완벽한 테스트와 똑같이 보일 수 있습니다. 기존 테스트를 잠가두는 것은 에이전트가 게이트를 약화시키는 것을 막지만, 낮은 기준을 설정하는 새로운 테스트에 대해서는 아무것도 하지 못합니다.
또한 두 번째로 더 조용하고 미묘한 문제가 있습니다. 우리가 검토를 위임할수록, 실제로 시스템 작동 방식을 아는 사람은 줄어듭니다. 체크가 완벽하더라도, 결국 누군가는 아키텍처를 변경해야 하는데, '테스트가 통과했다'라는 사실은 그 코드가 왜 이런 형태로 만들어졌는지 아무도 가르쳐주지 못합니다.
그리고 우리는 이 모든 것을 판단하는 데 능숙하지 않습니다. METR의 2025년 무작위 연구에서는 숙련된 오픈소스 개발자 16명이 실제 작업 246개를 완료했습니다. 그들은 AI가 자신들을 24% 빠르게 해줄 것이라고 예상했습니다. 하지만 AI를 사용했을 때는 오히려 19% 더 오래 걸렸고, 나중에도 여전히 AI가 자신들을 20% 빠르게 했다고 믿었습니다. METR 자체도 이제 이 결과를 구식이라고 부르며, 이후 도구들이 많이 개선되었습니다. 그러나 팀이 검토 프로세스가 '괜찮게 느껴진다'고 말할 때마다, 느꼈던 속도와 실제 속도 사이의 간극은 기억할 가치가 있습니다.
오늘날 사용할 수 있는 것들
어느 진영에 있든 상관없이, 이 방법들은 저렴하고 어느 한쪽 편을 들 필요가 없습니다:
- 기존 테스트를 에이전트에 읽기 전용(read-only)으로 설정하세요. 새로운 테스트는 허용하되, 기존 어설션(assertion)을 변경하려면 반드시 사람이 검토해야 합니다. 이것이야말로 제가 아는 가장 높은 레버리지의 규칙입니다.
- 코드 diff보다 테스트 diff를 먼저 검토하세요. 테스트가 정직하다면 코드는 대부분 제약됩니다. 그렇지 않다면 코드 리뷰 자체가 무의미했을 것입니다.
- 한 번에 하나씩 병합(merge)하고, 매번 병합 후에 모든 것을 다시 실행하세요. 자체 브랜치에서 통과하는 것과 병합 후 통과하는 것은 다릅니다.
- 의존성 세트(dependency set)를 고정하세요. 에이전트는 새로운 크레이트(crate)나 패키지를 요청합니다. 이를 사람이 승인해야 합니다. 모든 새로운 의존성은 부작용(side effect)이 아니라 검토 대상 항목입니다.
- 사람에게 더 작고 명확한 diff를 제공하세요. 에이전트에게 "무엇이 바뀌었고 왜 바뀌었는지"에 대한 짧은 설명과 가장 위험도가 높은 30줄만 요청하세요. 전체 600줄을 훑어보는 대신, 그 부분을 주의 깊게 읽으세요.
- 느낌으로 검토하지 말고 측정하세요. 에이전트 PR(Pull Request)에서 비롯된 리버트되거나 핫픽스된 코드가 얼마나 자주 발생하는지 추적하세요. 이것에 답할 수 없다면, 자신이 실제로 어느 편에 서 있는지 모른다는 뜻입니다.
당신의 차례입니다
실제 팀들이 이 문제를 어떻게 처리하는지, 특히 문제가 발생했던 환경 설정(setup)이 있다면 듣고 싶습니다:
- 에이전트 PR이 너무 커서 읽기 어려울 때, 당신은 실제로 무엇부터 확인하나요?
- 두 번째 모델 검토자(second-model reviewer)가 당신의 테스트가 놓친 것을 포착한 적이 있습니까? 아니면 대부분 노이즈만 추가했나요?
- 당신의 환경 설정에서는 누가 테스트를 검토합니까? 에이전트가 자신의 버그를 승인하는 테스트를 작성하는 것을 막는 규칙이 있습니까?
- 만약 완전한 병합 및 리버트(merge-and-revert) 과정을 거쳤다면, 무엇이 그것을 신뢰하게 만들었고, 가장 먼저 잘못된 것은 무엇이었나요?
위 내용 중 본인이 경험한 것과 맞지 않는 것이 있다면 수정해 주세요. 실제 실패 사례가 제 이론보다 더 가치가 있습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기