본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 05. 02:44

10일 동안 작은 PR 리뷰 봇을 만들며 배운 것들

요약

팀의 컨벤션과 문서를 기반으로 Pull Request를 리뷰하는 GitHub 봇 개발 경험을 공유합니다. 단순한 린터를 넘어 LLM을 실제 작업 흐름에 유용하고 측정 가능하게 통합하기 위한 아키텍처 설계에 집중했습니다.

핵심 포인트

  • 팀의 구체적인 규칙(CLAUDE.md, Notion 등)을 지식 베이스로 활용
  • NestJS, BullMQ, Redis를 활용한 안정적인 웹훅-큐-워커 파이프라인 구축
  • Chroma와 Voyage를 이용한 벡터 인덱싱 기반의 RAG 구현
  • Claude Haiku를 활용한 멀티 턴 에이전트 루프 설계

지난 10일 동안 저는 팀이 직접 작성한 컨벤션(Conventions)을 바탕으로 풀 리퀘스트(Pull Request, PR)를 리뷰하는 GitHub 봇을 만들었습니다. 단순히 느낌만으로 작동하는 일반적인 "분위기 위주의 린터(Linter with vibes)" 리뷰어가 아닙니다. CLAUDE.md 파일, 온보딩 문서, 그리고 이제는 아무도 읽지 않는 Notion 페이지에 담긴, 여러분의 팀이 합의한 구체적인 규칙들을 알고 있는 작고 집중된 리뷰어입니다.

이 프로젝트에 대해 글을 쓰고 싶은 이유는 솔직히 제가 이것에 조금 집착하고 있기 때문입니다. 봇 자체(이것은 괜찮습니다, 스프린트 프로젝트니까요)에 집착하는 것이 아니라, 문제의 "형태"에 집착하고 있습니다. 즉, LLM(Large Language Model)을 실제 작업에서 유용하고, 반복 가능하며, 측정 가능하게 만들기 위해 실제로 무엇이 필요한가에 대한 문제입니다. 현재의 "AI 툴링(AI tooling)" 중에는 오직 분위기와 데모로만 이루어진 버전이 있습니다. 반면, 먼저 지루한 기반(Substrate)을 구축해야 하는 또 다른 버전이 있습니다. 이번 스프린트는 그 지루한 기반을 둘러보는 여정이었으며, 저는 시작했을 때보다 호기심이 줄어들기는커녕 오히려 더 커진 상태로 마쳤습니다.

소스 코드는 github.com/azaz101hassan/ai-pr-review-copilot에서 공개되어 있습니다.

내가 만든 것

이 봇은 GitHub pull_request 웹훅(Webhook)을 수신하고, 팀 컨벤션이 벡터 인덱싱(Vector-indexed)된 지식 베이스(Knowledge base)를 바탕으로 diff(차이점)에 대해 멀티 턴 에이전트 루프(Multi-turn agent loop)를 실행하며, 발견된 사항을 인라인 리뷰 코멘트(Inline review comments)와 하나의 워크스루 코멘트(Walkthrough comment)로 게시합니다. Next.js 대시보드는 동일한 SQLite 저장소에서 데이터를 읽어와 운영자가 봇이 무엇을 하고 있는지 한눈에 확인할 수 있게 합니다.

기술 스택은 의도적으로 지루하게 구성했습니다: 웹훅 → 큐(Queue) → 워커(Worker) 파이프라인을 위해 NestJS + BullMQ + Redis를 사용했고, 감사 로그(Audit log)로는 SQLite + Drizzle을, 검색(Retrieval)을 위해서는 Chroma + Voyage voyage-code-3를, 기본 리뷰어 LLM으로는 Claude Haiku 4.5를 사용했습니다. 핵심은 구성 요소가 무엇인지가 아니라, 그 요소들이 어떻게 맞물려 돌아가는지에 대한 규율에 있습니다.

Walkthrough comment from the bot on a PR that exceeded the 500-line size gate

PR #15에 봇이 게시한 실제 워크스루 (walkthrough) 댓글입니다 — 774개의 변경된 라인이 있으며, 500라인 크기 제한 (size gate)을 초과했습니다. 이 봇은 제대로 리뷰할 수 없는 PR에 대해 신뢰도가 낮은 리뷰를 게시하는 대신, 자신의 추론 과정을 직접 설명합니다.

이 도구가 실제로 빛을 발한다고 생각하는 지점

스프린트(sprint) 중반에 저를 가장 놀라게 했던 점은, 봇의 범위를 좁게 설정할수록 성능이 오히려 _더 좋아졌다_는 것입니다. 제가 배운 점들을 말씀드리기 전에, 이와 같은 도구가 실제로 제 역할을 다할 수 있는 곳이 어디인지 말씀드리겠습니다.

  • 새로운 기여자 온보딩 (Onboarding). 새로운 팀원이 합류하여 PR을 올리면, 시니어 개발자가 직접 작성했을 법한 "데이터베이스 서비스에 CRUD를 추가하지 마세요"와 같은 동일한 코멘트를 받게 됩니다. 봇이 코멘트를 한 번 작성하면, 시니어 개발자는 오후 시간을 온전히 되찾을 수 있습니다.
  • 고유한 스타일 가이드를 가진 오픈 소스 메인테이너 (Maintainers). 작성 규칙(CONTRIBUTING.md, 프로젝트 스타일 문서, "이 패턴보다 저 패턴을 선호함"과 같은 명시적 규칙)은 있지만, 스쳐 지나가는 모든 PR에 이를 강제할 여력이 없는 저장소(repos)들입니다.
  • 린터 (Linter)가 잡아낼 수 없는 아키텍처 규칙이 있는 코드베이스. "컨트롤러(Controllers)는 얇아야 한다.", "레포지토리(Repositories)는 인터페이스 토큰 뒤에 존재해야 한다.", "설정(config) 모듈 외부에서 process.env를 직접 읽지 않는다." 등은 많은 성숙한 코드베이스에 존재하는 실제 규칙들이지만, ESLint나 RuboCop으로는 확인할 수 없습니다.
  • 활발하게 움직이는 팀 내의 작고 집중된 PR들. 규칙이 많기 때문에 규칙 위반율은 높지만, 변경 사항(diff)이 충분히 작아서 리뷰어의 주의력이 실제 병목 현상 (bottleneck)이 되는 경우입니다.

이 도구가 명시적으로 목표로 하지 않는 것은 CodeRabbit, Sourcery 또는 다른 훌륭한 상용 리뷰어들을 대체하는 것이 아닙니다. 그들은 제가 혼자 만들 수 있는 것보다 범용적인 리뷰에 더 뛰어납니다. 여기서 이 도구의 틈새 시장은 "상용 도구들이 당신 팀의 특정 규칙을 알지 못하기 때문에 볼 수 없는 것들을 리뷰하는 것"입니다.

예상치 못하게 배우게 된 세 가지

1. LLM은 시스템에서 가장 작은 부분이다

프롬프트 (Prompt)가 가장 어려운 부분이 될 것이라고 생각하며 시작했습니다. 하지만 그렇지 않았습니다. 진짜 어려운 부분은 큐 (Queue, GitHub의 재시도가 중복 리뷰를 발생시키지 않도록 하는 용도), 감사 로그 (Audit log, 외부 호출 전에 모든 작업이 데이터베이스에 행으로 기록되도록 하는 용도), 평가 하네스 (Eval harness, 변경 사항이 봇을 더 좋게 만들었는지 혹은 나쁘게 만들었는지 판단하기 위한 용도), 그리고 대시보드 (Dashboard, 로그를 grep으로 검색하지 않고도 봇이 무엇을 하고 있었는지 확인할 수 있는 용도)였습니다.

LLM은 사용자가 보는 표면입니다. 데이터 모델 (Data model)은 그 표면을 신뢰할 수 있게 만드는 핵심입니다.

2. 10일간의 스프린트에서도 평가 온도계가 필요하다

5일 차쯤 되었을 때, 평가 하네스 (Eval harness) 구축을 거의 건너뛸 뻔했습니다. "10일밖에 안 남았으니, 그냥 내 PR에 봇을 직접 써보며 테스트(Dogfooding)하면 돼."라고 생각했죠. 그것은 이번 스프린트에서 가장 게으른 본능이었고, 제가 그 생각을 억누르길 정말 잘했다고 생각합니다.

하네스가 봇을 더 좋게 만드는 것은 아닙니다. 프롬프트를 수정하고 규칙을 큐레이션 (Curating)하는 것은 여전히 저의 몫입니다. 하네스가 하는 역할은 제가 한 변경 사항이 어느 방향으로 지표를 움직였는지 알려주는 것입니다. 이것이 없다면 모든 프롬프트 수정은 그저 '느낌 (Vibes)'에 의존하는 것이 됩니다. 스프린트 동안 세 번이나, 하네스는 제가 승리라고 확신했던 변경 사항이 실제로는 그렇지 않거나, 혹은 그 반대라는 것을 알려주었습니다. 그것이 바로 이 도구의 전체 ROI (투자 대비 효율)입니다.

구조는 매우 단순합니다. 실제 픽스처 (Fixtures)를 실제 API를 통해 실행하고 출력을 기록하는 capture 스크립트, 그리고 CI에서 실행되며 임계값 (Thresholds)과 비교하는 score 스크립트입니다. 비용이 많이 드는 부분은 제가 원할 때 실행하고, 비용이 적게 드는 부분은 모든 PR에서 실행됩니다. 올해 AI 도구를 하나 만든다면, 온도계부터 만드세요.

3. 실패 모드 (Failure modes)는 하나의 기능이다

봇은 몇 가지 흥미로운 방식으로 실패할 수 있습니다: 턴 제한 (Turn cap) 초과, 속도 제한 (Rate-limited), 잘못된 형식의 모델 출력 (Malformed model output), 또는 모델이 검색된 세트(Retrieved set)에 없는 규칙을 스스로 만들어내는 경우 등입니다. 초기에는 이 모든 것들이 로그에만 존재하고 다른 곳에는 나타나지 않았습니다. 8일 차의 목표는 이 실패들을 가시화하는 것이었습니다.

이제 대시보드에는 세 개의 작은 칩(chips)이 있습니다: 환각(hallucinated)된 결과물 삭제 건수, 주요 실패 모드(failure modes), 캐시된 도구 호출(cached tool calls)입니다. 이 칩들은 수치가 0일 때는 아무것도 렌더링하지 않습니다 (깨끗한 상태가 문제처럼 보여서는 안 되기 때문입니다). 수치가 올라가면, 운영자가 데이터베이스를 직접 열어보지 않아도 무엇이 잘못되었는지 알려줍니다.

이 칩들의 핵심 목적은 봇이 흥미로운 방식으로 실패할 수 있으며, 운영자는 그 실패가 어떤 종류의 '흥미로운' 방식인지 알아야 한다는 것입니다. 실패를 초록색 "모두 정상" 배지 뒤에 숨기는 것이 배포하기에는 더 빨랐겠지만, 영원히 더 나쁜 선택이 되었을 것입니다.

내가 하지 않은 것들

정직한 회고(retro)의 정신에 따라 작성하자면:

  • 학습 루프(learning loop)의 부재. 실제 PR(Pull Request)에서 얻은 결과물들이 봇에 다시 피드백되지 않습니다. 시드 규칙(seed rules)의 수가 충분히 적어서(~50개) 여전히 인간의 큐레이션(curation)이 더 효과적입니다. 실제 피드백 루프를 구축하는 것은 남은 스프린트(sprint) 기간을 모두 소모했을 것입니다.
  • 실제 환경에서의 재현율(recall)은 평범한 수준입니다. 합성 테스트 피스처(synthetic test fixtures)에서는 봇이 약 0.72의 F1 점수를 기록합니다. 하지만 실제 과거 PR의 스니펫(snippets)에서는 약 0.33으로 떨어집니다. 깔끔한 디프(diff)에서는 성능이 좋지만, 지저분한 디프에서는 성능이 떨어집니다. 현재는 500라인 크기 제한(size gate) 덕분에 지저분한 영역을 피하고 있습니다. 다음 단계로 적절한 방향은 임계값 조정(threshold gymnastics)이 아니라, 더 정교하게 큐레이션된 실제 PR 피스처를 만드는 것입니다.
  • 로컬 모델(local-model)로의 마이그레이션이 다음 주요 과제입니다. 제가 평가 하네스(eval harness)를 구축한 유일한 이유는 모델을 교체했을 때 단순히 추측하는 것이 아니라, 그 교체가 성공적이었는지 "알 수" 있기 위해서였습니다. 이제부터는 일일 스프린트의 속도를 늦추겠지만, 언제든 이 작업을 다시 시작할 수 있도록 기반(substrate)은 마련되어 있습니다.

내게 남은 것들

이전까지 제가 만든 모든 "AI 기능"들은 LLM(Large Language Model)이 곧 제품인 것처럼 만들었습니다. 하지만 이번에는 LLM을, 다른 부분들 또한 하중을 견뎌야 하는 시스템 내의 하나의 하중 지지 구성 요소(load-bearing component)처럼 만들었습니다. 감사 로그(audit log), 큐(queue), 평가 게이트(eval gate), 대시보드 칩 — 이 중 그 어느 것도 AI가 아닙니다. 이 모든 것들이 바로 AI 부분을 신뢰할 수 있게 만드는 요소들입니다.

저는 프롬프트 엔지니어링 (Prompt Engineering)에 대해 배우게 될 것이라고 생각하며 스프린트를 시작했습니다. 하지만 스프린트를 마칠 때쯤에는 큐 세맨틱스 (Queue Semantics)와 관측 가능성 표면 (Observability Surfaces)에 대해 생각하고 있었습니다. 결과적으로 그것이 훨씬 더 값진 배움이 되었습니다.

출처: github.com/azaz101hassan/ai-pr-review-copilot. 만약 여러분이 이와 유사한 형태의 무언가를 구축하고 있거나, 이런 봇이 여러분 팀의 컨벤션 (Conventions)에 도움이 될 것이라고 생각한다면, 여러분의 이야기를 듣고 싶습니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0