이 기사는 playpark Blog에서 전재되었습니다.
- Claude Code Skills에서 에러 출력을 JSON 형식으로 통일하는 이유
- exit code만 사용 / 로그 파일 기록 / 구조화된 출력(Structured Output)의 비교와 선택 기준
- 어떤 상황에서 이 설계를 채택해야 하는가
Claude Code Skills가 늘어나면, "스크립트가 실패했는데도 정상으로 처리되는" 문제가 발생합니다.
AI 에이전트는 스크립트의 출력을 해석하여 다음 행동을 결정합니다. 이때 에러 정보가 구조화되어 있지 않으면:
- exit code가 1이라도 "무엇이", "어디서" 실패했는지 알 수 없음
- 에이전트가 잘못된 판단을 내려 디버깅이 길어짐
- 다른 Skill에서 호출될 경우, 상위 단계에서 정확한 에러 이유를 파악할 수 없음
"에러는 발생했지만, 이유는 알 수 없는" 상황은 인간에게도 시간을 소모하게 만들지만, AI 에이전트에게는 구조화된 데이터가 없으면 말 그대로 해석이 불가능합니다.
| 접근 방식 | 장점 | 단점 |
|---|
| exit code만 사용 | 구현이 가장 단순함 | 실패 이유를 알 수 없음, 에이전트 측에서 해석 불가 |
| 로그 파일에 기록 | 상세 로그를 남길 수 있음 | 실시간 파악이 어려움, 파일 경로 관리가 필요함 |
| stderr에 JSON 출력 (채택) | 구조화되어 있어 에이전트가 해석 가능, stdout과 분리 가능 | 헬퍼 함수(Helper Function)가 필요함 |
Claude Code의 에이전트는 stdout을 데이터로, stderr를 에러 정보로 구분하여 처리할 수 있습니다. JSON으로 출력함으로써, SKILL.md 측에서 jq를 사용하여 실패 이유를 추출하고, 다음 행동(재시도할지, 다른 접근 방식을 시도할지)을 판단할 수 있습니다.
exit code만으로는 "실패했다"라는 사실만 알 수 있습니다. JSON화하면 "어떤 에러로", "어떤 exit code로" 실패했는지를 즉시 파악할 수 있습니다. 이것이 에이전트 간의 신뢰할 수 있는 통신 프로토콜이 됩니다.
공통 라이브러리에 die_json 헬퍼 함수를 정의합니다:
die_json() {
local msg="$1"
local code="${2:-1}"
...
입력 검증(Input Validation)도 이 패턴으로 통일합니다:
VALID_CATEGORIES="logic state external environment"
local valid=false
for v in $VALID_CATEGORIES; do
...
상태 파일의 초기화에서는 jq -n을 사용하여 스키마를 확정합니다:
jq -n \
--arg target "$target" \
--arg scope "$scope" \
...
version 필드를 넣는 이유는 스키마 변경 시 호출 측에서 "이것은 구형 포맷이다"라고 감지할 수 있도록 하기 위함입니다.
Skill이 다른 Skill로부터 호출되는 구성이거나, AI 에이전트가 스크립트 출력을 해석하여 판단을 내리는 구성이라면, JSON화는 빠른 단계에서 도입할 가치가 있습니다.
Skill이 단독으로 완결되어 인간이 로그를 확인하는 운용 방식이라면 exit code만으로도 충분합니다. 다만, Skill이 10개를 넘어서며 상호 호출이 증가하는 시점이 이 설계를 도입할 임계치(Threshold)의 기준입니다.
이 기사에서는 JSON 계약을 통한 타입 안전성(Type Safety)의 선정 이유를 해설했습니다.
:page_facing_up: :page_facing_up:
【Claude Code】Skills 설계 패턴 상급편 - 타입 안전성·에러 핸들링·Skill 간 연계 구현 에서는 더욱 상세하게:
- 페이즈별 Auto-Retry 프로토콜 (8개 페이즈별 재시도 전략과 "맹목적 재시도 금지"의 설계 판단)
- 3층 컨피그 머지 (Global 설정과 Project 설정을
jq로 deep merge하는 구현)
- Agent Team 병렬 협조 상태 관리 패턴 (단일 라이터 설계와 크로스 도메인 탐지)
를 다루고 있습니다.
playpark LLC - 업무 자동화·AI 활용·Web 개발