들어가며
40차시는 이렇게 끝났다 — “사람의 터미널에서 팀의 파이프라인으로, 무대가 옮겨간다.” 섹션 8에서 변경 확인(37), 커밋(38), PR(39), 리뷰(40)의 사이클을 터미널에서 한 바퀴 돌았다. 그 끝에서 리뷰는 이미 사람이 부르지 않아도 도는 형태(관리형 Code Review)로 넘어가고 있었다. 섹션 9의 첫 차시인 이번 41차시가 그 이사(移徙)다 — 같은 사이클을 이번엔 파이프라인이 돌린다.
다룰 것은 셋이다. CI/CD 파이프라인이라는 무대의 성질, 터미널의 Claude를 그 무대에 올릴 수 있는 형태로 바꾸는 헤드리스 모드, 그리고 그 형태를 GitHub 이벤트에 배선해 주는 공식 GitHub Actions 통합.
CI/CD 한 컷 — 파이프라인에 ‘판단’이 들어온다
용어부터 짧게. CI(Continuous Integration, 지속적 통합)는 변경이 공유 저장소에 합류할 때마다 빌드와 테스트가 자동으로 도는 것이고, CD(Continuous Delivery/Deployment, 지속적 전달/배포)는 그 검증을 통과한 변경이 자동으로 릴리스까지 나가는 것이다. 이 둘을 실어 나르는 것이 파이프라인 — 이벤트(push, PR 열림, 스케줄)가 트리거하는 단계(step)들의 사슬이다. GitHub에선 .github/workflows/*.yml의 GitHub Actions가, GitLab에선 .gitlab-ci.yml의 GitLab CI/CD가 이 사슬을 정의한다.
지금까지 이 사슬에 들어가던 단계는 전부 결정론적 검사였다. 컴파일이 되는가, 테스트가 통과하는가, 린트가 깨끗한가 — 같은 입력이면 언제나 같은 답이 나오는 일들. Claude Code가 파이프라인에 들어오면 처음으로 판단이 필요한 단계가 사슬에 끼어든다. “이 diff에 버그가 있는가”, “이 이슈를 어떻게 고치는가”, “어제 커밋들을 요약하면” — 지금까지는 사람이 파이프라인 바깥에서 하던 일이다.
그런데 파이프라인이라는 무대에는 터미널과 결정적으로 다른 성질이 하나 있다. Enter 칠 사람이 없다. 지금까지의 Claude Code는 권한이 필요하면 물어봤다(17차시). 파이프라인의 Claude에겐 물어볼 상대가 없다 — 이 결핍이 이번 차시의 모든 설계를 결정한다.
부품의 조건 — 헤드리스 모드의 세 전환
공식 문서에서 이 주제를 다루는 페이지의 제목이 방향을 말해 준다 — “Run Claude Code programmatically”, Claude Code를 프로그램처럼 실행하기. 핵심은 -p(--print) 플래그다.
claude -p "Find and fix the bug in auth.py" --allowedTools "Read,Edit,Bash"
-p를 붙이면 Claude Code는 대화형 세션을 열지 않고, 프롬프트 하나를 받아 일하고, 결과를 출력하고, 종료한다. 이걸 헤드리스(headless) 모드라 부른다 — 화면(head) 없이 도는 실행. 문서가 밝히듯 이것은 Claude Code를 움직이는 것과 같은 도구·에이전트 루프를 노출하는 Agent SDK의 CLI 얼굴이고, 뒤에서 볼 GitHub Actions 통합도 이 SDK 위에 지어져 있다.
터미널의 대화가 파이프라인의 부품이 되려면 세 가지가 바뀌어야 한다. 헤드리스 모드의 플래그들이 정확히 그 세 전환에 대응한다.
전환 ① 입출력 — 대화가 아니라 파이프
부품은 stdin으로 받고 stdout으로 내보낸다. -p 모드는 여느 CLI 도구처럼 파이프에 낀다.
cat build-error.txt | claude -p 'concisely explain the root cause of this build error' > output.txt
빌드 로그가 들어가고 원인 설명이 나온다(파이프로 들어가는 stdin은 v2.1.128부터 10MB 캡 — 넘으면 명확한 에러와 함께 종료된다. 큰 입력은 파일로 쓰고 경로를 프롬프트에 넘긴다). 공식 문서의 package.json 예제는 이 패턴으로 Claude를 프로젝트 전용 린터로 만든다.
{
"scripts": {
"lint:claude": "git diff main | claude -p \"you are a typo linter. for each typo in this diff, report filename:line on one line and the issue on the next. return nothing else.\""
}
}
diff를 파이프로 넣는 디테일에 이유가 있다 — diff를 읽기 위한 Bash 권한이 필요 없어진다. 입력을 파이프로 주면 권한 선언(전환 ②)이 그만큼 줄어든다.
출력 쪽은 기계가 읽을 형식이 필요하다. --output-format이 세 가지를 준다 — text(기본), json, stream-json(실시간 스트리밍). json이 파이프라인의 주력이다.
claude -p "Summarize this project" --output-format json | jq -r '.result'
응답엔 텍스트 결과(result) 외에 session_id와 total_cost_usd(모델별 비용 내역 포함)가 들어 있다 — 스크립트가 호출 한 번마다 얼마를 썼는지 스스로 추적할 수 있다. 특정 스키마의 출력이 필요하면 --json-schema에 JSON Schema를 넘긴다. 응답의 structured_output 필드에 스키마를 만족하는 구조화된 결과가 담긴다.
claude -p "Extract the main function names from auth.py" \
--output-format json \
--json-schema '{"type":"object","properties":{"functions":{"type":"array","items":{"type":"string"}}},"required":["functions"]}'
전환 ② 권한 — 물어볼 사람이 없으니, 미리 선언한다
터미널에서 권한은 그때그때 승인이었다. CI에선 그 자리에 사전 선언이 온다. 도구는 둘이다.
--allowedTools — 허용할 도구를 명시한다. 17차시의 권한 규칙 문법이 그대로 쓰인다. 38차시에서 절차로 배웠던 커밋 생성이 헤드리스에선 이렇게 한 줄 부품이 된다.
claude -p "Look at my staged changes and create an appropriate commit" \
--allowedTools "Bash(git diff *),Bash(git log *),Bash(git status *),Bash(git commit *)"
뉘앙스 하나가 중요하다 — * 앞의 공백이다. *는 접두사 매칭이라 Bash(git diff *)는 git diff로 시작하는 모든 명령을 허용하지만, 공백 없는 Bash(git diff*)는 git diff-index까지 매칭해 버린다. 선언은 좁게, 정확하게.
--permission-mode — 개별 도구 대신 세션 전체의 기준선을 놓는다. CI에서 쓰는 둘의 정의를 문서에서 정확히 옮긴다.
| 모드 | 하는 일 |
|---|---|
acceptEdits | 파일 쓰기를 승인 없이 허용 + mkdir·touch·mv·cp 같은 흔한 파일시스템 명령 자동 승인 |
dontAsk | permissions.allow 규칙과 읽기전용 명령 집합 밖의 모든 것을 거부 — “잠긴” CI 실행용 |
acceptEdits를 만능으로 오해하기 쉬운데, 문서가 선을 긋는다 — 그 밖의 셸 명령과 네트워크 요청은 여전히 --allowedTools나 permissions.allow 규칙이 필요하고, 없으면 시도하는 순간 실행이 중단된다(“otherwise the run aborts when one is attempted”). 물어볼 수 없으니, 선언 밖의 시도는 곧 실패다. 37차시의 안전 원칙이 CI에선 이렇게 미리 선언하는 형태로 돌아온다.
전환 ③ 환경 — 어느 러너에서나 같은 결과, --bare
세 번째 전환이 가장 눈에 안 띄고 가장 자주 문제가 된다. claude -p는 기본적으로 대화형 세션과 같은 컨텍스트를 로드한다 — 작업 디렉토리와 ~/.claude에 설정된 훅(2831차시), 스킬(2427차시), 플러그인, MCP 서버(32~36차시), 자동 메모리, CLAUDE.md(21차시)까지. 내 머신에서 잘 되던 스크립트가 러너에서 다르게 도는 이유가 대부분 여기 있다.
--bare가 이 자동 탐색을 전부 끈다. 문서의 표현이 정확하다.
Bare mode is useful for CI and scripts where you need the same result on every machine. A hook in a teammate’s
~/.claudeor an MCP server in the project’s.mcp.jsonwon’t run, because bare mode never reads them. Only flags you pass explicitly take effect.
어느 머신에서나 같은 결과 — 명시적으로 넘긴 플래그만 효력을 가진다. 필요한 컨텍스트는 되살릴 플래그가 하나씩 있다(--append-system-prompt, --settings, --mcp-config, --agents, --plugin-dir). 인증도 자동 탐색하지 않으므로 ANTHROPIC_API_KEY 환경 변수(또는 --settings의 apiKeyHelper)로 준다. 그리고 문서의 예고 한 줄 —
--bareis the recommended mode for scripted and SDK calls, and will become the default for-pin a future release.
스크립트 호출의 공식 권장이자 미래의 -p 기본값이다. 단, 잊지 말 것 — bare는 CLAUDE.md도 읽지 않는다. 여기서 두 용도가 갈라진다. --bare의 Claude는 재현 가능한 부품이고, 프로젝트의 규칙과 맥락을 아는 협업자가 아니다. 협업자가 필요한 자리 — 그게 다음 절의 GitHub Actions 통합이다(액션은 CLAUDE.md를 따르도록 설계돼 있다).
GitHub Actions — 부품을 이벤트에 배선하는 공식 액션
원리적으로는 방금 배운 재료만으로 CI에 Claude를 넣을 수 있다 — 러너에서 curl -fsSL https://claude.ai/install.sh | bash로 설치하고, 시크릿으로 ANTHROPIC_API_KEY를 주고, claude -p를 부르면 된다(뒤에서 볼 GitLab 통합이 정확히 이 모양이다). 하지만 GitHub에는 이 배선을 포장한 공식 액션이 있다 — anthropics/claude-code-action@v1(GA. 베타에서 올라왔고, @beta의 mode·direct_prompt 같은 입력은 v1에서 제거·통합됐다). 문서의 첫 문장이 핵심 사용감을 요약한다.
With a simple
@claudemention in any PR or issue, Claude can analyze your code, create pull requests, implement features, and fix bugs - all while following your project’s standards.
그리고 바로 다음 문장이 40차시와의 경계를 그어 준다 — 트리거 없이 모든 PR에 자동으로 리뷰를 달고 싶으면 그건 관리형 Code Review(40차시)의 자리다. 이 액션은 리뷰 전용 완제품이 아니라 범용 — 어떤 워크플로우든 그 위에 지을 수 있는 토대다.
설치 — /install-github-app
Claude Code 터미널에서 한 줄이다.
/install-github-app
Claude GitHub App을 저장소에 설치하고, 워크플로우 파일 추가와 API 키 시크릿 등록까지 대화형으로 안내한다. 저장소 admin이어야 하고, 이 빠른 경로는 직접 Claude API 사용자용이다(Bedrock·Vertex는 수동 설정). v2.1.187부터는 App만 설치하고 “Skip for now”로 멈췄다가 나중에 다시 이어갈 수 있다. 수동으로 하려면 세 단계 — App 설치(요구 권한: Contents·Issues·Pull requests 각 Read & write), ANTHROPIC_API_KEY 시크릿 등록, 예제 워크플로우 복사.
기본 워크플로우는 이게 전부다.
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
jobs:
claude:
runs-on: ubuntu-latest
steps:
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
# Responds to @claude mentions in comments
두 모드, 자동 감지
이 액션의 설계에서 가장 배울 만한 부분이다. 같은 액션이 두 방향의 자동화를 다 담당하는데, 어느 쪽으로 돌지 설정으로 고르지 않는다.
The action now automatically detects whether to run in interactive mode (responds to
@claudementions) or automation mode (runs immediately with a prompt) based on your configuration.
- interactive 모드 —
prompt없이 코멘트 이벤트에 배선하면, Claude는@claude멘션(트리거 문구는trigger_phrase로 변경 가능)을 기다리는 팀의 대화 상대가 된다. 사람이 파이프라인 안의 Claude를 부르는 방향이다. - automation 모드 —
prompt를 주면 이벤트가 발생하는 즉시 그 지시대로 돈다. 파이프라인이 Claude를 부품으로 부르는 방향이다.
interactive — @claude가 팀의 인터페이스가 된다
이슈나 PR 코멘트에서 이렇게 부른다.
@claude implement this feature based on the issue description
@claude how should I implement user authentication for this endpoint?
@claude fix the TypeError in the user dashboard component
터미널에서 하던 대화가 GitHub의 스레드로 옮겨온 것이다 — 이슈가 컨텍스트가 되고, 진행 상황은 동적으로 갱신되는 체크박스 목록으로 코멘트에 표시된다. 그런데 여기서 39차시의 감각으로 보면 이상한 지점이 하나 나온다. @claude implement …가 구현을 마치면 PR이 자동으로 열릴 것 같지만, 액션의 보안 문서는 정반대를 못 박는다.
Claude does not create pull requests automatically when responding to
@claudementions.
Claude는 새 브랜치에 커밋하고, PR 생성 링크까지만 제공한다. 마지막 클릭은 사람 몫이다. 실수가 아니라 설계다 — 사람의 검토가 끼어들 자리를 구조적으로 남겨 두는 것. 39차시의 “생성한 PR을 그대로 신뢰하지 말라”가 여기선 아예 신뢰할지 말지를 사람이 클릭으로 결정하는 형태로 굳어 있다.
automation — 사람 없이 도는 워크플로우
prompt를 주면 트리거만 있고 사람은 없다. 공식 예제 둘이 방향을 잘 보여 준다.
PR이 열릴 때마다 리뷰 — 40차시의 /code-review가 여기서 자동화된다. prompt는 평문만이 아니라 스킬 호출도 받는다(저장소 .claude/skills/의 스킬이면 checkout 후 /skill-name, 플러그인 스킬이면 설치 입력과 함께 네임스페이스 경로로).
name: Code Review
on:
pull_request:
types: [opened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
plugin_marketplaces: "https://github.com/anthropics/claude-code.git"
plugins: "code-review@claude-code-plugins"
prompt: "/code-review:code-review ${{ github.repository }}/pull/${{ github.event.pull_request.number }}"
스케줄 작업 — 이벤트는 push나 PR만이 아니다. cron도 이벤트다.
name: Daily Report
on:
schedule:
- cron: "0 9 * * *"
jobs:
report:
runs-on: ubuntu-latest
steps:
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: "Generate a summary of yesterday's commits and open issues"
claude_args: "--model opus"
마지막 줄의 claude_args가 액션의 확장 통로다 — Claude Code CLI 인자가 그대로 통과한다. 앞 절에서 배운 헤드리스 플래그들이 여기서 재등장한다는 뜻이다. 자주 쓰는 것들: --max-turns(대화 턴 상한, 액션 기본 10), --model, --allowedTools, --mcp-config, --debug. 액션이 구조화된 출력을 지원하는 것도 같은 맥락이다 — 검증된 JSON 결과가 GitHub Actions의 output으로 나와 다음 스텝이 이어받을 수 있다.
주요 파라미터를 정리하면 이렇다.
| 파라미터 | 역할 |
|---|---|
prompt | 지시(평문 또는 스킬 호출). 코멘트 이벤트에서 생략하면 @claude 멘션 응답 모드 |
claude_args | Claude Code CLI 인자 패스스루 |
anthropic_api_key | 직접 Claude API에서만 필수 |
github_token | GitHub API 토큰(커스텀 App 토큰을 넘길 때) |
trigger_phrase | 트리거 문구(기본 @claude) |
plugin_marketplaces / plugins | 실행 전 설치할 플러그인 |
use_bedrock / use_vertex | Amazon Bedrock / Google Cloud(Vertex) 경유 |
게이트 — claude ultrareview, exit 코드의 함정
40차시에서 예고한 다리를 건널 차례다. /code-review ultra(클라우드 멀티 에이전트 리뷰)에는 CI용 비대화형 서브커맨드가 있다.
claude ultrareview # 현재 브랜치 vs 기본 브랜치
claude ultrareview 1234 # PR 리뷰
claude ultrareview origin/main # 지정한 base 브랜치와의 diff
동작은 파이프라인 친화적으로 설계돼 있다 — 리뷰가 끝날 때까지 블록하고, 발견을 stdout에 출력하며(진행 메시지와 라이브 세션 URL은 stderr로 — stdout은 파싱 가능하게 유지된다), --json이면 raw bugs.json을, --timeout <minutes>(기본 30)로 대기 상한을 정한다. 대화형에서 뜨던 과금 확인 다이얼로그는 서브커맨드 호출 자체가 동의로 간주된다.
그런데 exit 코드에 함정이 하나 있다. 문서를 정확히 읽자.
exits with code 0 when the review completes with or without findings, code 1 when the review fails to launch, the cloud session errors, or the timeout elapses
exit 0은 ‘버그 없음’이 아니라 ‘리뷰 완료’다. 발견이 열 개 나와도 0이다. 그러니 claude ultrareview && deploy 식으로 배포 게이트를 걸면 버그가 있어도 통과한다 — exit 코드가 게이트하는 건 리뷰가 돌았는가이고, 발견이 있는가로 게이트하려면 --json 출력을 직접 파싱해야 한다. 참고로 Ctrl-C(exit 130)로 서브커맨드를 끊어도 원격 리뷰는 계속 돈다(stderr의 세션 URL로 브라우저에서 확인).
CI에서 쓰기 전 제약도 확인하자 — ultrareview는 Claude Code on the web 인프라에서 돌기 때문에 Claude.ai 계정 인증이 필요하다(API 키 단독 불가, Bedrock·Vertex·Foundry 경유 불가, ZDR 조직 불가). usage credits도 켜져 있어야 한다. 그리고 문서가 자리를 정리해 준다 — PR마다 자동 리뷰가 목적이면 CLI 스텝 없이 인라인 코멘트를 다는 관리형 Code Review(40차시)가 그 자리다. claude ultrareview는 특정 시점(머지 직전, 릴리스 브랜치)에 파이프라인이 명시적으로 부르는 깊은 검사 쪽이다.
보안과 비용 — 사람이 없는 곳의 규율
터미널의 안전 장치(37차시)는 사람의 승인이었다. 파이프라인엔 그 자리에 규율이 온다.
키는 시크릿으로. 액션 보안 문서의 표현 그대로 — “CRITICAL: Never hardcode your Anthropic API key or OAuth token in workflow files!” GitHub Secrets(${{ secrets.ANTHROPIC_API_KEY }}), GitLab masked variable이 그 자리다.
아무나 트리거할 수 없다. 기본값으로 @claude를 발동시킬 수 있는 건 저장소에 write 권한이 있는 사용자뿐이고, 봇은 기본 차단이다(allowed_bots로 선별 허용 — '*'는 권한 검사를 건너뛰므로 공개 저장소에서 위험하다). GitHub App이 받는 토큰은 해당 저장소로 스코프된 단기 토큰이라 저장소 간 접근이 없다.
프롬프트 인젝션. 공개 저장소에서 외부 기여자의 이슈에 @claude를 태깅하는 순간, 그 이슈 본문이 Claude의 입력이 된다. 보안 문서가 경고한다 — 외부 콘텐츠에는 HTML 주석, 보이지 않는 문자, 숨은 속성 등으로 숨은 지시가 들어 있을 수 있다. 액션은 이런 숨은 마크다운을 스트리핑하는 방어를 내장하고 있고, include_comments_by_actor류 입력으로 신뢰할 작성자를 제한할 수 있다. 도구 출력 전체를 로그에 노출하는 show_full_output이 기본 비활성인 것도 같은 이유다(공개 저장소의 Actions 로그는 공개다).
Claude의 커밋에 CI가 안 돈다면. 흔한 미스터리인데 원인은 토큰이다 — 공식 트러블슈팅이 “GitHub App 또는 커스텀 App을 쓰고 있는지(Actions user가 아니라) 확인하라”고 짚는다. 기본 GITHUB_TOKEN으로 푸시된 커밋은 후속 워크플로우를 트리거하지 않는 것이 GitHub의 정책이라, Claude가 만든 커밋에도 테스트가 돌게 하려면 App 토큰이 필요하다.
사람 리뷰는 여전히 마지막 관문. GitHub 문서는 “Review Claude’s suggestions before merging”, GitLab 문서는 “Review Claude’s MRs like any other contributor”라고 쓴다. 자동화가 늘수록 이 문장의 무게가 커진다 — 40차시의 원칙이 그대로 이어진다.
비용은 이중이다. 러너 사용 시간(GitHub Actions 분, GitLab 러너 분)과 API 토큰. 절약의 도구도 양쪽에 하나씩 — 워크플로우 쪽은 타임아웃과 concurrency 컨트롤(폭주·중복 실행 방지), Claude 쪽은 --max-turns(액션 기본 10)와 모델 선택, 그리고 구체적인 @claude 지시(모호한 지시가 턴을 늘린다). 호출당 실비는 앞서 본 --output-format json의 total_cost_usd로 추적한다.
GitHub 밖 — GitLab, 그리고 기업 클라우드
GitLab CI/CD. 공식 문서가 있는 두 번째 배선이다 — 단, 상태 표시를 정확히 읽자. 베타이고, “This integration is maintained by GitLab”, 즉 Anthropic이 아니라 GitLab이 유지관리한다. 모양은 이 차시 앞부분에서 배운 직접 배선 그대로다 — 잡 하나, masked 변수 하나.
claude:
stage: ai
image: node:24-alpine3.21
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
before_script:
- apk add --no-cache git curl bash
- curl -fsSL https://claude.ai/install.sh | bash
script:
- >
claude
-p "${AI_FLOW_INPUT:-'Review this MR and implement the requested changes'}"
--permission-mode acceptEdits
--allowedTools "Bash Read Edit Write mcp__gitlab"
러너 컨테이너에 Claude Code를 설치하고 claude -p를 부른다 — 헤드리스 모드의 세 전환이 그대로 보인다(입력은 AI_FLOW_INPUT 변수로, 권한은 --permission-mode+--allowedTools로 사전 선언). @claude 멘션 트리거는 GitHub처럼 내장이 아니라 웹훅+파이프라인 트리거 API로 직접 배선한다. 거버넌스는 GitLab의 기존 장치를 탄다 — 모든 변경이 MR로 흐르므로 브랜치 보호와 승인 규칙이 AI가 만든 코드에도 그대로 적용된다.
기업 클라우드. GitHub·GitLab 양쪽 다 Amazon Bedrock과 Google Cloud(Vertex, 문서 표기는 “Google Cloud’s Agent Platform”) 경유를 지원한다. 공통 패턴은 키 없는 인증 — AWS는 OIDC로 IAM 역할을 임시로 맡고(assume role), GCP는 Workload Identity Federation으로 서비스 계정을 가장한다. 장기 자격증명을 시크릿에 넣지 않는 것 자체가 보안 설계다. GitHub 액션에선 use_bedrock: "true" / use_vertex: "true" 입력과 리전 접두사 모델 ID(예: us.anthropic.claude-sonnet-4-6)를 쓴다.
흔한 함정
- CI에서 권한 선언 없이
claude -p를 부른다. 러너엔 Enter 칠 사람이 없다 — 선언 밖의 도구를 시도하는 순간 실행이 중단된다(“the run aborts”).--allowedTools나--permission-mode로 미리 선언한다. acceptEdits를 만능으로 안다. 파일 쓰기와mkdir·mv류까지만 자동이다. 그 밖의 셸 명령·네트워크 요청은 여전히 선언이 필요하다. 잠긴 실행이 필요하면dontAsk.Bash(git diff*)처럼 공백을 빼먹는다.git diff-index까지 매칭된다. 접두사 매칭은 공백 있는*다.- 로컬에서 되던 스크립트가 러너에서 다르게 돈다. 내 머신의 훅·스킬·MCP·메모리를 러너는 모른다(역도 성립 — 팀원 머신엔 뭐가 있을지 모른다). 스크립트 호출은
--bare+명시 플래그로 고정한다. --bare인데 CLAUDE.md 규칙이 적용되길 기대한다. bare는 CLAUDE.md도 읽지 않는다. 프로젝트 맥락이 필요한 자리(액션의 interactive 모드 등)와 재현 가능한 부품의 자리를 구분한다.- API 키를 워크플로우 파일에 하드코딩한다. 보안 문서의 CRITICAL 항목이다. GitHub Secrets / GitLab masked variable로.
/claude로 멘션한다. 트리거는@claude다(공식 트러블슈팅 1번 항목). 문구를 바꿨다면trigger_phrase확인.- 멘션했는데 PR이 자동으로 안 열린다고 버그로 안다. 설계다 — 브랜치 커밋과 PR 생성 링크까지가 Claude의 몫, 클릭은 사람의 몫이다.
- Claude가 푸시한 커밋에 CI가 안 돈다. 기본 토큰으로 푸시된 커밋은 워크플로우를 깨우지 않는다. GitHub App(또는 커스텀 App) 토큰을 쓴다.
claude ultrareview의 exit 0을 ‘버그 없음’으로 게이트한다. 0은 ‘리뷰 완료’다 — 발견이 있어도 0. 발견 기반 게이트는--json파싱으로 직접 만든다. 러너에 Claude.ai 인증과 usage credits가 없으면 애초에 못 뜬다.- automation 모드에 상한 없이 프롬프트만 준다.
--max-turns(기본 10), 워크플로우 타임아웃, concurrency 컨트롤 — 폭주 방어는 비용 방어다.
정리
핵심 요점
- CI/CD 파이프라인 = 이벤트가 트리거하는 단계 사슬, 지금까지는 결정론적 검사뿐이었다. Claude가 들어오면 판단이 필요한 단계(리뷰·수정·요약)가 처음으로 사슬에 들어온다. 결정적 차이: Enter 칠 사람이 없다.
- 헤드리스 모드(
claude -p)가 대화를 부품으로 바꾼다 — 세 전환. ① 입출력: stdin/stdout 파이프,--output-format json(result·session_id·total_cost_usd),--json-schema(structured_output). ② 권한: 사전 선언 —--allowedTools(17차시 규칙 문법,*공백 주의),acceptEdits(파일 쓰기+기본 파일시스템만),dontAsk(잠긴 CI). 선언 밖 시도는 run aborts. ③ 환경:--bare— 훅·스킬·플러그인·MCP·CLAUDE.md 자동 탐색을 꺼서 “어느 머신에서나 같은 결과”. 권장이자 미래 기본값. - GitHub의 공식 배선은
claude-code-action@v1. 설치는/install-github-app. 모드는 자동 감지 —prompt없으면 interactive(@claude멘션 응답), 있으면 automation(즉시 실행).claude_args로 CLI 플래그가 그대로 통과한다(--max-turns기본 10). - interactive의 설계 — PR의 마지막 클릭은 사람.
@claude implement …는 브랜치 커밋+PR 생성 링크까지만 만든다(“Claude does not create pull requests automatically”). 39차시 원칙의 구조판. - automation이 섹션 8의 사이클을 자동화한다. PR마다 code-review 플러그인 스킬(40차시), cron 데일리 리포트, 그리고 머지 게이트로
claude ultrareview— 단 exit 0은 ‘완료’지 ‘무결’이 아니다. 발견 게이트는--json파싱. Claude.ai 인증 필요(API 키 단독·Bedrock·Vertex 불가). - 사람이 없는 곳의 규율. 키는 시크릿(하드코딩 CRITICAL 금지), 트리거는 write 권한자만+봇 차단, 숨은 마크다운 인젝션은 액션이 스트리핑, Claude 커밋에 CI를 돌리려면 App 토큰, 그리고 머지 전 사람 리뷰는 여전히 마지막 관문. 비용은 이중(러너 분+토큰) —
--max-turns·타임아웃·concurrency로 다스린다. - 같은 재료, 다른 배선. GitLab CI/CD는 러너에 설치+
claude -p의 직접 배선(베타, GitLab 유지관리), 모든 변경은 MR로 흘러 브랜치 보호·승인 규칙을 그대로 탄다. Bedrock·Vertex는 OIDC/WIF로 키 없이.
다음 단계
이번 차시의 자동화는 전부 이벤트가 주도했다 — push가, PR이, cron이 Claude를 불렀고 나는 결과만 받았다. **42차시(원격 세션 활용)**는 방향을 뒤집는다. 주도권은 내게 있는데 실행 환경만 원격인 형태 — claude ultrareview가 돌던 바로 그 원격 샌드박스 인프라에서 내 세션을 직접 굴린다. 원격 환경에서 Claude Code를 실행하고, 원격 세션에서만 도는 Hook을 설정하고, CLAUDE_CODE_REMOTE 같은 환경 변수로 로컬과 원격을 가르는 법을 다룬다.
참고 자료
- GitHub Actions —
claude-code-action@v1,/install-github-app, 모드 자동 감지(interactive/automation), 파라미터·claude_args, 스킬·플러그인 호출, Bedrock/Vertex(OIDC/WIF), 비용·트러블슈팅 - Run Claude Code programmatically —
claude -p,--bare(“same result on every machine”, 권장·미래 기본값), 파이프(stdin 10MB 캡),--output-format json(total_cost_usd)·--json-schema,--allowedTools규칙 문법,acceptEdits/dontAsk - claude-code-action 저장소 — v1.0 GA, 진행 체크박스, 구조화된 출력(GitHub Action outputs), 프로바이더 4종(API·Bedrock·Vertex·Foundry), examples 디렉토리
- 액션 보안 문서 — write 권한 트리거·봇 차단, 숨은 마크다운 인젝션 스트리핑, “Claude does not create pull requests automatically”, 키 하드코딩 금지, 커밋 서명
- GitLab CI/CD — 베타(GitLab 유지관리),
.gitlab-ci.yml잡+masked 변수,AI_FLOW_*멘션 배선, MR 거버넌스, Bedrock/Vertex 예제 - Ultrareview —
claude ultrareview서브커맨드(블록·stdout 발견·--json·--timeout), exit 코드 의미(0=완료), 인증·usage credits 제약, 관리형 Code Review와의 자리 구분