
AI 리뷰의 지적 사항을 자산화하기 — 추기 전용 로그와 예방 DoD로 만드는 자기 학습 루프
요약
AI 리뷰의 반복되는 지적 사항을 자산화하여 구현 및 리뷰 에이전트의 성능을 개선하는 자기 학습 루프 구축 방법을 소개합니다. 회고(Retrospective)를 통해 지적 로그를 쌓고, 이를 예방 DoD와 빈출 위반 랭킹으로 연결하여 실수를 방지하는 메커니즘을 다룹니다.
핵심 포인트
- 리뷰 지적 사항을 로그로 기록하여 에이전트의 반복 실수를 방지
- 예방 DoD(Definition of Done)를 통해 구현 전 셀프 체크 유도
- 병행 개발 환경에서의 데이터 유실을 막기 위한 이벤트 기반 설계 필요성
- 학습 메커니즘을 개발 플로우의 필수 단계로 통합
용어 메모
retrospective (레트로스펙티브): 태스크 후의 회고. 배운 것을 환경에 반영하는 단계
예방 DoD: 구현 에이전트가 제출 전에 스스로 체크하는 항목 리스트 (DoD = Definition of Done, 「완료라고 말하기 위한 조건」)
worklog: 태스크별 작업 기록 파일
P0 / P1 / P2: 리뷰 지적의 중요도 라벨 (P0=반드시 수정 / P1=수정해야 함 / P2=개선 제안)
AI 리뷰를 개발 플로우에 도입한 직후, 필자의 팀은 어떤 허탈함을 느꼈습니다. 매번, 같은 종류의 지적이 나옵니다. RORO 위반¹, 컬러 토큰을 무시한 하드코드 hex, 전송 버튼의 이중 클릭 방지 누락, 비동기 에러 무시(swallowing errors)…… 모델은 똑똑하지만, 팀의 과거 실패를 알지 못합니다. 그래서 구현 에이전트는 같은 실수를 반복하고, 리뷰 에이전트는 같은 지적을 반복합니다.
인간 팀이라면 이럴 때 레트로스펙티브 (retrospective, 회고)를 합니다. 그렇다면 AI의 개발 플로우에도 레트로스펙티브를 포함시키면 됩니다 — 이것이 본 기사에서 소개하는 메커니즘의 출발점입니다.
하는 일은 심플합니다. 태스크가 끝날 때마다 리뷰 지적을 추기 전용 로그에 쌓습니다. 로그를 집계하여 리뷰 에이전트의 빈출 위반 랭킹을 업데이트합니다. 구현 전에 막을 수 있었던 지적은 예방 DoD (제출 전 셀프 체크)에 다시 씁니다. 그리고 효과를 **실측 메트릭스 (metrics)**로 확인합니다. 이 기사에서는 그 데이터 구조부터 실측값까지, 구현 레벨에서 전부 보여드리겠습니다.
먼저 한 장의 그림입니다. 이전에 소개한 개발 플로우 (orchestrate)의 최종 페이즈에 「retrospective」라는 생략 불가능한 단계가 있으며, 거기서 배운 내용이 두 가지 경로로 환경에 다시 쓰여집니다.
(색상 구분: 보라색=학습 메커니즘 (로그·랭킹·DoD) / 청록색=에이전트의 작업 / 노란색=계측 / 파란색=개발 플로우)
경로 ① 「체크를 똑똑하게 만들기」: 지적 로그 → 빈출 위반 랭킹 → 리뷰 에이전트가 자주 발생하는 실수를 중점적으로 체크
경로 ② 「애초에 나오지 않게 하기」: 예방 DoD → 구현 에이전트의 제출 전 자기 체크
경로 ③ (보조) 「환경의 함정 제거하기」: 빌드 절차의 함정 등은 해당 스킬의 reference로 반영 (하네스 엔지니어링 (Harness Engineering)의 기본 동작 그 자체)
중요한 것은, retrospective가 「임의의 좋은 습관」이 아니라 플로우의 필수 단계라는 점과, 학습 내용 (로그에 대한 추기·DoD 업데이트)을 기능 구현과 동일한 PR에 포함하는 것입니다. 이유는 후술할 실패 패턴에서 설명하겠습니다.
개인적으로 이 메커니즘 중에서 가장 이야기하고 싶은 부분이 이 장입니다. 학습 데이터의 보유 방식은 사실 AI라기보다 일반적인 데이터 설계의 문제이며, 병행 개발과 Git의 머지 (merge)까지 고려하여 결정해야 합니다.
처음에는 리뷰 에이전트의 정의 파일에 랭킹 표를 갖게 하고, 지적이 나올 때마다 횟수를 직접 「+1」 하면 된다고 생각했습니다. 이것이 병행 개발에서 조용히 망가집니다.
브랜치 A와 브랜치 B가 동시에 실행되고 있고, 양쪽 모두 「RORO 위반: 4회」를 「5회」로 업데이트했다고 가정해 봅시다. 3-way 머지 (3-way merge, 공통 조상과 양측의 변경 사항을 대조하는 Git 등이 사용하는 통합 방식)는 양쪽의 변경이 모두 「4→5」이므로, 통합 결과는 「5」가 됩니다. 2건 있었어야 할 증분이 1건으로 사라집니다. 컨플릭트 (conflict, 충돌)조차 발생하지 않기 때문에 아무도 눈치채지 못합니다 (증분이 서로 다른 값이라면 컨플릭트가 발생하겠지만, 이번에는 수동 머지를 할 때마다 오집계의 리스크를 안게 됩니다).
그래서 카운터가 아니라 **이벤트 (event)**를 기록합니다.
# Review Findings Log (리뷰 지적의 추기 전용 로그)
- **1지적 = 1행을 끝에 추기한다. 행의 편집·삭제는 금지**
(추기 전용. 병행 브랜치 간의 3-way 머지에서 증분이 사라지지 않기 위한 설계)
...
- 2026-07-07 TICKET-123 be-review-security 인가·소유자 검증 누락 — 삭제 API에 소유자 체크 없음(IDOR)
- 2026-07-07 TICKET-123 fe-review-quality 실패 무시/무한 스피너 — 비동기 status를 소비하지 않고 성공 화면을 무조건 그림
- 2026-07-07 TICKET-123 fe-review-ux 중복 전송 방지 결여 — 저장 버튼에 isMutating 미연결·연타 시 중복 전송
...
1행의 IDOR2와 같이, 지적의 요점이 「1지적=1행」으로 압축되어 쌓여갑니다. 추기(Append)만 한다면, 병행 브랜치를 머지(Merge)해도 양쪽의 행이 모두 남습니다. 같은 끝부분에 추가했을 경우에는 컨플릭트(Conflict)가 발생하지만, 「둘 다 채택」으로 기계적으로 해결할 수 있으며, 적어도 조용히 사라지는 일은 없습니다(이력을 새로 쓰지 않고, 발생한 일을 한 건씩 추기해 나가는 「이벤트 소싱 (Event Sourcing)」이라는 데이터 설계 방식입니다). 랭킹 표 쪽은 로그로부터 만든 집계표로 위치를 정하고, 직접 편집을 금지합니다. 쌓인 기록으로부터 집계표를 다시 만드는, 일방통행적인 관계로 두는 것입니다.
「그럼, 언제 다시 집계하는가". 이 또한 인간의 기억에 의존하지 않습니다. 로그 파일 안에 HTML 코멘트로 마커를 심어 두었습니다.
<!-- regen-threshold: 20 -->
<!-- last-aggregated-count: 0 -->
스킬 그룹의 건전성을 체크하는 lint 스크립트(필수 섹션의 누락·포트 정의의 불일치 등을 확인하는 것)가, 실행될 때마다 미집계 행 수를 표시하고, 임계값인 20행을 초과하면 「★재집계 유지보수 PR 권장」이라고 통지합니다. 포인트는 통지만 할 뿐 lint를 fail 시키지 않는 것(집계 지연은 사고가 아니기 때문)과, 재집계를 기능 PR에 섞지 않고 main 상의 단독 유지보수 PR로 수행하는 것입니다. 에이전트 정의의 차분을 기능 PR에 섞으면 리뷰의 노이즈가 될 뿐만 아니라, 진행 중인 다른 브랜치의 추기와 충돌합니다.
참고로, 이 「미집계 행 수 카운트」를 담당하는 lint 측의 구현은 쉘 스크립트 십수 줄입니다(실물에서 발췌).
# review-findings-log.md 에서 2개의 마커를 읽음
threshold="$(sed -n 's/.*regen-threshold:[[:space:]]*\([0-9]\{1,\}\).*/\1/p' "$log" | head -1)"
last="$(sed -n 's/.*last-aggregated-count:[[:space:]]*\([0-9]\{1,\}\).*/\1/p' "$log" | head -1)"
...
거창한 기반은 필요 없습니다. 파일 내의 마커 2개와 grep만으로 학습 루프의 트리거를 만들 수 있습니다.

lint-skill.sh의 실행 결과. check7이 미집계 로그의 행 수를 세고, 임계값을 초과하면 「★재집계 유지보수 PR 권장」을 출력한다 (통지만 할 뿐 fail은 시키지 않는다)
집계 결과는 각 리뷰 에이전트의 정의 파일 서두에 다음과 같은 표로서 재생성됩니다.
## 빈출 위반 랭킹 (과거의 실제 지적 사항. 횟수가 많은 관점부터 우선적으로·상세하게 확인)
| 카테고리 | 횟수 |
|---|---|
...
(표는 실물에서 발췌한 것입니다. 실제로는 8개의 카테고리가 있습니다)
그리고 에이전트에 대한 지시는 다음과 같습니다.
먼저 위의 빈출 위반 랭킹 상위 카테고리부터 우선적으로·상세하게 검사하고, 그 후에 나머지 관점을 망라한다.
이는 체크의 수고(주의력)의 배분 변경입니다. LLM의 리뷰는 체크리스트가 길어질수록 항목당 주의력이 분산됩니다(인간의 훑어보기와 같습니다). 모든 항목을 균등하게 보여주는 것이 아니라, 「실제로 일어나기 쉬운 실수」부터 순서대로 주의를 기울이게 합니다. 랭킹은 그 우선순위를 팀의 실적 데이터로 결정하기 위한 메커니즘입니다.
리뷰에서 검출할 수 있더라도, 검출→수정의 왕복에는 1회분의 비용이 듭니다. 더 좋은 것은 처음부터 내지 않는 것입니다. 회고(Retrospective)에서는 「이 지적, 구현 전의 셀프 체크로 방지할 수 있었는가?」를 분류하고, 방지할 수 있었던 것은 예방 DoD (Definition of Done)에 추가합니다.
예방 DoD는 구현 에이전트가 제출 전에 항목별로 자신에게 적용하는 채점표입니다. 모두 YES/NO로 판정할 수 있는 형태로 통일되어 있습니다. 실물에서 몇 가지 항목을 발췌합니다.
- 비동기 처리 (Asynchronous processing) 훅이 반환하는 status(loading/success/failed)를 UI에서 반드시 소비하고, 성공 화면을 무조건적으로 그리지 않는다 (무한 스피너/실패 무시를 만들지 않는다). 출처: TICKET-123
- 전송 버튼의 disabled/loading 상태에 mutation의 isPending을 연결한다
...
보시는 바와 같이, 각 항목에는 출처인 티켓 번호가 붙어 있습니다. 이는 규칙이 '누군가의 취향'이 아니라 '실제로 발생한 사고'임을 보증하는 것입니다. 나중에 "이 규칙 정말 필요한가?"라는 의문이 생겨도, 출처까지 거슬러 올라가 판단할 수 있습니다. 규칙의 근거를 언제든 추적할 수 있도록 해두는 것은 규칙이 계속해서 늘어나는 메커니즘에서는 특히 중요합니다.
운영해 보며 알게 된 중요한 교훈이 있습니다. 자기 체크(Self-check)를 자기 신고(Self-declaration) 방식으로 하면 제대로 작동하지 않습니다. 실제로 있었던 사고 중에는, "비동기 에러를 catch 하고 있는가"라는 항목에 구현 에이전트가 "YES"라고 신고했음에도 불구하고, grep 해보니 catch가 없는 곳이 2군데 남아 있어 리뷰에서 P0로 검출된 사례가 있었습니다.
대책은 체크 항목 자체에 검증 방법을 적는 것입니다.
- ⚠️ 이 체크는 "grep 하여 실제로 catch가 있는지"로 확인할 것.
(「YES」라는 자기 신고만으로 통과시키지 말 것)
지난번 리뷰 수렴 규칙에서 "'대체로 대응 완료'로의 말바꾸기 금지"를 소개했지만, 사상은 같습니다. LLM을 상대로 하는 규칙은 성선설에 기반해 작성하지 마십시오. 판정을 선언이 아니라 검증(grep, lint, 실행)에 연결하십시오. 이 부분을 철저히 할 수 있느냐에 따라 셀프 체크가 실제로 효과가 있는지 여부가 완전히 달라집니다.
메커니즘을 만들면 그것만으로 만족해버리기 쉽습니다. 하지만 효과가 있는지는 별개의 문제입니다. 그래서 orchestrate 실행 시에는 worklog에 grep 가능한 정형화된 행을 반드시 기록하도록 규칙을 정했습니다.
- metrics: 최초 리뷰 BE P0=n P1=n / FE P0=n P1=n (변경 파일 m건)
- metrics: 회차 수 BE=n FE=n 중단=none|상한|정체|진동
- metrics: 최종 게이트 fail n건 (typecheck=n unit 테스트=n e2e=n)
...
(3행의 e2e는 실운영에서는 E2E 공유 패키지명으로 기록하고 있습니다. 본 기사에서는 간략화했습니다)
설계 의도를 몇 가지 말씀드리겠습니다.
- 분모를 반드시 붙인다. "P1이 16건"이라고만 하면 많은 것인지 적은 것인지 알 수 없습니다. 변경 파일 수를 분모로 덧붙여, 태스크의 규모를 맞춰 비교할 수 있도록 합니다.
- "예방 가능한 위반(Preventable violation)"이 이 메커니즘의 가장 중요한 지표입니다. 최초 P0/P1 지적 중, 기존의 예방 DoD나 랭킹 게재 항목에 해당하는 건수입니다. 즉 "기존 실수의 재발"을 의미합니다. 새로운 종류의 지적은 나와도 상관없습니다 (그것은 체크가 제대로 작동하고 있다는 증거입니다). 줄이고 싶은 것은 기존의 실수뿐이며, 이 숫자의 감소가 예방 메커니즘의 직접적인 효과입니다.
- 그 자리에서 기록한다. 최초 리뷰 통합 직후에 기록하며, 나중에 몰아서 쓰는 것은 금지합니다. 기억에 의존한 기록은 누락되거나 왜곡되기 마련입니다.
- metrics: 프리픽스(prefix)를 고정해 두었기 때문에, 집계는 원라이너(one-liner)로 끝납니다.
grep -rh "- metrics:" .claude/workspace/*/worklog.md
실제 태스크에서 몇 가지를 발췌하겠습니다 (티켓 번호는 마스킹 처리했습니다).
| 태스크 | 변경 규모 | 최초 리뷰 P0/P1 | 그 중 예방 가능한 위반 | 수정 회차 | 중단 |
|---|---|---|---|---|---|
| A (BE+FE 중규모) | BE 8 + FE 8 파일 | BE: 0/3, FE: 0/4 | 4 / 7 | BE=1, FE=1 | 없음 |
| ... | |||||
| ※ D와 E는 동일한 태스크를 FE / BE 트랙별로 별도 기록한 것입니다 (독립된 태스크로는 4건). |
솔직히 말씀드리면, 아직 데이터 수가 적어 "예방 가능한 위반이 계속 줄어들고 있다"라고 단언할 단계는 아닙니다. 그럼에도 불구하고, 이 측정만으로 이미 알 수 있는 것이 있습니다.
- 최초 지적 사항의 약 40%(합계 22/53건)가 「기존 실수의 재발」이었다. 태스크별로는 약 28%(C: 5/18)에서 약 57%(A: 4/7), 트랙 단위로는 약 83%(D: 5/6)까지 편차가 다소 큰 편입니다. 이는 리뷰가 이를 검출하여 수정 루프(Correction Loop)를 통해 고쳐지고는 있지만, 이미 알고 있는 실수인 이상 본래는 제출 전 예방을 통해 없앨 수 있었던 왕복 비용(Back-and-forth cost)입니다. 예방 메커니즘을 통해 줄일 수 있는 여지가 지적 사항의 상당 부분을 차지한다는 것을 데이터로 확인할 수 있었습니다. -
수정 루프는 1~2회 안에 수렴하고 있다. 대부분의 태스크는 1~2회 안에 P0/P1이 사라지며, 상한 3회·정체·진동에 의한 중단은 측정 시작 이후 제로입니다. 정확히 말하면, 이는 「안전장치가 작동할 상황이 아직 오지 않았다」는 뜻이기도 하며, 중단 규칙이 실전에서 제대로 작동할지는 향후 검증 사항입니다. -
측정하지 않았다면, 둘 다 「감각」에 머물러 있었을 것이다. 메커니즘의 효과를 이야기할 때 「체감상 좋아졌다」라고밖에 말할 수 없다면, 검증 메커니즘(Verifier)을 만드는 측으로서 설득력이 없습니다.
숫자의 외관보다, 숫자를 뽑아낼 수 있는 상태를 먼저 만드는 것. 이것이 이 장에서 가장 하고 싶은 말입니다.
실운영에서 겪었던 시행착오와 설계 단계에서 회피한 것들을 정리합니다.
랭킹 테이블의 직접 편집— 병행 브랜치(Parallel branch) 머지 시 증분 데이터가 사라집니다(2장 참조). 카운터가 아닌 이벤트를 기록하고, 테이블은 재생성합니다. -
「나중에 몰아서 기록하기」— 기억에 의존한 작업 로그(Worklog)는 누락되거나 왜곡됩니다. 「작업 완료 = 문서 업데이트까지」를 플로우(Flow)의 정의에 포함합니다. -
체크리스트의 무한 비대화— 실수할 때마다 항목을 추가하면 리스트가 길어져 모든 항목을 대충 훑어보게 되고, 오히려 검출률이 떨어집니다. 기존 카테고리에 집계(횟수 +1)하는 것을 우선하고, 신규 항목은 기존 카테고리에 담기지 않는 새로운 종류로만 한정합니다. 빈도순으로 정렬하여 주의 사항을 상단에 배치합니다. -
자기 체크의 자기 신고— 단순히 「YES」라고 답하게 하는 것만으로는 기능하지 않습니다. 검증 커맨드(grep, lint)까지 항목에 작성합니다(4장 참조). -
학습 반영을 「임의 사항」으로 두기— 임의적인 회고는 반드시 건너뛰게 됩니다. 회고(Retrospective)를 플로우의 필수 단계(PR 전, 생략 불가)로 편입시키고, 학습 내용을 동일한 PR에 포함시킵니다. 이렇게 하면 기능이 main에 도달할 때 학습 내용도 함께 도달하는 것이 구조적으로 보장됩니다. -
중요도의 임의 강등— P1에서 P2로의 강등을 AI의 독단에 맡기면, 지적 사항을 묵살하는 데 악용될 수 있습니다. 강등은 이유와 함께 장부에 기록하고, 사용자 승인 사항으로 만듭니다.
지금까지 기술한 메커니즘은 한 번에 완성된 것이 아니라, 운영하면서 조금씩 보완해 나간 것입니다. 오늘부터 시작한다면 최소 세트는 이 정도라고 생각합니다.
- 지적·실패를 기록한다
- 추가 전용 공간을 파일 하나로 만든다 (1지적=1행이라면 형식은 무엇이든 상관없음)
- 리뷰용 프롬프트(Agent 정의)에 「과거 빈출 실수」 섹션을 마련하고, 처음에는 수동이라도 로그에서 반영한다
- 지적 사항을 「구현 전에 막을 수 있었는가?」로 분류하고, 막을 수 있었던 것은 제출 전 체크리스트(예방 DoD)로 옮긴다. 반드시 출처를 기재한다
- 반영 타이밍을 플로우에 고정한다 (PR 전 등. 「마음 내킬 때」로 두지 않는다)
- 효과를 하나의 숫자로 측정한다 (추천하는 지표는 「기존 실수의 재발 수」)
제1회에서 소개했듯이, 하네스 엔지니어링(Harness Engineering)의 명명자인 Mitchell Hashimoto는 이를 「에이전트가 실수할 때마다, 같은 실수가 일어나지 않도록 환경 측에 영구적인 수정을 넣는」 사고방식이라고 정의했습니다. 본 기사의 메커니즘은 그것을 사람의 마음가짐에 의존하는 방식에서, 데이터 구조와 정해진 플로우로 재설계한 것입니다. 모델의 진화를 기다리지 않아도, 현재 모델 그대로 환경의 학습을 통해 성적을 올릴 수 있는—그런 설계로 되어 있습니다. 효과의 실증은 이제부터이지만, 적어도 「효과가 있었는지 숫자로 확인할 수 있는 상태」까지는 만들 수 있었습니다.
다음은 최종회입니다. 이 「닫기·측정·학습을 다시 쓰기」라는 형식을 외관 체크로 확장한, Figma to Code의 측정 우선(Measurement-first) 구현을 소개합니다.
- 엔지니어의 Agent Loop를 정비하는 기술 ─피드백 루프 is All You Need─ (karamage)
- Scoped Meta-Harness: 기업용 AI 에이전트의 자기 개선 루프 설계 이론 (shark_same_same)
- Claude Code로 하네스 엔지니어링을 실천하기 — 5개 층의 설계 패턴 (sasadango28)
- Loop Engineering 입문 (suwash)
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기