Claude Code 헤드리스 실행(claude -p)으로 스크립트·CI 작업 자동화하기
bare 모드, 허용 도구 지정, 권한 모드, JSON·스키마 출력, 비용 필드, 실패 처리까지 스크립트에 넣는 순서

핵심 요약
- CI에서는 --bare로 실행해 저장소의 훅·MCP·CLAUDE.md가 신뢰 확인 없이 실행되지 않게 한다.
- --allowedTools는 권한 규칙 문법이며 Bash(git diff *)처럼 공백과 *로 접두사를 지정한다.
- 잠긴 CI는 --permission-mode dontAsk로 허용 목록 밖의 호출을 거부한다.
- --output-format json의 result, structured_output, session_id, total_cost_usd를 jq로 꺼내 쓴다.
- 종료 코드를 먼저 확인하고, --max-budget-usd로 실행당 지출 상한을 둔다.
Claude Code를 대화창에서만 쓰다 보면 "이 작업은 매번 같은 지시로 반복하는데"라는 순간이 온다. 빌드 로그 원인 요약, 변경분 오탈자 검사, 릴리스 노트 초안처럼 입력과 기대 출력이 정해진 작업이다. claude -p는 이런 작업을 셸 스크립트와 CI에서 실행하는 비대화형 모드다. 이 글은 공식 문서를 기준으로, 결과가 매번 같은 조건에서 나오고 실패하면 스크립트가 알아챌 수 있는 형태로 만드는 순서를 정리한다.
대화형 실행과 무엇이 다른가
| 항목 | 대화형 | claude -p | claude --bare -p |
|---|---|---|---|
| 권한 확인 | 사용자에게 묻는다 | 물을 사람이 없어 허용되지 않은 호출은 거부 | 같음 |
| CLAUDE.md·훅·스킬·MCP 자동 로드 | 로드 | 로드(신뢰 확인 창 없이) | 로드하지 않음 |
| 인증 | 로그인 세션 | 로그인 세션 또는 API 키 | ANTHROPIC_API_KEY 등 명시적 자격 증명만 |
| 맞는 용도 | 탐색·구현 | 로컬 스크립트 | CI, 여러 머신에서 같은 결과가 필요한 작업 |
문서에서 눈여겨볼 부분은 --bare 없이 -p를 실행하면 처음 보는 폴더에서도 .claude/settings.json의 훅과 .mcp.json 서버가 신뢰 확인 없이 실행된다는 점이다. 외부 기여자의 PR 브랜치를 체크아웃한 CI에서 -p를 돌린다면 그 브랜치의 훅이 실행될 수 있다는 뜻이므로, CI에서는 --bare를 기본으로 쓴다. 문서도 스크립트·SDK 호출에 bare 모드를 권장하며, 이후 -p의 기본값이 될 예정이라고 밝히고 있다. 저장소의 .mcp.json이 어떤 조건에서 승인 없이 실행되는지는 Claude Code MCP 범위 설정에서도 다뤘다.
스크립트용 실행 명령 만들기
- 입력은 파이프로 넘긴다 — 변경분이나 로그를 stdin으로 넘기면 Claude가 Bash로 직접 읽을 필요가 없어 허용 도구를 줄일 수 있다. 파이프 입력은 10MB까지다.
- 허용 도구를 최소로 지정한다 —
--allowedTools는 권한 규칙 문법을 쓴다.Bash(git diff *)처럼 공백 +*로 접두사 일치를 지정한다. 공백이 없으면git diff-index까지 일치한다. - 권한 모드를 명시한다 — 아무것도 지정하지 않으면 기본 시작 모드가 적용되는데 이것이
auto일 수 있다. 잠긴 CI라면--permission-mode dontAsk로 허용 목록 밖의 호출을 모두 거부한다. - 출력 형식을 정한다 — 사람이 읽을 결과면 기본
text, 스크립트가 처리할 결과면json. - 비용 상한을 둔다 — CLI 레퍼런스의
--max-budget-usd로 한 번 실행의 지출 상한을 둔다.
git diff origin/main...HEAD | claude --bare -p \
"이 변경분에서 오탈자만 찾아라. 각 항목을 파일:줄 한 줄, 문제 설명 한 줄로 출력하고 그 외에는 출력하지 마라." \
--permission-mode dontAsk \
--output-format json \
--max-budget-usd 0.50 \
> typo-report.json
echo "exit=$?"
jq -r '.result' typo-report.json
jq '.total_cost_usd' typo-report.json
문서에 따르면 성공 시 종료 코드는 0, 실행 중 실패하면 0이 아닌 값이다. 잘못된 옵션은 실행 전에 stderr로 알리고, 인증 누락처럼 실행 중 생긴 실패는 결과로 stdout에 찍힌다. 따라서 스크립트는 종료 코드를 먼저 보고, 0일 때만 결과를 다음 단계로 넘긴다. json 출력의 total_cost_usd는 클라이언트 쪽 추정치이므로 실제 청구액과 다를 수 있다.
결과를 스키마로 받기
다음 단계가 사람이 아니라 프로그램이면 --json-schema로 형식을 고정한다. 결과는 structured_output 필드에 들어간다. 스키마가 올바르지 않으면 Error: --json-schema is not a valid JSON Schema로 실행 전에 종료된다.
cat build-error.txt | claude --bare -p "이 빌드 로그의 근본 원인을 분류해라" \
--permission-mode dontAsk \
--output-format json \
--json-schema '{"type":"object","properties":{"category":{"type":"string","enum":["dependency","type-error","test-failure","config","unknown"]},"file":{"type":"string"},"summary":{"type":"string"}},"required":["category","summary"]}' \
| jq '.structured_output'
분류 결과를 받아 dependency면 의존성 담당 채널로, test-failure면 PR 작성자에게 알리는 식으로 후속 처리를 나눌 수 있다. 스키마로 출력을 고정하는 일반 원리는 LLM 구조화 출력에서 다뤘다.
GitHub Actions에 넣기
# .github/workflows/typo-check.yml
name: typo-check
on: [pull_request]
permissions:
contents: read
jobs:
typo:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- run: npm install -g @anthropic-ai/claude-code
- env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
git diff origin/main...HEAD | claude --bare -p \
"오탈자만 파일:줄 형식으로 보고하라. 없으면 NONE만 출력하라." \
--permission-mode dontAsk --output-format json --max-budget-usd 0.5 > out.json
jq -r '.result' out.json
test "$(jq -r '.result' out.json)" = "NONE"
이 예는 Claude가 코드를 고치지 않고 보고만 하도록 도구를 허용하지 않았다. 수정까지 맡기려면 --allowedTools "Read,Edit"를 더하고, 결과를 PR 커밋으로 올릴지 제안 코멘트로만 남길지 팀 규칙을 먼저 정한다. 공식 GitHub Actions 연동과 리뷰 봇 구성은 GitHub Actions와 AI 코드 리뷰 봇 연동을 참고한다.
여러 단계로 이어서 실행하기
앞 단계의 맥락을 이어야 하는 작업은 세션 ID를 받아 --resume으로 이어 간다. --continue는 가장 최근 대화를 잇기 때문에 여러 작업이 동시에 도는 CI에서는 섞일 수 있다. 이어 실행하면 total_cost_usd는 이전 실행까지 합친 누적값으로 나온다.
sid=$(claude --bare -p "src/ 아래 성능 문제를 찾아라" --output-format json | jq -r '.session_id')
claude --bare -p "그중 DB 쿼리 관련 항목만 우선순위로 정렬하라" --resume "$sid" --output-format json | jq -r '.result'
대화형 세션에서 수정 직후 검증을 자동으로 붙이는 방법은 Claude Code 훅으로 자동 검증 붙이기에, 자동 승인 범위를 넓힐 때의 위험은 Claude Code Auto Mode 보안 점검에 정리했다.
초보자가 자주 실수하는 포인트
- 외부 PR을 체크아웃한 CI에서 --bare 없이 실행해 PR 브랜치의 훅이 실행되는 경우
- 권한 모드를 지정하지 않아 기본 시작 모드가 auto로 적용되는 것을 모르는 경우
- Bash(git diff*)처럼 공백 없이 써서 의도보다 넓은 명령이 허용되는 경우
- 병렬 CI 작업에서 --continue로 이어 실행해 다른 작업의 대화와 섞이는 경우
체크리스트
- CI 실행에 --bare와 명시적 API 키를 쓰는가
- 허용 도구가 작업에 필요한 최소 범위인가
- 권한 모드를 명시했는가
- 종료 코드와 결과 필드를 모두 확인하는가
- 실행당 비용 상한을 설정했는가
자주 묻는 질문
bare 모드에서 CLAUDE.md의 팀 규칙을 쓰고 싶으면요?
bare 모드는 CLAUDE.md를 자동으로 읽지 않으므로 필요한 규칙을 --append-system-prompt-file로 명시적으로 넘깁니다. 이렇게 하면 어떤 규칙이 적용됐는지 명령에서 바로 보여 재현성이 높아집니다.
실행을 중간에 멈추면 결과가 남나요?
문서에 따르면 SIGTERM으로 멈추면 종료 코드 143으로 끝나고 진행 중이던 턴의 결과는 기록되지 않습니다. 턴을 마무리하고 끝내려면 먼저 SIGINT를 보냅니다.
참고 자료 · 검증 기준
위 자료와 내용을 대조한 날짜: . 도구·서비스 정책은 이후 바뀔 수 있으므로 적용 전 공식 문서를 다시 확인하세요.
이 글은 실제 실무 환경 테스트 및 검증된 기술 자료를 바탕으로 작성되었으며, 환경 변화에 따라 지속적으로 갱신됩니다.