· 6 min read

self-hosted CI에 Claude Code 구독 물리기 — PR 자동 분석·테스트 파이프라인

API가 아닌 Claude Code 구독을 self-hosted runner에 물려, CI가 돌 때마다 테스트 자동화와 PR 변경 분석을 수행하는 파이프라인. AI와 함께 코딩하니 테스트가 늘고, 테스트가 늘으니 CI가 필요해진 흐름을 정리했다.

API가 아닌 Claude Code 구독을 self-hosted runner에 물려, CI가 돌 때마다 테스트 자동화와 PR 변경 분석을 수행하는 파이프라인. AI와 함께 코딩하니 테스트가 늘고, 테스트가 늘으니 CI가 필요해진 흐름을 정리했다.

AI와 코딩하면 테스트가 늘어난다

Aethelgard(2D MMORPG)를 Claude Code와 함께 개발하고 있다. 내가 모든 코드를 직접 작성하는 게 아니다 보니 테스트의 비중이 커졌다. AI가 작성한 코드가 의도대로 동작하는지 확인하는 가장 확실한 방법이 테스트니까.

단위 테스트에서 시작해서 통합 테스트, Playwright 기반 E2E 테스트까지. 테스트도 AI가 작성하니 수가 빠르게 늘었다. E2E 테스트만 수십 개, 전체를 로컬에서 돌리면 몇 분씩 걸리게 됐다.

CI가 필요해졌다

테스트를 로컬에서만 돌리면 작업 흐름이 끊긴다. GitHub Actions로 옮겨서 PR마다 자동으로 돌리게 했는데, hosted runner의 minutes가 빠르게 쌓였다. E2E 테스트는 서버 빌드 + 브라우저 실행이라 무겁다.

집에 놀고 있는 Windows 컴퓨터에 self-hosted runner를 세팅했다. CI 비용이 $0이 됐다.

PR 변경 분석도 자동으로

self-hosted runner가 생기니 하나 더 할 수 있는 게 있었다. PR의 변경사항을 자동으로 분석하는 것.

솔로 개발에 worktree 4개를 동시에 돌리다 보면 PR이 자주 올라온다. 각 PR이 어떤 시스템을 건드렸고 뭘 바꿨는지를 매번 직접 diff를 읽으며 파악하는 건 번거롭다.

Claude Code CLI에는 --agent 옵션이 있다. self-hosted runner에 구독 세션으로 로그인해두면 추가 API 비용 없이 Agent를 돌릴 수 있다.

다만 이건 코드 리뷰를 AI에게 맡기는 게 아니다. 코드의 품질을 판단하는 건 내 몫이다. Agent가 하는 일은 “이 PR이 뭘 바꿨는지”를 빠르게 요약해주는 것에 가깝다.

구성

워크플로우

# pr-agent.yml
test:
  uses: ./.github/workflows/test.yml  # 유닛 + E2E

agent:
  needs: test
  if: always()  # 테스트 실패해도 실행
  runs-on: self-hosted
  steps:
    - run: |
        claude --agent ci-agent \
          -p "$PROMPT" \
          --permission-mode bypassPermissions \
          --max-turns 15 \
          --output-format stream-json

test job이 먼저 돌고, 결과와 무관하게 agent job이 변경사항을 분석한다. 테스트가 실패했을 때도 “뭐가 바뀌었는지”는 알아야 하니까.

Agent 정의

.claude/agents/ci-agent.md:

---
model: sonnet
maxTurns: 15
effort: high
allowedTools:
  - Bash
  - Grep
  - Glob
  - Read
---

핵심은 read-only라는 점이다. 파일을 수정하거나 커밋하지 않는다. git diff main...HEAD로 변경사항을 읽고, 어떤 시스템이 영향을 받았는지, feature/bugfix/refactor 중 어떤 성격인지를 정리해서 PR 코멘트로 남긴다.

--permission-mode bypassPermissions로 CI에서 승인 없이 실행하되, allowedTools를 읽기 전용으로 제한했다.

PR 코멘트 구조

Agent 출력은 parse_agent_output.py가 마크다운으로 변환한다. 최종 PR 코멘트는 이런 구조:

## CI Agent Report
[OK] 10 turns | 0:01:03

### Test Results
| Suite | Result | Passed | Failed | Total |
|-------|--------|--------|--------|-------|
| Unit  | ✅ Passed | 733 | 0 | 733 |
| E2E   | ✅ Passed | 89  | 0 | 89  |

### Changes
- Projectile 시스템 히트 판정 로직 수정
- Systems affected: Combat, Physics
- MeleeHitSystem 실행 순서를 ProjectileHitSystem 앞으로 변경

테스트 결과는 .trx 파일을 파이프라인이 직접 파싱한다. Agent는 테스트 분석을 하지 않는다 — 변경사항 요약만 담당한다.

실제 PR에 달리는 코멘트는 이렇게 생겼다:

테스트 통과 + 변경 요약
테스트 통과 + 변경 요약

E2E 테스트가 실패하면 실패한 테스트의 상세 정보도 함께 표시된다:

E2E 실패 시 상세 리포트
E2E 실패 시 상세 리포트

전체 흐름

태스크 시작부터 완료까지 터미널에서 처리된다.

CI Pipeline Flow
CI Pipeline Flow

/pick-task로 Notion에서 태스크를 시작하고, /pr로 PR을 올리면 CI가 테스트 + 변경 분석을 자동으로 돌린다. 머지하면 on-merge.yml이 Notion 태스크를 자동 완료 처리한다.

정리

항목내용
CI Agent 모델sonnet
평균 분석 시간~1분/PR
CI 비용$0 (self-hosted)
Agent 비용$0 (구독제)

AI와 함께 개발하면 테스트와 CI의 중요성이 커진다. 내가 모든 코드를 한 줄씩 읽는 흐름이 아니니까, 테스트가 통과하는지, 어떤 시스템이 바뀌었는지를 자동으로 확인하는 파이프라인이 필요했다. self-hosted runner와 Claude Code 구독의 조합으로 추가 비용 없이 구성할 수 있었다.

Related Posts

View All Posts »

.NET 서버 메모리 누수 잡기 — dotnet-gcdump로 안 보이는 할당 폭탄

.NET 서버 메모리가 2분 만에 136MB에서 14GB로 폭증했다. dotnet-gcdump와 dotnet-counters로도 안 잡히는 할당 폭탄을 추적하고, 그 과정을 재사용 가능한 스킬로 만든 이야기.

#Aethelgard #AI #Claude #GameDev
AI에게 게임 월드를 만들라고 시켰더니 — AI-Driven Game Engine Development #1

AI에게 게임 월드를 만들라고 시켰더니 — AI-Driven Game Engine Development #1

AI가 월드를 자율적으로 만들려면 뭘 준비해야 하는가. 도구를 설계하고, 시행착오를 거치고, 벽 없는 미로에서 교훈을 얻기까지.

#OFF #AI #Claude #GameDev
NuGet 프라이빗 패키지로 .NET 코드 분리하기 — 멀티 레포 9개 함정

NuGet 프라이빗 패키지로 .NET 코드 분리하기 — 멀티 레포 9개 함정

OFF를 NuGet 프라이빗 패키지로 떼어, 협업자에게 프레임워크·서버 소스를 노출하지 않고도 풀스택 dev 루프를 유지한 멀티 레포 구조와, 옮기면서 밟은 9개의 함정.

#OFF #Aethelgard #GameDev #DotNet