프롤로그 — 선(line)이 아니라 고리(loop)
지난 글에서는 AI 개발 자동화의 부품을 다뤘다 — 어떤 에이전트가 있고, GitHub과 어떻게 연동되고, 티켓을 어떻게 맡기는지.
이번 글은 그 부품들을 하나의 파이프라인으로 조립한다. 목표는 이것이다:
사람이 Issue를 하나 올린다. 자리를 비운다. 잠시 후 검증된 배포가 프로덕션에 올라가 있다. 사람이 개입한 건 단 하나 — 머지 버튼을 누른 결정뿐이다.
그리고 더 중요한 것: 이건 선이 아니라 고리다. 배포가 끝이 아니다. 모니터링이 이상을 감지하면 자동 롤백하고, 그 사건이 새 Issue가 되어 파이프라인의 입구로 다시 들어온다. 시스템이 스스로를 고친다.
이 글은 그 고리를 7개 스테이지로 쪼개고, 각 스테이지의 게이트를 설계하고, GitHub Actions로 전부 구현한다. 핵심 원칙은 하나다:
"작업은 자동화하고, 결정은 게이트한다 (Automate the work, gate the decisions)."
AI가 코드를 짜고 리뷰하고 배포하게 두되, 되돌릴 수 없는 결정(프로덕션 머지, 스키마 변경, 보안 로직)에는 반드시 사람 또는 강한 자동 검증을 세운다.
0장 · 전체 파이프라인 조감도
먼저 큰 그림. 7개 스테이지와 각 스테이지를 누가 소유하는지.
┌────────────────────────────────────────────────────────────────┐
│ │
│ [S1] Issue ──ai-ready 라벨──▶ AI 에이전트 ──▶ Draft PR │
│ ▲ │ │
│ │ ▼ │
│ │ [S2] AI 코드 리뷰 │
│ │ (생성≠리뷰) │
│ │ │ │
│ │ ▼ │
│ │ [S3] CI 검증 게이트 │
│ │ lint·type·test·build·E2E │
│ │ │ │
│ │ ┌── 실패 ──────┤ │
│ │ ▼ ▼ 통과 │
│ │ AI 자가 수정 [S4] Human Gate │
│ │ (로그 읽고 재푸시) 머지 결정 │
│ │ │ │
│ │ ▼ │
│ │ [S5] CD: Preview │
│ │ → Canary → Production │
│ │ │ │
│ │ ▼ │
│ │ [S6] 자동 검증 │
│ │ smoke·E2E·SLO 게이트 │
│ │ │ │
│ │ ┌── 실패 ──────┤ │
│ │ ▼ ▼ 통과 │
│ └──── 자동 Issue 생성 ◀─ [S7] 모니터링 & 피드백 │
│ (자동 롤백 + 사건화) 배포 후 관측, 롤백 트리거 │
│ │
└────────────────────────────────────────────────────────────────┘
| 스테이지 | 이름 | 소유 | 출력 |
|---|---|---|---|
| S1 | Issue → PR | AI 에이전트 | Draft PR |
| S2 | AI 코드 리뷰 | AI 리뷰어 (생성과 다른 모델) | 인라인 코멘트 + APPROVE/REQUEST_CHANGES |
| S3 | CI 검증 게이트 | CI 시스템 | 초록불/빨간불 |
| S4 | Human Gate | 사람 | 머지 결정 |
| S5 | CD 배포 | CD 시스템 | Preview → Canary → Production |
| S6 | 자동 검증 | 검증 시스템 | 승격 또는 롤백 |
| S7 | 모니터링 & 피드백 | 관측 시스템 | 정상 또는 자동 Issue |
이 글의 나머지는 각 스테이지를 하나씩 깊게 판다.
1장 · 파이프라인 설계 5원칙
구현 전에 원칙을 못 박는다. 이게 흔들리면 파이프라인이 위험해진다.
원칙 1 — 모든 스테이지는 fail-closed
스테이지가 실패하거나 판단이 불확실하면 멈춘다. 통과시키지 않는다. AI 리뷰가 헷갈리면 → REQUEST_CHANGES. CI가 flaky하면 → 빨간불. 검증이 모호하면 → 롤백. 의심스러우면 진행하지 않는다.
원칙 2 — 모든 스테이지는 되돌릴 수 있어야 한다
PR은 close, 머지는 revert, 배포는 rollback. 각 스테이지에 "취소 버튼"이 없으면 그 스테이지는 자동화하면 안 된다.
원칙 3 — 모든 스테이지는 관측 가능해야 한다
누가·언제·무엇을·왜 했는지 로그가 남는다. AI가 한 일은 특히. 디버깅 불가능한 자동화는 시한폭탄이다.
원칙 4 — 멱등성 (Idempotency)
같은 Issue가 두 번 트리거돼도 PR이 두 개 생기면 안 된다. 같은 커밋이 두 번 배포돼도 안전해야 한다. 재시도가 안전해야 자동화가 안전하다.
원칙 5 — 신뢰 사다리 (Trust Ladder)
처음부터 다 자동화하지 않는다. 저위험·고빈도 작업부터 자동화하고, 성공률 데이터가 쌓이면 자동화 범위를 넓힌다. 문서 수정 자동 머지 → 의존성 범프 자동 머지 → … 한 칸씩.
2장 · S1 — Issue → PR (생성 스테이지)
지난 글에서 다룬 부분이라 핵심만. ai-ready 라벨이 트리거, AI 에이전트가 브랜치를 만들고 구현하고 Draft PR을 연다.
이 스테이지의 진짜 목표는 "코드를 짜는 것"이 아니라 **"리뷰 가능한 PR을 낳는 것"**이다. 뒤의 모든 스테이지가 PR 품질에 의존한다.
PR이 "리뷰 가능하게 태어나는" 조건
- Draft 상태로 생성 — 사람이 "Ready for review"로 승격하기 전엔 머지 불가.
- Issue 링크 — PR 본문에
Closes #123. 머지 시 Issue 자동 close. - 작업 요약 — 에이전트가 "무엇을·왜" 했는지 본문에 남긴다. 리뷰어의 출발점.
- 일관된 PR 제목 —
[AI] fix: ...처럼. 뒤 스테이지가 제목으로 분기한다. ai-generated라벨 — 추적용.
멱등성 — 중복 PR 방지
# 같은 이슈에 PR이 이미 있으면 새로 안 만든다
- name: Check existing PR
id: check
run: |
EXISTING=$(gh pr list --search "in:title #${{ github.event.issue.number }}" --json number --jq 'length')
echo "exists=$EXISTING" >> "$GITHUB_OUTPUT"
- name: Run agent
if: steps.check.outputs.exists == '0'
uses: anthropics/claude-code-action@v1
# ...
3장 · S2 — AI 코드 리뷰 자동화 (핵심 스테이지)
여기가 이 파이프라인의 심장이다. 사람 리뷰 전에 AI 리뷰를 한 겹 끼운다.
왜 사람 리뷰 전에 AI 리뷰인가
- 노이즈 필터 — 명백한 실수(누락된 에러 처리, 타입 불일치, 컨벤션 위반)를 사람이 보기 전에 잡는다.
- 사람 리뷰 시간 절약 — 사람은 "이게 맞는 접근인가" 같은 판단에 집중. 잔손질은 AI가.
- 24/7 — PR이 새벽에 올라와도 즉시 리뷰.
철칙: 생성 ≠ 리뷰 (다른 모델, 다른 에이전트)
PR을 만든 에이전트가 자기 PR을 리뷰하게 두지 마라. 자기 코드의 사각지대는 자기가 못 본다. 가능하면 다른 모델로 리뷰한다. Claude가 짰으면 다른 도구가 리뷰하거나, 최소한 별도 세션·별도 프롬프트로.
생성 에이전트 (Claude/Copilot) ──▶ PR
│
리뷰 에이전트 (다른 모델/도구) ──▶ 리뷰 코멘트
│
사람 ──────────────────────────▶ 최종 결정
도구 지형도
| 도구 | 형태 | 특징 |
|---|---|---|
| CodeRabbit | GitHub App | PR마다 자동 리뷰, .coderabbit.yaml 설정, 요약+인라인 |
| Greptile | GitHub App | 코드베이스 전체 컨텍스트 기반 리뷰 |
| GitHub Copilot 리뷰 | 내장 | Copilot을 PR 리뷰어로 지정 가능 |
| Claude Code Action | Actions | @claude 또는 워크플로로 리뷰, 커스터마이즈 강함 |
AI 리뷰가 잘 잡는 것 / 못 잡는 것
| 잘 잡음 | 못 잡음 |
|---|---|
| 누락된 null·에러 처리 | "이게 올바른 아키텍처인가" |
| 컨벤션·네이밍 위반 | 비즈니스 요구사항 충족 여부 |
| 명백한 보안 패턴 (SQLi, 하드코딩 시크릿) | 도메인 특화 미묘한 버그 |
| 테스트 커버리지 공백 | 성능의 미묘한 트레이드오프 |
| 흔한 버그 패턴 | 팀의 암묵적 맥락 |
→ 그래서 사람 게이트(S4)가 필요하다. AI 리뷰는 사람을 대체하지 않고 사람의 부담을 줄인다.
구현 — Claude Code Action으로 리뷰
name: AI Review
on:
pull_request:
types: [opened, synchronize, ready_for_review]
jobs:
ai-review:
# draft가 아닐 때만 — 작업 중인 PR은 리뷰 안 함
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
이 PR을 리뷰하라. 다음에 집중:
- 정확성: 로직 버그, 엣지 케이스, 누락된 에러 처리
- 보안: 입력 검증, 시크릿, 권한
- 테스트: 변경에 대한 커버리지가 충분한가
- 컨벤션: CLAUDE.md를 따르는가
문제는 해당 라인에 인라인 코멘트로 남겨라.
마지막에 요약과 함께 APPROVE 또는 REQUEST_CHANGES 를 명시하라.
사소한 스타일은 nit: 으로 표시해 사람이 무시할 수 있게 하라.
AI 리뷰를 "필수 체크"로 만들기
리뷰가 코멘트만 남기면 무시당한다. 상태 체크(status check)로 만들어 브랜치 보호 규칙에 넣으면, REQUEST_CHANGES일 때 머지 자체가 막힌다. 리뷰 액션이 GitHub Checks API로 pass/fail을 보고하도록 구성한다.
4장 · S3 — CI 검증 게이트
AI가 짠 코드를 기계가 검증하는 단계. CI가 약하면 이 파이프라인 전체가 약하다.
계층화된 체크
빠름 ──────────────────────────────────▶ 느림
lint → typecheck → unit test → build → integration → E2E
│ │ │ │ │ │
초 단위 초 단위 초~분 분 분 분~십분
빠른 체크를 앞에 둬서 빨리 실패하게 한다. lint에서 깨질 걸 E2E까지 가서 알 필요 없다.
CI를 "AI가 읽을 수 있게" 만들기
이게 핵심인데 자주 놓친다. CI 실패 메시지가 친절하면 AI가 스스로 고친다.
Error: test failed(나쁨) →Error: expected status 200, got 401 at auth.test.ts:42(좋음)- 실패한 명령, 파일·라인, 기대값·실제값을 출력하라.
- AI 에이전트는 이 로그를 읽고 다음 커밋에서 자가 수정한다.
자가 수정 루프
PR 푸시 ──▶ CI 실행 ──▶ 실패
│
▼
에이전트가 CI 로그를 읽음
│
▼
원인 파악 → 수정 커밋 푸시
│
▼
CI 재실행 ──▶ 통과
이 루프에 상한을 둔다 — 3회 시도 후에도 빨간불이면 사람을 호출한다. 무한 자가 수정은 비용 폭탄이다.
# CI 실패 시 에이전트에 로그를 주고 수정 시도 — 단, 라벨로 횟수 제한
- name: Self-heal on CI failure
if: failure() && !contains(github.event.pull_request.labels.*.name, 'ai-heal-exhausted')
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
CI가 실패했다. 아래 로그를 읽고 원인을 고쳐 커밋하라.
추측하지 말고 로그가 가리키는 것만 고쳐라.
브랜치 보호 — 게이트를 강제
Settings → Branches → main 보호 규칙:
✅ Require status checks: ci/lint, ci/test, ci/build, ai-review
✅ Require branches to be up to date before merging
✅ Require a pull request before merging
✅ Require approvals: 1+ (S4 사람 게이트)
✅ Dismiss stale approvals when new commits pushed
✅ Do not allow bypassing the above
5장 · S4 — Human Gate (사람이 지키는 관문)
자동화의 핵심은 역설적으로 "사람이 어디를 지키는가"를 명확히 하는 것이다.
사람이 반드시 결정해야 하는 것
- 머지 결정 그 자체 — "이걸 main에 넣는다"는 책임은 사람이 진다.
- 아키텍처 방향 — "이 접근이 맞는가"는 AI가 판단 못 한다.
- 비즈니스 요구사항 충족 — Issue의 의도를 진짜 만족했는가.
- 보안·데이터 민감 변경 — 실수 비용이 큰 영역.
사람 리뷰를 빠르게 만드는 것들
S4가 병목이 되지 않으려면, 사람이 PR을 받았을 때 이미:
- ✅ AI 리뷰가 끝나 있다 (잔손질 다 처리됨)
- ✅ CI가 초록불이다 (기계 검증 통과)
- ✅ PR이 작다 (리뷰 가능한 크기)
- ✅ 설명이 좋다 (무엇을·왜)
- ✅ Preview 배포 URL이 붙어 있다 (실제로 눌러볼 수 있음)
그러면 사람 리뷰는 "판단"만 하면 된다. 5분이면 끝난다.
자동 머지 — 언제 OK인가
GitHub의 auto-merge는 "모든 필수 체크 통과 + 필수 승인 완료 시 자동 머지"다. 위험하지만, 특정 변경 유형에는 안전하다.
| 자동 머지 OK | 자동 머지 금지 |
|---|---|
| 문서·주석 수정 | 애플리케이션 로직 |
| 의존성 패치 범프 (CI 통과 시) | 스키마 마이그레이션 |
| 린트·포맷 자동 수정 | 보안·인증 로직 |
| 생성된 타입·SDK 갱신 | 인프라·IaC |
# 의존성 PR이고 CI 통과 + AI 리뷰 APPROVE면 자동 머지 활성화
- name: Enable auto-merge for safe deps
if: |
contains(github.event.pull_request.labels.*.name, 'dependencies') &&
github.event.pull_request.user.login == 'dependabot[bot]'
run: gh pr merge --auto --squash "${{ github.event.pull_request.number }}"
에스컬레이션 경로
AI(생성이든 리뷰든)가 불확실하면 사람을 태그한다. "이 변경은 인증 로직을 건드립니다 — 보안 담당자 리뷰 필요" 같은 코멘트를 자동으로 남기고 needs-human 라벨을 붙인다.
6장 · S5 — CD: Preview → Canary → Production
머지됐다. 이제 배포. 핵심은 한 번에 다 배포하지 않는 것이다.
Preview 배포 — PR마다
PR이 열리면 격리된 실제 환경에 배포해 고유 URL을 준다. Vercel·Netlify·Cloudflare는 기본 제공, 자체 인프라는 PR별 네임스페이스로 구현.
→ S4의 사람 리뷰어가 코드가 아니라 동작하는 결과물을 본다. AI 리뷰어도 Preview URL에 E2E를 돌릴 수 있다.
머지 후 — 단계적 전달 (Progressive Delivery)
main 머지
│
▼
Canary 배포 (트래픽 5%)
│
▼ [S6 자동 검증]
smoke test + SLO 관측 (5~15분)
│
├── 실패 ──▶ 자동 롤백 (Canary 제거)
│
▼ 통과
Production 승격 (트래픽 100%)
- Canary — 새 버전에 트래픽 일부(5~10%)만. 문제가 나도 영향 범위가 작다.
- Feature Flag — 코드는 배포하되 기능은 꺼둔 채. 켜는 건 별도 결정.
- 환경 승격 — dev → staging → prod. 각 단계가 게이트.
GitHub Environments로 게이트 구현
name: Deploy
on:
pull_request:
types: [closed]
branches: [main]
jobs:
deploy-canary:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
environment: canary # 보호 규칙: 없음 (자동)
steps:
- uses: actions/checkout@v4
- run: ./scripts/deploy.sh canary
promote-production:
needs: [deploy-canary, verify-canary]
runs-on: ubuntu-latest
environment: production # 보호 규칙: required reviewers = 사람 게이트
steps:
- run: ./scripts/deploy.sh production
environment: production에 required reviewers를 걸면, 프로덕션 승격에 사람 승인이 강제된다 — 워크플로 안에 박힌 또 하나의 Human Gate.
7장 · S6 — 자동 검증 (Auto-Verification)
배포했다고 끝이 아니다. 배포된 것이 실제로 동작하는지 기계가 확인한다.
배포 후 검증 계층
| 검증 | 무엇을 | 언제 |
|---|---|---|
| Smoke test | 핵심 경로 몇 개 (로그인, 헬스체크) | 배포 직후 즉시 |
| E2E | 주요 사용자 시나리오 | Canary 단계 |
| Synthetic monitoring | 외부에서 주기적 핑 (Checkly, Datadog) | 지속 |
| SLO 관측 | 에러율·레이턴시·가용성 | Canary 윈도우 동안 |
| Visual regression | UI 픽셀 차이 | Preview/Canary |
검증 게이트 — Canary를 승격할까 롤백할까
verify-canary:
needs: deploy-canary
runs-on: ubuntu-latest
outputs:
verdict: ${{ steps.gate.outputs.verdict }}
steps:
- name: Smoke test
run: ./scripts/smoke-test.sh https://canary.example.com
- name: Observe SLO for 10 minutes
id: gate
run: |
sleep 600
ERROR_RATE=$(curl -s "$METRICS_API/error-rate?env=canary&window=10m")
P99=$(curl -s "$METRICS_API/latency-p99?env=canary&window=10m")
# 에러율 1% 초과 또는 p99 500ms 초과면 fail
if (( $(echo "$ERROR_RATE > 0.01" | bc -l) )); then
echo "verdict=rollback" >> "$GITHUB_OUTPUT"; exit 1
fi
echo "verdict=promote" >> "$GITHUB_OUTPUT"
rollback-canary:
needs: verify-canary
if: failure()
runs-on: ubuntu-latest
steps:
- run: ./scripts/rollback.sh canary
검증이 실패하면 → Canary 자동 제거. 프로덕션 트래픽은 애초에 5%만 영향받았고, 그것도 즉시 회수된다.
8장 · S7 — 모니터링 & 피드백 루프 (고리를 닫는다)
여기서 파이프라인이 선에서 고리가 된다. 배포 후에도 시스템은 계속 지켜본다.
자동 롤백 트리거
프로덕션 승격 후에도 관측은 계속된다. SLO를 깨면 자동 롤백.
- 에러 예산 소진 (error budget burn rate) — 빠르게 타들어가면 롤백.
- 레이턴시 급등 — p99가 임계 초과.
- 핵심 비즈니스 메트릭 하락 — 결제 성공률, 가입 전환율 등.
자가 치유 루프 — 사건이 다시 Issue가 된다
이게 고리의 핵심이다. 자동 롤백이 일어나면, 그 사건을 새 Issue로 만들어 파이프라인 입구(S1)로 돌려보낸다.
name: Monitor & Auto-Rollback
on:
schedule:
- cron: '*/5 * * * *' # 5분마다 SLO 점검
workflow_run:
workflows: ["Deploy"]
types: [completed]
jobs:
slo-check:
runs-on: ubuntu-latest
steps:
- name: Check production SLO
id: slo
run: |
BURN=$(curl -s "$METRICS_API/error-budget-burn?env=prod&window=1h")
echo "burn=$BURN" >> "$GITHUB_OUTPUT"
- name: Rollback + file issue if breaching
if: ${{ steps.slo.outputs.burn > 2.0 }}
run: |
./scripts/rollback.sh production
gh issue create \
--title "Auto-rollback: error budget burn rate ${{ steps.slo.outputs.burn }}" \
--label "ai-ready,incident,priority-high" \
--body "프로덕션 SLO 위반으로 자동 롤백했다.
마지막 배포: ${{ github.sha }}
burn rate: ${{ steps.slo.outputs.burn }}
에이전트는 롤백된 커밋의 diff를 분석하고 원인을 진단해
수정 PR을 제출하라."
이 Issue에 ai-ready 라벨이 붙어 있으므로 → S1이 다시 트리거된다. AI가 롤백된 커밋을 분석하고, 수정 PR을 만들고, AI 리뷰 → CI → 사람 게이트 → 재배포. 고리가 닫혔다.
닫힌 고리의 의미
Issue ──▶ PR ──▶ 리뷰 ──▶ CI ──▶ 머지 ──▶ 배포 ──▶ 검증 ──▶ 모니터
▲ │
└──────────────── 사건이 새 Issue로 ◀──────────────────────────┘
시스템이 자기가 낸 문제를 스스로 입력으로 받아 고친다. 사람은 여전히 머지 게이트를 지키지만, 탐지·진단·수정 착수·재배포의 반복 노동에서 해방된다.
9장 · 전체 구현 — GitHub Actions로 엮기
지금까지의 스테이지를 실제 파일로. 5개 워크플로가 이벤트로 연결된다.
워크플로 지도
| 파일 | 트리거 | 역할 |
|---|---|---|
ai-resolve.yml | issues: labeled | S1: Issue → PR |
ai-review.yml | pull_request: opened/synchronize | S2: AI 리뷰 |
ci.yml | pull_request: opened/synchronize | S3: 검증 게이트 |
deploy.yml | pull_request: closed (merged) | S5: Canary → Production |
monitor.yml | schedule + workflow_run | S6/S7: 검증·롤백·피드백 |
이벤트로 어떻게 연결되나
GitHub Actions는 중앙 오케스트레이터가 없다. 이벤트가 곧 연결선이다.
issue.labeled('ai-ready') ──▶ ai-resolve.yml ──▶ (PR 생성)
│
pull_request.opened ◀──────────────────────────────────┘
├──▶ ai-review.yml (AI 리뷰 → 체크 보고)
└──▶ ci.yml (검증 → 체크 보고)
│
[브랜치 보호: 모든 체크 + 사람 승인 대기] │
▼
pull_request.closed(merged) ──▶ deploy.yml ──▶ (Canary → 검증 → Production)
│
workflow_run('Deploy' completed) ──▶ monitor.yml │
schedule('*/5') ──▶ monitor.yml ◀─────┘
└──▶ SLO 위반 시: rollback + issue.create('ai-ready')
│
└──▶ (다시 ai-resolve.yml — 고리 완성)
ai-resolve.yml — S1
name: AI Resolve
on:
issues:
types: [labeled]
jobs:
resolve:
if: github.event.label.name == 'ai-ready'
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Skip if PR already exists
id: dedup
run: |
N=$(gh pr list --search "#${{ github.event.issue.number }} in:body" --json number --jq 'length')
echo "exists=$N" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Run agent
if: steps.dedup.outputs.exists == '0'
uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
Issue #${{ github.event.issue.number }}: ${{ github.event.issue.title }}
${{ github.event.issue.body }}
이 이슈를 구현하라. 브랜치를 만들고, 변경하고, 테스트를 통과시키고,
"Closes #${{ github.event.issue.number }}" 를 포함한 Draft PR을 열어라.
CLAUDE.md 컨벤션을 따르고, PR 본문에 무엇을·왜 했는지 요약하라.
ci.yml — S3
name: CI
on:
pull_request:
types: [opened, synchronize, ready_for_review]
concurrency:
group: ci-${{ github.event.pull_request.number }}
cancel-in-progress: true # 새 푸시 시 이전 실행 취소 — 비용 절감
jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm lint # 빠른 것 먼저
- run: pnpm typecheck
- run: pnpm test
- run: pnpm build
deploy.yml — S5 (6장 참조, 핵심만)
name: Deploy
on:
pull_request:
types: [closed]
branches: [main]
jobs:
canary:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
environment: canary
steps:
- uses: actions/checkout@v4
- run: ./scripts/deploy.sh canary
verify:
needs: canary
runs-on: ubuntu-latest
steps:
- run: ./scripts/smoke-test.sh https://canary.example.com
- run: ./scripts/observe-slo.sh canary 600 # 10분 관측
production:
needs: verify
runs-on: ubuntu-latest
environment: production # required reviewers = Human Gate
steps:
- uses: actions/checkout@v4
- run: ./scripts/deploy.sh production
rollback:
needs: [canary, verify]
if: failure()
runs-on: ubuntu-latest
steps:
- run: ./scripts/rollback.sh canary
리포 설정 체크리스트
- Secrets:
ANTHROPIC_API_KEY, 배포 자격증명. - Environments:
canary(자동),production(required reviewers 지정). - Branch protection (main): 필수 체크에
ci/verify,ai-review포함. 필수 승인 1+. - Labels:
ai-ready,ai-generated,needs-human,incident,dependencies.
10장 · 결정 게이트 매트릭스 — 무엇을 자동화하고 무엇을 사람이
파이프라인의 진짜 설계는 "어디까지 자동화하는가"다. 변경 유형별로 게이트를 다르게 건다.
| 변경 유형 | AI 생성 | AI 리뷰 | CI | 사람 게이트 | 자동 머지 | 배포 |
|---|---|---|---|---|---|---|
| 문서·주석 | ✅ | ✅ | ✅ | 선택 | ✅ 가능 | 자동 |
| 의존성 패치 범프 | ✅ | ✅ | ✅ 필수 | 선택 | ✅ 조건부 | Canary 자동 |
| 버그 픽스 (좁은 범위) | ✅ | ✅ | ✅ | ✅ 1명 | ❌ | Canary→자동 승격 |
| 기능 추가 | ✅ | ✅ | ✅ | ✅ 1명+ | ❌ | Canary→사람 승격 |
| 리팩터링 | ✅ | ✅ | ✅ + 커버리지 | ✅ 1명 | ❌ | Canary→자동 승격 |
| DB 마이그레이션 | ⚠️ AI 보조 | ✅ | ✅ | ✅ 필수 | ❌ | 사람 트리거 |
| 보안·인증 로직 | ⚠️ AI 보조 | ✅ | ✅ | ✅ 2명 | ❌ | 사람 트리거 |
| 인프라·IaC | ⚠️ AI 보조 | ✅ | ✅ plan diff | ✅ 필수 | ❌ | 사람 트리거 |
범례: ✅ 자동 / ⚠️ AI는 보조만, 사람이 주도 / ❌ 안 함
신뢰 사다리를 오르는 법
처음엔 모든 칸을 보수적으로 시작한다 — 자동 머지 다 끄고, 배포는 다 사람 트리거. 그리고 데이터로 한 칸씩 올린다:
- 문서 PR 자동 머지를 한 달 켜본다 → 사고 0건 → 유지.
- 의존성 범프 자동 머지를 켠다 → CI 통과율·롤백률 관찰 → 안정적이면 유지.
- 버그 픽스의 Canary 자동 승격을 켠다 → escape rate(놓친 버그) 관찰.
- …
성공률 데이터 없이 자동화 범위를 넓히지 마라. 신뢰는 적립되는 것이지 선언하는 게 아니다.
11장 · 안전장치 & 실패 모드
자동화가 빠를수록 사고도 빠르다. 각 실패 모드에 방어를 건다.
"AI가 AI를 승인" 문제
가장 위험한 안티패턴. 생성 에이전트가 자기 PR을 리뷰·승인하면 검증이 0이다. 방어:
- 생성과 리뷰는 다른 도구/모델.
- AI 리뷰의 APPROVE는 사람 승인을 대체하지 못한다 — 브랜치 보호의 "필수 승인"은 반드시 사람 계정.
- 봇 계정의 승인은 필수 승인 수에서 제외 설정.
파이프라인을 통한 Prompt Injection
Issue 본문, PR 코멘트, 코드, CI 로그 — 전부 에이전트의 입력이다. 공격자가 거기 명령을 심을 수 있다.
- 외부 사용자의 Issue/PR은 자동 트리거 금지 — 멤버가 단
ai-ready라벨만 트리거. - 에이전트 토큰 최소 권한 — 프로덕션·시크릿 접근 불가.
- 워크플로 파일 변경 감지 — PR이
.github/workflows/를 건드리면 무조건 사람 필수 리뷰.
비용 폭주
- 스텝 상한, 자가 수정 횟수 상한(3회).
concurrency+cancel-in-progress로 중복 실행 제거.- 동시 실행 에이전트 수 제한.
- 일/주 단위 비용 알람.
모든 스테이지는 fail-closed + 롤백
| 스테이지 | 실패 시 | 롤백 수단 |
|---|---|---|
| S1 생성 | PR 안 만듦, 이슈에 코멘트 | — |
| S2 리뷰 | REQUEST_CHANGES, 머지 차단 | — |
| S3 CI | 빨간불, 머지 차단 | — |
| S4 사람 | 머지 안 함 | — |
| S5 배포 | Canary 단계에서 중단 | rollback.sh canary |
| S6 검증 | 승격 안 함 | Canary 제거 |
| S7 모니터 | 자동 롤백 + 이슈화 | rollback.sh production |
감사 — 누가·언제·무엇을
모든 스테이지의 행위가 로그로 남아야 한다. AI가 한 일은 커밋 트레일러(Co-Authored-By:), PR 라벨(ai-generated), 워크플로 실행 기록으로 추적 가능해야 한다. "왜 이게 배포됐지"에 답할 수 없으면 그 자동화는 끄는 게 낫다.
12장 · 운영 — 메트릭과 점진적 신뢰
파이프라인을 켰다고 끝이 아니다. 측정하고 튜닝한다.
추적할 메트릭
| 메트릭 | 의미 | 건강한 방향 |
|---|---|---|
| Lead time (Issue→배포) | 한 바퀴 도는 시간 | ↓ |
| % auto-merged | 사람 없이 머지된 비율 | ↑ (단, escape rate 보면서) |
| AI 리뷰 정밀도 | AI 리뷰 지적 중 실제 유효 비율 | ↑ |
| Escape rate | 파이프라인을 통과한 버그 비율 | ↓ (가장 중요) |
| Rollback rate | 배포 중 롤백된 비율 | 낮고 안정적 |
| 자가 수정 성공률 | CI 실패 후 AI가 고친 비율 | ↑ |
| 사람 리뷰 대기 시간 | S4 큐 대기 | ↓ |
DORA 메트릭을 이 파이프라인에
전통적 DORA 4지표가 그대로 적용된다 — Deployment Frequency, Lead Time, Change Failure Rate, MTTR. AI 파이프라인의 목표는 Throughput을 올리되 Stability를 깨지 않는 것이다. Change Failure Rate가 오르면 자동화 범위를 줄여라.
병목은 거의 항상 S4
에이전트 10개가 PR 10개를 5분에 만들어도, 사람 리뷰가 하루 5개면 처리량은 하루 5개다. 생성 속도가 아니라 리뷰 용량이 진짜 상한이다. 그래서 튜닝의 초점은:
- AI 리뷰(S2)를 강화해 사람 리뷰 부담을 줄인다.
- PR을 더 작게 쪼개 리뷰를 빠르게 한다.
- 저위험 변경의 자동 머지를 (데이터를 보며) 늘려 S4를 우회시킨다.
점진적 신뢰 — 데이터로 사다리를 오른다
매주 메트릭을 본다. Escape rate가 낮고 안정적이면 → 신뢰 사다리(10장)를 한 칸 올린다. Escape rate가 오르거나 Rollback rate가 튀면 → 한 칸 내린다. 자동화 범위는 고정값이 아니라 데이터로 조절하는 다이얼이다.
에필로그 — 파이프라인이 곧 팀의 형태
이 글의 파이프라인을 다 구현하면 팀의 일하는 방식이 바뀐다.
- 개발자는 코드를 적게 타이핑하고 Issue를 잘 쓰고 PR을 잘 리뷰한다.
- 잔손질 리뷰는 AI가, 판단 리뷰는 사람이.
- 탐지·진단·수정 착수·재배포의 반복 노동은 시스템이.
- 사람은 되돌릴 수 없는 결정의 게이트를 지킨다.
핵심 통찰 세 가지로 정리한다.
-
선이 아니라 고리다. 배포가 끝이 아니다. 모니터링이 사건을 잡아 다시 Issue로 만들면, 시스템이 자기 문제를 입력으로 받아 고친다.
-
"작업은 자동화, 결정은 게이트." AI가 코드를 짜고 리뷰하고 배포하게 두되, 머지·스키마·보안 같은 비가역 결정에는 사람 또는 강한 자동 검증을 세운다. fail-closed가 기본값이다.
-
신뢰는 적립된다. 처음부터 다 자동화하지 말고, 저위험 작업부터, 메트릭(특히 escape rate)을 보며 한 칸씩 올린다. 자동화 범위는 데이터로 조절하는 다이얼이다.
역설적이게도 이 모든 자동화의 최종 목적은 사람을 일에서 빼는 것이 아니라, 사람을 가장 중요한 곳 — 판단과 결정 — 에 집중시키는 것이다. 파이프라인이 반복 노동을 흡수하면, 팀의 사고는 "어떻게 만들까"에서 "무엇을·왜 만들까"로 올라간다.
14개 항목 체크리스트
- 7개 스테이지를 다 식별하고 각 소유자를 정했는가?
- 모든 스테이지가 fail-closed인가?
- 모든 스테이지에 롤백/취소 수단이 있는가?
- 생성 에이전트와 리뷰 에이전트가 분리됐는가 (다른 모델)?
- AI 리뷰가 필수 상태 체크로 걸려 있는가?
- CI 실패 메시지가 AI가 읽을 만큼 친절한가?
- 자가 수정 루프에 횟수 상한이 있는가?
- 브랜치 보호에 필수 체크 + 사람 승인이 강제되는가?
- 사람 승인은 봇이 아닌 사람 계정만 카운트되는가?
- Preview·Canary·Production이 단계적으로 분리됐는가?
production환경에 required reviewers가 걸려 있는가?- 자동 검증(smoke·SLO)이 Canary 승격을 게이트하는가?
- SLO 위반 시 자동 롤백 + 자동 Issue 생성이 되는가 (고리)?
- Escape rate를 추적하고 그 데이터로 자동화 범위를 조절하는가?
안티패턴 10가지
- 생성 에이전트가 자기 PR을 리뷰·승인.
- AI 리뷰 APPROVE를 사람 승인으로 카운트.
- 애플리케이션 로직을 자동 머지.
- 머지 즉시 100% 프로덕션 배포 (Canary 없음).
- 배포 후 검증 없음 (smoke·SLO 게이트 부재).
- 자동 롤백은 있는데 사건이 Issue로 안 돌아옴 (고리가 안 닫힘).
- CI 실패 메시지가 불친절해 AI 자가 수정 불가.
- 자가 수정 루프에 상한 없음 → 비용 폭탄.
- 외부 사용자 Issue가 파이프라인을 자동 트리거.
- Escape rate 측정 없이 자동화 범위만 넓힘.
다음 글 예고
다음 글 후보: AI 코드 리뷰어 직접 만들기 — 팀 컨벤션을 학습하는 커스텀 리뷰 봇, Progressive Delivery 심층 — Argo Rollouts·Flagger로 SLO 기반 자동 승격, 에이전트 오케스트레이션 — LangGraph로 멀티 스테이지 파이프라인을 상태 머신으로.
"최고의 파이프라인은 사람을 대체하지 않는다. 사람이 결정만 하도록, 나머지 모든 반복을 흡수한다."
— Issue에서 배포까지, 자동화 파이프라인 만들기, 끝.
현재 단락 (1/499)
[지난 글](/blog/2026-05-14-ai-development-automation-github-copilot-claude-code-devin-ticket-based-agen...