본문으로 건너뛰기
JSL107's Tech Note
돌아가기

과거 코드 리뷰 판정으로 프롬프트 회귀 테스트 만들기

8분 분량이 글 고치기
목차 · 7

코드 리뷰 프롬프트를 바꾼 뒤 품질이 나빠졌는지는 어떻게 알 수 있을까요? 이 글에서는 사람이 기각한 지적 44건과 실제 코드 수정으로 이어진 지적 131건을 당시 커밋에서 다시 평가하는 historical replay를 다뤄요. 제공된 근거에는 실제 실행 결과와 복원 실패·중복 기록·재시작 정책의 집계 규칙이 없어 성능이 얼마나 나아졌는지, 일부 지표가 정확히 어떻게 동작하는지는 확인할 수 없어요.

형식 점수만으로는 리뷰 품질을 판단할 수 없다

대상 저장소의 코드 리뷰어 프롬프트에는 최근 60일 동안 관련 커밋이 19개 생겼어요. 변경이 잦다는 것보다, 바꾼 뒤 품질이 나빠졌는지 판단할 기준이 없다는 게 문제였어요.

기존 contractScore는 필수 필드가 있는지 등 출력 계약을 검사해요. 최근 30일간 CODE_REVIEWER를 실행한 312건의 평균 점수는 1.00이었어요. 이 수치로 알 수 있는 건 출력 형태가 계약을 지켰다는 사실뿐이에요. 지적한 내용이 실제 결함인지, 사람이 이미 기각한 오탐을 다시 만들어 냈는지는 알 수 없어요.

pnpm review:dry는 PR의 현재 head를 다시 리뷰하지만 결과를 사람의 과거 판정과 대조해 채점하지는 않아요. 과거 리뷰에서 발견한 문제를 개발자가 이미 수정했다면 현재 diff에는 원래 문제가 남아 있지 않아요. 회귀 테스트를 하려면 과거와 같은 코드에 새 프롬프트를 적용해, 사람이 인정한 지적을 다시 찾고 기각한 지적은 되풀이하지 않는지 확인해야 했어요.

사람의 판정 이력을 평가 데이터로 바꾸다

운영 중 사람이 내린 판정은 이미 리뷰 지적 기록 테이블에 쌓여 있었어요. 이번 작업에서는 그중 두 종류의 자료를 사용했어요.

판정 이력건수회귀 테스트에서 확인할 것
사람이 👎로 기각한 지적44건같은 지적이 다시 발생하는가
실제 코드 수정으로 이어진 지적131건새 프롬프트도 해당 문제를 찾는가

각 기록에는 커밋 SHA와 파일, 줄 정보가 함께 남아 있었어요. 덕분에 지적을 특정 시점의 코드와 연결해 평가 사례로 쓸 수 있었어요.

여기서는 수정으로 이어진 이력을 정탐 후보로, 사람이 기각한 이력을 오탐 후보로 사용해요. 다만 사람의 운영 판정을 회귀 기준으로 삼았을 뿐, 그 판정이 일관됐는지는 따로 검증하지 않았어요.

현재 diff가 아니라 당시 커밋을 다시 리뷰한다

평가 명령은 pnpm review:replay로 구성했어요. 저장된 지적의 커밋 SHA를 기준으로 당시 코드를 다시 리뷰하는 명령이에요.

재생 과정은 다음과 같다.

pr_review_finding
├─ 사람 판정: 기각 또는 실제 수정
├─ 당시 커밋 SHA
├─ 파일 경로
└─ 줄 위치
│
▼
당시 커밋의 diff 조회
│
▼
기존 코드 리뷰 유스케이스에 diff 주입
│
▼
새 프롬프트로 다시 리뷰
│
▼
파일과 줄 위치를 기준으로 과거 판정과 비교

기존 리뷰 유스케이스와 조회한 diff를 주입하는 경로를 활용했어요. 평가 전용 구현이 운영 경로와 멀어지면 테스트를 통과해도 실제 리뷰에서는 다른 결과가 나올 수 있으니까요.

과거 리뷰어가 src/queue.ts의 120번째 줄에서 큐가 종료되지 않는 문제를 찾았고 이후 수정 커밋이 반영됐다고 해볼게요. 현재 PR head를 리뷰하면 그 문제는 이미 사라진 상태예요. 이때 지적이 나오지 않았다는 이유로 새 프롬프트를 실패로 처리하면 리뷰어의 성능이 아니라 입력 시점의 차이를 채점하게 돼요. 당시 커밋을 입력하면 이전 리뷰어와 새 리뷰어가 같은 상태의 코드를 보게 돼요.

같은 파일의 ±5줄 안에서 지적을 매칭한다

재생한 리뷰 결과가 과거 지적과 같은 문제인지 판단하려면 매칭 기준이 필요해요. 이번 작업에서는 별도의 LLM 채점관을 두지 않고 같은 파일에서 기준 위치의 ±5줄 안에 지적이 있는지 확인했어요.

과거 지적이 src/queue.ts:120에 기록돼 있다면 다음 범위를 같은 위치로 본다.

파일: src/queue.ts
허용 범위: 115~125줄

서로 다른 원인을 지적해도 위치가 가까우면 같은 발견으로 처리될 수 있고 같은 문제를 더 넓은 함수나 다른 관련 파일에서 설명하면 매칭에 실패할 수 있어요. 그래도 이 규칙은 반복해서 적용할 수 있고 채점관 모델의 프롬프트나 비결정성이라는 변수를 더하지 않아요. 모든 지적의 의미를 완벽하게 분류하려는 게 아니라, 프롬프트 변경 전후를 같은 기준으로 비교해 회귀 신호를 얻으려는 거예요.

±5줄은 이 평가 체계에서 정한 운영 기준이지 보편적인 최적값은 아니에요. 언어와 diff 크기, 리뷰어가 위치를 지정하는 방식에 따라 적절한 범위는 달라질 수 있어요. 파일 이동이나 대규모 코드 재배치를 어떻게 처리하는지도 확인되지 않았어요.

정탐 유지와 오탐 재발을 함께 본다

지적을 거의 생성하지 않는 프롬프트는 오탐을 줄일 수 있지만 실제 문제도 놓쳐요. 반대로 지적을 많이 내놓으면 과거 정탐을 다시 찾을 가능성은 커지지만 사람이 기각했던 지적까지 되살아날 수 있어요. review:replay는 이 두 방향을 나눠 살펴봐요.

비율로 나타내면 다음과 같이 해석할 수 있다.

정탐 유지율 =
다시 발견한 수정 이력 건수 / 평가 가능한 수정 이력 건수
오탐 재발률 =
다시 발생한 기각 이력 건수 / 평가 가능한 기각 이력 건수

분모를 단순히 131건과 44건으로 고정하기보다는 평가 가능한 사례 수로 잡는 편이 안전해요. 커밋이나 diff를 복원하지 못한 사례를 실패로 볼지 제외할지에 따라 지표의 의미가 달라지니까요. 이번 변경에서 확인할 수 있는 성과는 특정 유지율이나 재발률이 개선됐다는 결과가 아니라, 프롬프트 후보를 두 축으로 비교할 회귀 검증 체계를 마련했다는 점이에요.

다른 검사와 함께 사용할 때의 한계

Historical replay는 운영 이력을 재사용하므로 실제 리뷰 분포에 가까운 사례를 평가할 수 있어요. 하지만 아직 발견되지 않은 유형의 결함은 데이터셋에 들어 있지 않아요. 사람이 잘못 기각하거나 수정하기로 했다면 그 오류도 평가 기준에 들어가요. 위치 기반 매칭만으로는 지적의 의미까지 같다고 보장할 수 없고 과거 사례에 지나치게 맞춰 프롬프트를 조정하면 새로운 코드 유형에 대한 일반화 성능이 낮아질 수도 있어요.

검사마다 찾아내는 실패가 달라요.

검증 수단확인하는 것확인하지 못하는 것
contractScore필수 필드 등 출력 계약지적 내용의 정확성
pnpm review:dry현재 PR에서 리뷰가 실행되는지과거 사람 판정 대비 품질
pnpm review:replay과거 정탐 유지와 오탐 재발새로운 결함 유형 전반의 성능

출력 스키마 변경은 계약 점수로 확인하고 현재 실행 경로의 문제는 dry run으로 살피고, 프롬프트 품질 회귀는 replay로 검증하는 구성이 적절해요.

재현에 필요한 조건과 다음 실험

같은 평가 방식을 적용하려면 사람이 인정하거나 기각한 리뷰 판정, 지적 당시의 커밋 SHA, 대상 파일 경로와 줄 위치가 필요해요. 당시 커밋의 diff를 복원할 Git 이력과 그 diff를 실제 리뷰 경로에 주입할 방법, 재실행 결과를 비교할 고정된 매칭 규칙도 있어야 해요.

다음 명령으로 실행한다.

pnpm review:replay

실제 저장소에 적용할 때는 필요한 환경 변수와 외부 모델 설정, 데이터 저장소 연결 조건을 확인하고 실패한 사례의 재시도와 중복 지적 제거, 중단 후 재시작 범위를 정해야 해요. 다음 실험에서는 같은 사례 집합으로 프롬프트 변경 전후의 정탐 유지율과 오탐 재발률을 비교하고 매칭 범위를 ±3줄, ±5줄, ±10줄로 바꿨을 때 판정이 얼마나 달라지는지 살펴볼 수 있어요. 복원 실패와 중복, 재시작 정책을 고정한 뒤 평가 가능한 분모가 실행마다 달라지지 않는지도 검증해야 해요.


이 글 고치기
이 글 공유하기:

이전 글
SDK 도입 전에 나눌 위임과 대화 소유권