2분

CI 실패용 Claude Code: 작은 수정과 테스트를 위한 프롬프트들

CI 실패를 해결할 때 Claude Code에게 실패 출력을 인용하게 하고, 가장 작은 수정과 반복 방지를 위한 회귀 테스트를 제안하게 하는 방법.

CI 실패용 Claude Code: 작은 수정과 테스트를 위한 프롬프트들

CI가 실패할 때 무엇이 잘못되는가와 AI의 추측 문제

CI 실패는 보통 신비롭지 않습니다. 로그는 어디서 멈췄는지, 어떤 명령이 실패했는지, 에러 메시지를 알려줍니다. 좋은 로그라면 스택 트레이스, 파일과 라인 번호가 있는 컴파일러 오류, 또는 어떤 어설션이 실패했는지 보여 주는 테스트 보고서가 있습니다. 때로는 "expected X, got Y"처럼 diff 스타일의 단서나 "lint", "build", "migrate database" 같은 명백한 실패 스텝이 나오기도 합니다.

문제는 사람들이(그리고 AI도) 로그를 배경 소음처럼 취급한다는 점입니다. 긴 로그를 붙여넣고 "수정해줘"라고 하면 많은 모델이 마지막 의미 있는 라인을 읽기보다 익숙한 설명으로 바로 점프합니다. 오류가 흔해 보일수록(예: "module not found", "timeout", "permission denied") 추측은 더 심해집니다. 그 결과 실제 실패와 맞지 않는 대대적인 리팩터, 새 의존성 추가, 또는 "모두 업데이트해봐" 같은 답변이 나오곤 합니다.

목표는 "어떻게든 통과시키기"가 아닙니다. 더 단순합니다:

  • 실패 출력을 읽어라.
  • 실패한 스텝을 통과하게 하는 가장 작은 변경을 식별하라.
  • 나머지는 그대로 유지하라.

실무에서는 "최소 수정"이 보통 다음 중 하나입니다: 한 곳의 몇 줄짜리 코드 변경, 빠진 import나 잘못된 경로, CI 환경에서 명백히 잘못된 설정값, 또는 재설계 대신 우발적으로 도입된 깨진 변경을 되돌리는 것.

후속 테스트도 중요합니다. CI가 한 번 통과했다고 반복을 막는 것은 아닙니다. 실패가 엣지케이스(null 입력, 타임존, 반올림, 권한)에서 왔다면, 수정 전에는 실패하고 수정 후에는 통과하는 회귀 테스트를 추가하세요. 그렇게 하면 일회성 구조 복구가 아니라 방어선이 됩니다.

도움을 요청하기 전에 모아야 할 것

대부분의 나쁜 수정은 맥락 부족에서 시작합니다. 마지막 빨간 줄만 붙여넣으면 모델은 앞에서 무슨 일이 있었는지 추측해야 하고, 그 추측이 리팩터로 이어지기 쉽습니다.

실패를 처음 실제 오류부터 끝까지 따라갈 수 있을 만큼 충분한 세부사항을 제공하세요. 가능한 한 그대로 붙여넣는 항목:

  • 첫 오류 라인부터 끝까지의 전체 실패 로그(단지 마지막 스택 트레이스만이 아님).
  • CI가 실행한 정확한 명령(예: go test ./..., npm test, flutter test, golangci-lint run).
  • 오류에 언급된 파일 경로들 및 관련 설정(test config, linter config, 빌드 스크립트).
  • 최근에 무엇이 바뀌었는지: PR diff 요약, 의존성 업그레이드, CI 설정 편집.
  • 플래키 여부: 가능하면 실패/성공이 섞인 실행 로그들.

제약을 평문으로 추가하세요. 아주 작은 수정만 원하면 분명히 말하세요: 리팩터 금지, 동작 변경 금지(필요하지 않은 한), 패치는 실패한 영역으로 제한하라.

간단한 예: 의존성 업그레이드 후 lint 단계에서 CI가 실패한다면, 첫 경고부터 시작하는 lint 출력을 붙이고 CI가 사용한 명령과 단일 패키지 버전 변경만 적어 두세요. 그러면 한 줄의 설정 수정이나 작은 코드 변경을 제안하는 수준이면 충분합니다.

복사·붙여넣기 가능한 구조는 보통 충분합니다:

CI command:

Failing output (full):

Recent changes:

Constraints (smallest fix, no refactor):

Flaky? (runs attached):

실패 출력을 읽게 만드는 프롬프트 규칙

모델이 CI 실패에서 빗나갈 때, 보통 프롬프트가 추측을 허용하기 때문입니다. 모델이 정확한 실패 출력을 사용해 작업 과정을 보여주고, 실패를 통과시키는 가장 작은 변경에 전념하게 만드세요.

모델을 정직하게 만드는 규칙

증거와 작은 계획을 요구하세요. 좋은 프롬프트는 다섯 가지를 강제합니다:

  • CI 로그의 정확한 실패 라인(오류, 스택 트레이스, file:line)을 인용하고 "나는 이 라인들을 사용하고 있다"고 명시하게 하세요.
  • 한 문장 진단을 제시하게 하세요. 모호함을 피하세요.
  • 정확히 어떤 파일을 건드릴지 이름을 대며 1–3개의 편집으로 이루어진 최소 패치 계획을 제안하게 하세요.
  • 관련 없는 변경(포맷, 이름 변경, 리팩터, 의존성 업그레이드)은 피하게 하세요. 당신이 승인한 경우만 허용하세요.
  • 불확실한 부분과 진단을 확증할 하나의 추가 정보를 나열하게 하세요.

불확실성은 괜찮지만, 숨겨진 불확실성은 시간을 낭비합니다.

바로 붙여넣을 수 있는 프롬프트 단편

질문 맨 위에 다음을 붙여넣으세요:

Use ONLY the evidence in the CI output below.
1) Quote the exact failing lines you are using.
2) Give ONE sentence: the most likely cause.
3) Propose the smallest fix: 1-3 edits, with file paths.
4) Do NOT do formatting/renames/refactors or "cleanup".
5) List uncertainties + the one extra detail that would confirm the diagnosis.

로그에 "expected 200, got 500"와 user_service.go:142로 들어가는 스택 트레이스가 있다면, 이 구조는 응답을 해당 함수와 작은 가드나 오류 처리 변경 쪽으로 밀어냅니다. 재설계가 아니라 작은 수정에 집중시키는 효과가 있습니다.

CI 실패를 위한 복사·붙여넣기 프롬프트 템플릿

가장 빠른 승리는 로그 인용을 강제하고 제약을 지키며 정보가 부족할 때 멈추는 프롬프트에서 옵니다.

You are helping me fix a CI failure.

Repo context (short):
- Language/framework:
- Test/build command that failed: <PASTE THE EXACT COMMAND>
- CI environment (OS, Node/Go/Python versions, etc.):

Failing output (verbatim, include the first error and 20 lines above it):
<PASTE LOG>

Constraints:
- Propose the smallest possible code change that makes CI pass.
- Do NOT rewrite/refactor unrelated code.
- Do NOT touch files you do not need for the fix.
- If behavior changes, make it explicit and justify why it is correct.

Stop rule (no guessing):
- If the log is incomplete or you need more info (missing stack trace, config, versions, failing test name), STOP and ask only the minimum questions needed.

Your response format (follow exactly):
1) Evidence: Quote the exact log lines that matter.
2) Hypothesis: Explain the most likely cause in 2-4 sentences.
3) Smallest fix: Describe the minimal change and why it addresses the evidence.
4) Patch: Provide a unified diff.
5) Follow-up: Tell me the exact command(s) to rerun locally to confirm.

Then, write ONE regression test (or tweak an existing one) that would fail before this fix and pass after it, to prevent the same failure class.
- Keep the test focused. No broad test suites.
- If a test is not feasible, explain why and propose the next-best guardrail (lint rule, type check, assertion).

두 가지 세부사항이 불필요한 왕복을 줄입니다:

  • 정확한 실패 명령과 첫 번째 오류를 포함하세요(최종 요약만이 아님).
  • 실패가 여러 개인 경우 먼저 고칠 것을 지시하세요(보통 로그에서 가장 이른 실제 실패).

최소 수정으로 유도하는 방법

최소 변경을 계획하세요
코드를 건드리기 전에 1–3개의 목표 편집을 정리하고, 그 변경만 실행하세요.

시간을 낭비하는 가장 빠른 방법은 한 번에 다섯 군데를 바꾸는 "정리" 패치를 받아들이는 것입니다. "최소"의 정의를 미리 하세요: 실패 스텝을 통과하게 하는 가장 작은 diff, 위험이 낮고 검증이 빠른 것.

간단한 규칙이 잘 작동합니다: 증상 먼저 고치고, 그다음에 더 넓은 리팩터가 필요한지 결정하세요. 로그가 한 파일, 한 함수, 빠진 import, 또는 하나의 엣지케이스를 가리키면 그곳을 목표로 하세요. "여기 있는 김에" 식의 변경은 피하세요.

정말로 대안을 원하면 두 개만 요구하세요: "가장 안전한 최소 수정" 대 "가장 빠른 최소 수정". 트레이드오프만 원하지 메뉴를 원하지 않습니다.

또한 로컬 검증이 CI와 동일하도록 요구하세요. 파이프라인이 실행한 동일한 명령(또는 가장 비슷한 명령)을 요구하면 몇 분 안에 확인할 수 있습니다:

# run the same unit test target CI runs
make test
# or the exact script used in CI
npm test

응답이 대규모 변경을 제안하면 다음과 같이 되물으세요: "관련 없는 포맷팅/이름 변경 없이 실패한 어설션을 고치는 가장 작은 패치를 보여달라."

자주 묻는 질문

Where in a CI log should I look first when a job fails?

먼저 실제 오류(최종 exit 1이 아닌)를 보세요.

  • 무엇이 실패했는지 보여주는 가장 이른 라인(테스트 이름, 파일:라인, 명령)을 찾으세요.
  • 그 오류의 설정/문맥을 이해하기 위해 그 위 20–40줄을 읽으세요.
  • 첫 번째 실패를 고치기 전에는 그 이후에 연쇄적으로 뜨는 오류들은 무시하세요.
How do I stop an AI from guessing and giving a generic fix?

로그를 읽었다는 증명을 요구하세요.

다음 같은 제약을 사용하세요:

  • “사용 중인 정확한 실패 라인을 인용하라.”
  • “한 문장 진단을 제시하라.”
  • “최소 수정: 1–3개의 편집과 정확한 파일 경로를 제시하라.”
  • “로그가 불완전하면 멈추고 필요한 최소 질문만 하라.”
What does “smallest fix” actually mean for a CI failure?

실패 스텝을 성공하게 하는 가장 작은 패치를 기본으로 하세요.

보통 다음 중 하나입니다:

  • 한 군데의 목표 코드 변경(가드 클로즈, 잘못된 import/경로 수정).
  • CI 전용의 설정값 조정.
  • 재설계 대신 깨진 변경을 되돌리기.

CI가 다시 통과할 때까지 정리(cleanup) 변경은 피하세요.

What should I include when I ask for help with a failing CI run?

실패를 재현할 수 있을 만큼의 문맥을 붙이세요. 마지막 빨간 줄 하나만 붙이면 부족합니다.

포함할 것:

  • 정확한 CI 명령(go test ./..., npm test, flutter test 등).
  • 첫 오류 라인부터 끝까지의 로그.
  • 오류에 나온 파일 경로들.
  • 최근에 무엇이 변경되었는지(의존성 업데이트, CI 설정 편집, PR 요약).
  • 플래키인지 여부(여러 번 실패와 한 번의 성공이 섞여 있다면).
Can I explicitly tell the model not to refactor or reformat anything?

네—제약을 평문으로 적고 반복하세요.

예시 제약:

  • “리팩터, 이름 변경, 포맷팅, 의존성 업데이트 금지.”
  • “수정에 필요한 파일만 건드려라.”
  • “동작이 바뀌면 무엇이 바뀌는지 정확히 명시하고 이유를 설명하라.”

이렇게 하면 응답이 집중되고 검토하기 쉬워집니다.

If CI shows multiple failures, which one do I fix first?

가장 이른 실제 실패를 먼저 고치세요.

  • 이후의 실패들은 종종 첫 번째 문제 때문에 발생합니다(예: 빌드 실패 → 테스트/린트가 제대로 실행되지 않음).
  • 오류들이 독립적이면 차단에 가까운 것(보통 빌드/린트)을 먼저 고르세요.

모르겠다면 모델에게 로그에서 첫 실패 스텝을 식별하라고 시키고 그 부분만 고치게 하세요.

How can I tell if a CI failure is flaky, and what should I do?

플래키함은 재시도(retry)를 추가할 신호가 아닙니다. 무작위를 제거하세요.

일반적인 안정화 방법:

  • 시간을 고정(Clock 주입, 고정 타임스탬프).
  • RNG에 시드 설정.
  • 네트워크 호출을 모킹/스텁 처리.
  • 격리된 임시 디렉터리와 고유 포트 사용.

결정성이 확보되면 ‘가장 작은 수정’이 명확해집니다.

What’s the best way to verify the fix matches CI and isn’t a lucky pass?

CI가 실행한 정확한 명령을 물어보고, 그 명령을 로컬에서 그대로 실행하세요.

  • CI와 동일한 명령과 플래그.
  • 가능한 경우 핵심 런타임 버전(Go/Node/Flutter, OS)을 맞추세요.

로컬 재현이 어렵다면 동일한 오류를 일으키는 최소한의 재현(target/test)을 리포에서 요청하세요.

What makes a good follow-up test after fixing a CI failure?

수정 전에는 실패하고 수정 후에는 통과하는, 단 하나의 집중된 회귀 테스트를 작성하세요.

좋은 대상:

  • 문제를 일으킨 엣지케이스(null 입력, 타임존, 반올림, 권한).
  • 더 넓은 오류 클래스(예: 하나의 잘못된 ID가 아니라 여러 잘못된 ID)를 포괄하는 테스트.

린트/빌드 실패라면 동일한 효과를 주는 규칙 강화나 체크 추가가 대안일 수 있습니다.

How do I iterate quickly without turning my repo into a mess while debugging CI?

스냅샷/롤백을 이용해 실험을 되돌릴 수 있게 하세요.

실용적인 루프:

  • 가장 작은 변경을 시도한다.
  • 정확히 실패하던 명령을 실행한다.
  • 여전히 실패하면 롤백하거나 스냅샷으로 되돌려 다른 작은 패치를 시도한다.

Koder.ai처럼 스냅샷을 제공하는 환경이면 실험 편집이 최종 패치에 섞이지 않도록 도와줍니다.

Related posts