Gatekeeper: Azure DevOps를 위한 AI 기반 Pull Request 리뷰 데스크 구축하기
요약
Azure DevOps 환경에서 AI를 활용해 Pull Request(PR)의 품질과 거버넌스를 검증하는 'Gatekeeper' 구축 사례를 소개합니다. 단일 HTML 파일 구조를 통해 인프라 구축 없이도 티켓 매핑, 리뷰 여부, 코드 품질을 즉각적으로 확인할 수 있는 리뷰 데스크를 구현했습니다.
핵심 포인트
- Azure DevOps REST API를 활용한 결정론적 준수 체크
- LLM을 통한 코드 품질 및 에러 핸들링 검증
- 인프라와 데이터베이스가 없는 단일 HTML 파일 아키텍처
- 보안을 위해 자격 증명을 메모리에만 유지하는 설계
Gatekeeper: Azure DevOps를 위한 AI 기반 Pull Request 리뷰 데스크 구축하기
단 하나의 HTML 파일이 어떻게 우리의 엉망이었던 PR 위생 상태를 검증 가능하고 감사 가능한 리뷰 파이프라인으로 바꾸었는지에 대하여.
문제점: 아무도 추적할 수 없는 머지 (merges)
모든 엔지니어링 팀은 이와 유사한 경험이 있습니다. 버그가 프로덕션(production)에 배포됩니다. 누군가 질문합니다. "어떤 변경 사항이 이 문제를 일으켰나요?" 해당 Pull Request (PR)를 찾아보지만, 연결된 작업 항목(work item)도 없고, 설명(description)은 비어 있으며, 리뷰어의 승인(approvals)도 전혀 없습니다. 금요일 저녁, 서두르던 누군가에 의해 머지된 것이었습니다.
우리 팀은 규모가 작습니다. 프로세스를 강제할 릴리스 매니저(release manager) 군단이 없습니다. Azure DevOps는 브랜치 정책(branch policies)을 강제할 수 있지만, 정책은 다소 투박한 도구입니다. 차단하거나, 아니면 차단하지 않거나 둘 중 하나일 뿐입니다. 우리가 실제로 원했던 것은 그 중간 단계였습니다. 즉, 요청 시 언제든 어떤 PR이든 살펴보고 다음 세 가지 질문에 즉각 답할 수 있는 **리뷰 데스크 (review desk)**였습니다:
- 이 PR이 티켓(ticket)에 매핑되어 있는가? 코드가 작업 항목(work item)으로 추적될 수 없다면, 우리의 감사 추적(audit trail)은 깨진 것입니다.
- 실제로 사람이 리뷰했는가? 투표를 하지 않은 할당된 리뷰어는 의미가 없습니다.
- 코드 자체가 괜찮은가? 버그, 설정 파일 내의 비밀 정보(secrets), 누락된 에러 핸들링(error handling) — 지친 리뷰어가 대충 훑고 지나치기 쉬운 것들 말입니다.
앞의 두 가지는 결정론적인(deterministic) 답변이 가능한 거버넌스(governance) 문제입니다. 세 번째는 판단의 영역이며, 바로 이 지점이 LLM (Large Language Model)이 제 역할을 다하는 곳입니다.
모든 것을 결정지은 제약 조건: 단 하나의 HTML 파일
우리는 의도적으로 이것을 단일한, 독립적인 HTML 파일로 구축했습니다. 백엔드(backend)도, 배포 파이프라인(deployment pipeline)도, 데이터베이스(database)도, npm 빌드도 없습니다. 파일을 열고 네 가지 값 — 조직(organization), 프로젝트(project), 개인 액세스 토큰(PAT, personal access token), 그리고 PR ID — 을 붙여넣고 버튼 하나만 누르면 됩니다.
이러한 제약 조건 덕분에 우리는 세 가지를 얻을 수 있었습니다:
- 인프라 제로 (Zero infrastructure). 호스팅, 패치 또는 비용 지불이 필요 없습니다.
- 자격 증명 저장 제로 (Zero credential storage). 모든 비밀 정보(secret)는 페이지의 메모리에만 존재하며 새로고침 시 사라집니다. 아무것도 영구 저장되지 않으므로 유출될 데이터도 없습니다.
- 즉각적인 도입 (Instant adoption). "파일이 여기 있으니 여세요"는 지금까지 작성된 가장 짧은 온보딩 문서입니다.
아키텍처: 두 단계의 리뷰 레이어
흥미로운 설계 결정은 리뷰를 매우 다른 신뢰 모델을 가진 두 개의 레이어로 분리한 것입니다.
레이어 1 — 결정론적 준수 게이트 (Deterministic compliance gates)
사실(Facts)을 언어 모델(Language Model)에 외주 주어서는 안 됩니다. 작업 항목(work item)이 PR에 연결되어 있는지 여부는 의견의 문제가 아니라, API 응답의 필드 값입니다. 따라서 첫 번째 레이어는 Azure DevOps REST API를 대상으로 네 가지 체크를 수행하는 단순한 JavaScript입니다:
| 게이트 (Gate) | 확인 방법 | 판정 (Verdict) |
|---|---|---|
| 티켓 매핑 (Ticket mapping) | GET .../pullRequests/{id}/workitems — 결과가 비어 있다면, 정규 표현식(regex)을 사용하여 제목, 설명 및 브랜치 이름에서 티켓 형태의 참조(AB#123, PROJ-456)를 스캔합니다 | 매핑됨 (MAPPED) / 느슨한 참조 (LOOSE REF) / 매핑되지 않음 (NOT MAPPED) |
| ... |
각 게이트는 화면에 강렬하게 찍히는 고무 도장 판정 (rubber-stamp verdict) 카드로 렌더링됩니다. 이는 작은 극적 장치이지만 실제로 효과가 있습니다. 커다란 빨간색 NOT MAPPED 도장은 표의 행(row)이 줄 수 없는 방식으로 무시하기 어렵게 만듭니다. 프로세스 실패는 실패처럼 느껴져야 합니다.
"느슨한 참조 (loose ref)" 상태는 언급할 가치가 있습니다. 팀들은 브랜치 이름이 feature/PROJ-456-fix-login이기 때문에 티켓을 연결했다고 생각하는 경우가 많습니다. 그것은 관례(convention)일 뿐 연결(link)이 아닙. Azure DevOps에서 추적 가능성(traceability)을 쿼리할 수 있게 만드는 것은 작업 항목 간의 관계입니다. Gatekeeper는 이 둘을 구분합니다. 공식적인 링크는 통과되지만, 명명 규칙만 따른 참조는 제대로 연결하라는 노란색 경고를 받게 됩니다.
레이어 2 — AI 심층 리뷰 (The AI deep review)
사실 관계에 도장이 찍히고 나면, 판단 레이어가 작동합니다:
- 증거 수집 (Gather the evidence). 앱은 PR의 최신 반복(iteration)을 가져오고, 변경된 모든 파일을 나열하며, 소스 브랜치 팁(source branch tip)에서 최대 8개의 텍스트 파일 실제 내용을 다운로드합니다 (프롬프트의 안정성을 유지하기 위해 확장자로 필터링하고 파일당 개수를 제한함).
- 하나의 구조화된 프롬프트 구축 (Build one structured prompt). 제목, 설명, 브랜치, 연결된 티켓 목록(또는 명시적인 "NONE — 매핑된 티켓 없음"), 리뷰어 투표, 해결되지 않은 스레드 수, 전체 파일 목록, 그리고 원시 코드(raw code)를 포함합니다.
- 구조화된 출력 요구 (Demand structured output). 모델은 엄격한 시니어 리뷰어(senior reviewer)로서 행동하도록 지시받으며, 고정된 스키마(schema)에 맞는 JSON 형식으로만 응답해야 합니다: 평이한 언어로 된 요약, 전체 위험 수준 (low → critical), 권장 사항 (approve / request changes / block), 그리고 최대 8개의 발견 사항(findings)이 포함됩니다. 각 발견 사항에는 심각도(severity), 카테고리(category), 파일, 무엇이 잘못되었는지에 대한 설명, 그리고 구체적인 수정 제안이 포함됩니다.
- 거버넌스(governance)를 코드 스멜(code smell)로 취급. 프롬프트는 티켓 누락이나 승인(sign-off) 누락 자체가 그 자체로 심각도가 높은 발견 사항임을 모델에게 명시적으로 알려줍니다. 따라서 AI의 판결과 컴플라이언스(compliance) 도장이 서로를 보완합니다.
응답은 방어적으로 파싱되며(모델이 산문 속에 JSON을 포함할 경우 가장 바깥쪽 JSON 객체를 추출하는 폴백(fallback) 메커니즘 포함), 발견 사항은 심각도가 높은 순서대로 정렬되며, 전체 결과는 위험 다이얼(risk dial)과 확장 가능한 발견 카드(finding cards) 형태로 렌더링됩니다.
왜 레이어를 하나만 두지 않나요?
실패 모드(failure modes)가 서로 반대이기 때문입니다. 결정론적 체크(Deterministic checks)는 절대 환각(hallucinate)을 일으키지 않지만 코드를 읽을 수는 없습니다. LLM은 코드를 훌륭하게 읽지만 리뷰어 수를 세는 작업까지 신뢰해서는 안 됩니다. 레이어를 분리한다는 것은 AI 호출이 완전히 실패하더라도 컴플라이언스 판결은 항상 정확하다는 것을 의미하며, AI는 오직 자신만이 할 수 있는 작업에 모든 토큰 예산(token budget)을 집중할 수 있게 됩니다.
직접 AI를 가져오세요 (Bring your own AI)
첫 번째 버전은 모델 호출이 보이지 않게 인증되는 호스팅된 AI 환경 내부에서만 작동했습니다. 이는 누군가 당연한 질문을 던지기 전까지는 괜찮았습니다: "내 개인 컴퓨터에서도 실행할 수 있나요?"
그래서 사이드바에 세 가지 모드를 가진 AI 엔진 (AI engine) 선택기가 추가되었습니다:
- 내장형 (Built-in) — 키가 필요 없으며, 모델 호출을 프록시(proxy)하는 플랫폼 내부에서 페이지가 실행될 때 작동합니다.
- Anthropic API 키 (Anthropic API key) — 본인의 키를 붙여넣습니다. 페이지가 브라우저에서 Claude API를 직접 호출합니다 (Anthropic은 명시적인 opt-in 헤더를 통해 브라우저 직접 접근을 지원합니다). 이를 통해 파일은 완전히 독립적으로 작동할 수 있습니다.
- Azure OpenAI — 본인의 엔드포인트(endpoint), 배포 이름(deployment name) 및 키를 지정합니다. 한 가지 주의사항은, Azure OpenAI는 기본적으로 브라우저 호출을 허용하지 않으므로, 실제로는 CORS 정책이 적용된 API 게이트웨이를 통해 엔드포인트를 라우팅해야 합니다. 앱은 이 실패를 감지하고 사용자에게 정확히 해당 내용을 알려줍니다.
자격 증명(Credential) 처리 방식은 세 가지 모두 동일합니다. 페이지 메모리에만 유지되며, 브라우저에서 각 서비스로 직접 전송되고, 어떤 종류의 저장소에도 기록되지 않습니다.
우리가 배운 점
프로세스 실패를 명확하게 알리세요. 가장 효과적인 단일 기능은 AI가 아니라 바로 '스탬프'입니다. 거대한 빨간색 'NOT MAPPED' 표시에는 아무도 반박하지 못합니다.
정직함을 제한하지 말고, 증거를 제한하세요. 60개의 파일이 포함된 PR의 경우, AI는 샘플(8개 파일, 약 45K자)을 리뷰합니다. 우리는 이 한계를 숨기는 대신 표면화합니다. 모든 것을 다루는 척하는 리뷰 도구는 자신의 컨텍스트 윈도우(window)에 대해 정직한 도구보다 더 나쁩니다.
구조화된 출력 (Structured output)은 계약입니다. 모델이 엄격한 JSON 스키마 (JSON schema)를 따르도록 강제하고, 모델이 벗어날 경우 이를 복구하여 파싱(salvage-parsing)함으로써, "내 코드에 대해 채팅하기"를 예측 가능한 형태를 가진 반복 가능한 파이프라인 단계로 전환했습니다.
검증 순서가 중요합니다. 자격 증명은 API 호출이 발생하기 전에 확인되므로, 마지막 단계에서 키 누락 오류를 만나기 위해 전체 페치(fetch) 파이프라인을 낭비하는 일이 발생하지 않습니다.
솔직한 한계점
- AI는 변경된 파일의 **현재 버전 (current version)**을 확인하며, 라인 단위의 디프 (diff)를 보는 것이 아닙니다. 즉, 변경 사항(delta)만 보는 것이 아니라 코드가 현재 있는 상태 그대로를 리뷰합니다. (실제 디프 (diff) 지원은 로드맵의 최우선 순위 항목입니다.)
- 매우 큰 PR (Pull Request)은 전체를 읽지 않고 샘플링하여 처리합니다.
- 브라우저 기반 방식이므로 CORS (Cross-Origin Resource Sharing) 문제가 발생할 수 있습니다. 일부 기업 네트워크 정책에 따라 게이트웨이 (gateway)를 통한 라우팅이 필요할 수 있습니다.
향후 계획
자연스러운 진화 단계는 루프를 완성하는 것입니다. 즉, AI가 발견한 내용을 댓글 스레드 형태로 PR에 다시 게시하는 버튼, 모든 오픈된 PR을 한 번에 검토하는 배치 모드 (batch mode), 그리고 궁극적으로는 동일한 2단계 리뷰를 인테이크 자동화 (intake automation) 파이프라인에 연결하는 것입니다. 이를 통해 사람이 작업 항목 (work item)을 확인하는 시점에는 이미 해당 항목에 연결된 PR에 검토 결과가 찍혀 있게 됩니다.
Gatekeeper는 단일 HTML 파일로 구성됩니다: 하나의 입력 패널, 네 개의 컴플라이언스 (compliance) 스탬프, 그리고 하나의 AI 판결. 때로는 이메일에 바로 첨부할 수 있는 구조가 가장 최선의 아키텍처 (architecture)가 되기도 합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기