Home > AI > AI-Pairing > AI 코드리뷰 봇 2종을 파이프라인에 넣고, 봇이 못 잡는 걸 배웠다

AI 코드리뷰 봇 2종을 파이프라인에 넣고, 봇이 못 잡는 걸 배웠다
AI Claude Code 코드리뷰 개발문화

배경

한 프론트엔드 저장소에 AI 코드리뷰를 두 겹으로 붙였다. PR에 자동으로 달리는 Gemini 리뷰 봇, 그리고 작업 중간에 명시적으로 태우는 code-review 패스다.

목표는 “지적을 받는 것” 자체가 아니라, 지적사항을 실제 커밋으로 환류시키는 것이었다. 두 달 정도 돌려보니 봇이 잘 잡는 클래스와 전혀 못 잡는 클래스가 꽤 뚜렷하게 갈렸다.

참고로 이 저장소는 해당 기간 커밋 1708건 중 1212건(71%)Co-Authored-By: Claude 서명이 남아 있다. 모델 세대도 커밋 메시지에 그대로 기록돼 있어서 Sonnet 4.6 → Opus 4.8 → Opus 4.8 1M → Opus 5 순으로 바뀐 흔적을 볼 수 있다.

봇이 잡은 것

실제로 커밋으로 이어진 지적들을 유형별로 정리하면 이렇다.

유형 지적 내용
관용구 위반 차트 option 객체를 매 렌더 재생성 (useMemo 누락)
누락된 cleanup 메모리 누수 3건 추가 발견
레이스 컨디션 민감도 분석 로딩 순서
가드 비대칭 heatmap의 x축은 가드가 있는데 y축만 있음
중복 방어 escapeHtml이 이미 정규화하는데 바깥에 String() 래퍼
부작용 혼입 setGridRows 업데이터 함수 안에서 다른 setState 호출
도달 불가능한 분기 crumbOf의 구분선 처리

몇 개는 조금 더 설명할 만하다.

업데이터 안에 부작용이 섞여 있었다

setGridRows((prev) => { ... setDeleteIdList(...); return next; }) 형태였다. React의 업데이터 함수는 순수해야 하는데 안에서 다른 상태를 갱신하고 있었다. Strict Mode나 동시성 렌더에서 업데이터가 두 번 호출되면 deleteIdList에 같은 항목이 중복으로 들어간다.

수정은 부작용을 이벤트 핸들러 쪽으로 끌어올려 현재 스코프의 gridRows를 직접 참조해 계산하고, 두 상태를 순차로 갱신하는 방식이었다. 실제로 버그로 신고된 적은 없었지만 조건이 맞으면 터질 자리였다.

가드의 비대칭

heatmap에서 yCats[y]에는 존재 확인 가드가 있는데 MODULES[x]에는 없었다. 봇이 “왜 한쪽만 있냐”고 지적했다. 이런 대칭성 위반은 사람이 diff를 볼 때 잘 안 보이는데, 패턴 매칭으로는 잘 걸린다.

상태 미러링

useState + useEffectlocation.hashactiveId에 복사해두고 쓰는 코드가 있었다. 이건 desync 창(hash는 바뀌었는데 state는 아직 안 바뀐 순간)을 만든다. 렌더 시점에 hash에서 직접 파생하도록 바꿔 미러링과 effect를 함께 제거했다.

봇이 못 잡은 것

가장 큰 성능 문제는 봇이 하나도 못 잡았다.

PDF 다운로드가 7초 걸리던 문제가 그렇다. 코드만 보면 어디에도 이상한 구석이 없다. react-pdf로 PDF를 만드는 지극히 평범한 코드다. 병목이 한글 폰트의 텍스트 layout이라는 건 chrome-devtools로 직접 계측해서 20여 종의 조합을 실험한 끝에 나왔다. 정적 분석으로는 도달할 수 없는 결론이다.

봇이 잘 잡는 것과 못 잡는 것

두 달을 돌리고 나서 경계가 이렇게 정리됐다.

잘 잡는 클래스 못 잡는 클래스
관용구 위반 (useMemo·useCallback 누락) 성능 병목 (실측이 필요한 것)
누락된 cleanup (dispose·revoke·clearTimeout) 도메인 정합성 (이 계산식이 업무적으로 맞는가)
exhaustive check·가드 대칭성 아키텍처 수준의 중복 (V1/V2 이중 스택 같은 것)
도달 불가능한 분기 사용자에게 실제로 어떻게 보이는가

공통점이 있다. 봇이 잘 잡는 건 파일 하나 안에서 판정 가능한 것이고, 못 잡는 건 실행해봐야 알거나 도메인 지식이 필요한 것이다.

남는 교훈

AI 리뷰 봇을 붙이면 지적사항 수는 확실히 늘어난다. 위 표의 왼쪽 열은 사람 리뷰어가 꾸준히 잡기 어려운 종류이기도 하다 — 지루하고, 놓쳐도 당장 티가 안 나기 때문이다. 그런 걸 기계가 대신 봐주는 건 실질적인 이득이다.

다만 봇을 붙였다고 리뷰가 끝났다고 착각하면 위험하다. 오른쪽 열은 여전히 사람 몫이고, 그중에서도 성능 문제는 계측 없이는 누구도 알 수 없다. 봇의 통과 여부를 “문제 없음”의 근거로 쓰지 않는 것 — 그게 두 달 동안 얻은 가장 실용적인 결론이다.