TECHARTNOMAD | MAZELINE.TECH

MAZELINE TOPIC

인터페이스 없이 급히 붙인 사내 코드 리뷰 자동화를 3개월 운영하며, TD에게 ‘서비스’로 전달하는 Go/React 시스템까지

jplee 2026. 5. 25. 00:59
이 글은 3개월 전부터 개발 해 왔던 고객사 코드리뷰 프로그램을 만들고 운영 해 오면서 왜 완전히 새롭게 만들게 되었는지에 대한 회고를 담고 있으며 코드 리뷰 자체에서 테크니컬 디렉터에게 서비스를 제공해야 한다는 구조로 피보팅 했던 이야기들을 담고 있습니다. 전체 글의 맥락은 직접 작성 하고 최종 문서는 Composer 2.5 를 사용하여 트리트먼트 되었습니다.

"리뷰는 돌아가는데, TD에게 전달되는 건 왜 이렇게 불편하지?"

약 3개월 전, 고객사 현장에 사내 코드 리뷰 자동화를 급히 붙여야 했습니다. SVN과 로컬 LLM, 스케줄러, 이메일까지 — “일단 매일 뭔가 나오게” 만드는 데는 성공했습니다. 그런데 그 시스템에는 화면이 없었고, 흐름도 코드 리뷰어(TD)가 매일 열어보는 결과물을 중심으로 설계되지 않았습니다.

그래서 실제로 diff를 보고 판단하는 TD 입장에서는 결과가 너무 적어서 놓치거나, 반대로 한꺼번에 너무 많아서 어디부터 볼지 모르는 날이 반복됐습니다. 실패한 Job인지, 어제 윈도에 무엇이 들어왔는지, 어떤 규칙으로 finding이 나왔는지 — 운영자·리뷰어가 상태를 읽을 UI가 없으니 매번 스크립트·로그·메일함을 뒤져야 했죠.

그걸 천천히 3개월 가까이 운영해 보니, 동기가 바뀌었습니다. “자동화가 되냐”가 아니라, 어떻게 만들고, 어떻게 이걸 ‘서비스’라는 개념으로 TD에게 제공할 수 있을까? — 그 질문에서 AI Codereviewer를 다시 설계하기 시작했습니다.

이전 글들도 같은 줄기입니다. SVN 코드 리뷰 자동화 리포트 시스템 개발기에서 하루 만든 Python 프로토타입으로 가능성을 확인했고, 설계 철학 글에서 모듈·파이프라인·데이터 구조를 정리했습니다. 이번 글은 그다음 단계 — TD가 매일 쓰는 운영 도구로 옮긴 기록입니다. SVN 커밋을 LM Studio로 리뷰하고, Gerrit 스타일 Web UI에서 Job을 추적하며, 익일 아침 SMTP 리포트까지 보내는 풀스택 AI Codereviewer를 단계별로 쌓아 올린 과정을 정리합니다.


처음부터 다시 만들기

급히 붙인 사내 버전의 한계는 기능 목록보다 전달 방식에 가깝습니다.

  • 인터페이스 없음 — 설정·재실행·실패 원인이 코드·env·로그에 묶여, TD가 “지금 무슨 일이 일어나는지”를 한 화면에서 볼 수 없음
  • 코드 리뷰어 드리븐이 아님 — 파이프라인 중심 출력이라, TD에게 필요한 만큼 정리된 결과가 아니라 부족하거나 과한 덩어리로 도착
  • 서비스 경계 없음 — 누가 언제 무엇을 받는지, Viewer와 Admin이 무엇을 보는지, 리포트 미리보기·재생성 같은 운영 시나리오가 정의되지 않음

3개월 가까이 돌려 보면서 드러난 건, LLM 품질만의 문제가 아니라는 점이었습니다. 리뷰어가 아침에 열 결과물밤에 돌아가는 Job이 같은 제품 안에서 이어져야 하고, Prompt·Skill·프로젝트 규칙을 한곳에서 바꿀 수 있어야 했습니다. “스크립트가 메일을 쏜다”에서 “TD가 신뢰하고 매일 여는 서비스”로 옮기는 게 목표였습니다.

그래서 스택을 Go (API + Worker) + React (Vite) + SQLite/PostgreSQL + Redis (Asynq) 로 잡고, Docker 없이도 로컬에서 dev-local.ps1로 바로 돌릴 수 있게 설계했습니다. LM Studio는 여전히 호스트에서 돌리되, OpenAI 호환 API로 붙입니다. 회사 코드를 외부로 보내지 않는다는 전제는 그대로 두고, 전달·가시성·역할만 프로덕트 수준으로 끌어올렸습니다.


1단계: 뼈대 — SVN 폴링과 AI 리뷰 Job

첫 커밋 묶음의 목표는 “매일 10:00~24:00 커밋을 자동으로 리뷰한다”는 약속을, TD가 상태를 추적할 수 있는 서비스로 옮기는 것이었습니다. 어제 Job이 왜 실패했는지 UI에서 보이지 않던 문제부터 막았습니다.

구성 역할
api Gin REST, WebSocket, Health
worker Asynq + 내장 스케줄러 (beat 불필요)
web React + Vite, Gerrit 톤 UI
db Job, Commit, Finding, Project
redis 큐 + WS pub/sub

Worker cron으로 30분마다 svn log / svn diff를 수집하고, diff를 청크로 나눠 LM Studio에 structured JSON 리뷰를 요청합니다. 실패 시 3회 retry, WebSocket으로 /ws/jobs에 진행률을 push합니다. “돌고는 있는데 보이지 않던” 구간을 Changes·Commits 화면으로 닫았습니다.

Post-commit Hook은 일부러 넣지 않았습니다. 데일리 윈도만 맞추면 되고, SVN 서버 쪽 설정 부담을 줄이려는 선택이었습니다.


2단계: LM Studio 설정 UI와 Prompt scope

초기 사내 버전처럼 LM Studio URL·모델이 .env에만 있으면, TD가 현장에서 바꿀 때마다 배포·스크립트 수정이 필요합니다. LM Studio 페이지를 만들고 설정을 DB에 저장하도록 바꿨습니다. env는 seed·폴백만 담당합니다.

모델 목록은 OpenAI /v1/models가 아니라 LM Studio 네이티브 /api/v1/models를 호출합니다. 설치된 전체 목록이 아니라 현재 Load된 LLM만 보여주는 게 운영상 맞더군요.

Prompt는 scope(global / project)당 active 1개. project active → global active → 내장 기본값 순으로 적용합니다. User 템플릿 변수 {revision}, {author}, {message}, {diff}로 diff 리뷰 형식을 TD가 직접 다듬을 수 있습니다. “너무 많거나 너무 애매한” 출력의 첫 번째 조절점입니다.


3단계: Reports — 운영 리포트와 Dev 모드

사내 시스템에서 TD가 매일 받던 아침 이메일을, 생성·미리보기·테스트까지 포함한 리포트 서비스로 옮겼습니다.

  • Generate Now — 어제 리뷰 윈도 분량 수동 생성 (Admin)
  • Preview — HTML 모달 미리보기 (보내기 전에 TD 관점에서 확인)
  • Development — 지정 시간대(window start/end) 테스트용 리포트, 기본 이메일 미발송

목록 API는 html_body를 빼고 메타만 내려주도록 경량화했습니다. Cron 00:30 UTC는 중복 batch가 있으면 Skip, Generate Now는 confirm 후 Replace — 운영·개발 시나리오를 나눴습니다.

Projects 쪽에는 SVN Test 배너를 추가했습니다. VPN·사내망 SVN은 API/Worker가 도는 PC에서 svn CLI로 접속해야 한다는 걸 UI에서 바로 확인할 수 있게 했습니다.


4단계: Prompt vs Skill 하이브리드

리뷰 품질과 TD에게 전달되는 밀도를 올리려면 "JSON 형식"과 "도메인 규칙"을 분리하는 게 맞다고 판단했습니다.

  Prompt Skill
역할 리뷰 JSON 형식, system/user 템플릿 C++/UE 도메인 규칙, 금지 API 등
활성화 scope당 1개 여러 개 동시
파일 매칭 없음 (전체 diff) trigger_rules.extensions

Prompt는 "어떻게 답할 것인가", Skill은 "무엇을 더 볼 것인가"입니다. Job Activity에 Prompt: …; Skills: …가 남아서 나중에 "이 finding은 어떤 규칙으로 나왔지?"를 추적할 수 있습니다. skills/ 디렉터리 seed와 Web UI CRUD, 감사 로그까지 연결했습니다. 리뷰어 드리븐으로 가려면 왜 이 알림이 나왔는지 설명 가능해야 했습니다.


5단계: Static Analysis — 커밋 리뷰 너머

SVN diff 리뷰만으로는 잡히지 않는 클래스가 있습니다. UBT -StaticAnalyzer로 Clang/MSVC 정적 분석을 돌리면, null dereference·dead store 같은 패턴을 빌드 파이프라인 관점에서 볼 수 있죠. diff 리뷰와 별도 채널로 TD에게 넘기기 위한 확장입니다.

여기서 중요한 설계 선택이 하나 있었습니다.

별도 정적분석 서버를 두지 않고, TD 로컬 PC에서 Worker가 RunUBT.bat를 실행한다.

Unreal 프로젝트 경로와 Engine 경로는 SVN Projects 저장소와 무관합니다. Web UI Static Analysis에서 로컬 프로필(engine_path, .uproject, target)을 등록하고, Windows에서는 Browse...로 네이티브 폴더·파일 선택 API를 씁니다. 최소 UE 5.5.4 (Engine/Build/Build.version 검증).

실행 흐름은 Review Job과 같습니다.

  1. Admin이 Run analysis → DB에 Run 생성, Asynq analysis:static enqueue
  2. Worker가 UBT 실행, 2초마다 raw log flush (UI 실시간 모니터)
  3. MSVC/Clang 출력 regex 파싱 → findings 저장
  4. COMPLETED 후 Generate AI summary — LM Studio가 findings를 한국어 Markdown(## 개요, ## 심각 이슈, ## 권장 조치)으로 요약
  5. Dashboard Recent Static Analysis 위젯에 최근 Run 표시

AI summary는 Markdown으로 생성하지만 UI는 pre-wrap 원문 표시입니다. 복사·붙여넣기와 파이프라인 단순화를 택한 trade-off입니다.


아키텍처 한 장 요약

localhost (로컬 개발)
├── api       :8080   REST + WebSocket
├── worker    Asynq   SVN poll · AI review · daily report · static analysis
├── web       :5173   Vite → API proxy
├── db        SQLite  (backend/codereviewer-dev.db)
└── redis     :6379   큐 + pub/sub

LM Studio는 :1234에서 호스트 실행. Docker compose는 PostgreSQL + nginx web(:3000) 경로도 지원합니다.

상단 nav: Dashboard → Changes → Commits → Reports → Prompts → Skills → Projects → Static Analysis → LM Studio.


지금 상태와 다음

현재 AI Codereviewer는 다음까지 갖춰진 상태입니다.

  • SVN 기반 데일리 AI 코드 리뷰 (Prompt + Skill)
  • Gerrit 스타일 Web UI, WebSocket Job 추적
  • LM Studio DB 설정, 모델 Detect
  • 데일리·Dev SMTP 리포트, Preview
  • UE UBT Static Analysis + AI 요약 + Dashboard 위젯
  • OneDev CI build spec, 로컬/Docker Quick Start README

앞으로 손대고 싶은 방향은 대략 이렇습니다.

  • Static Analysis AI summary 탭 Markdown 렌더링
  • Git 저장소 지원 (SVN-only 팀 외 확장)
  • Slack/Teams 알림 (이메일 보조)
  • 리뷰·finding 데이터를 로컬 모델 fine-tune 리소스로 축적 (설계 철학 글에서 언급한 장기 그림)

마무리

급히 붙인 사내 자동화는 “리뷰가 돈다”는 걸 보여 줬지만, TD에게는 서비스로 전달되지 않았습니다. 3개월 가까이 운영하며 얻은 답은, UI·역할·리포트·규칙 관리가 한 제품 안에 있어야 한다는 것이었고, 그게 AI Codereviewer입니다.

Python 프로토타입과 설계 철학 글은 가능성과 구조를 잡았고, 이번 시스템은 매일 여는 도구로 옮긴 결과입니다. 완벽한 Gerrit·SonarQube를 대체하려는 게 아니라, 데브옵스 팀 없이도 TD가 버틸 수 있는 1차 필터를 목표로 했습니다.

SVN diff 리뷰, 데일리 리포트, UE static analysis까지 한 UI에서 이어지게 만든 것도 그 맥락입니다. "오늘 뭐가 바뀌었고, 빌드 전에 뭘 더 봐야 하지?" — 리뷰어가 아침에 그 질문에 답받도록 계속 다듬어 갈 예정입니다.