도메인 2: 도구 설계 및 MCP 통합 (18%) — 심화편
비중: 전체 시험의 18%
태스크: 5개 (2.1 ~ 2.5)
교차 시나리오: 고객 지원 해결 에이전트, 멀티 에이전트 연구 시스템, 개발자 생산성
핵심 기술: MCP 서버/도구/리소스,tool_choice,isError, Grep/Glob/Edit/Read/Write/Bash
이 도메인은 에이전트가 외부 세계와 상호작용하는 인터페이스를 설계하는 능력을 평가한다. 도메인 1이 “에이전트가 어떻게 생각하고 움직이는가”를 다뤘다면, 도메인 2는 “에이전트가 도구를 통해 어떻게 행동하는가”를 다룬다.
핵심 메시지는 명확하다: 도구의 품질이 에이전트의 품질을 결정한다. 아무리 정교한 오케스트레이션을 설계해도, 도구 설명이 모호하거나 에러 응답이 빈약하면 에이전트는 올바른 판단을 내릴 수 없다.
이 장은 아래 세 질문을 기준으로 읽으면 정리가 빠르다.
- 에이전트가 이 도구를 왜 선택해야 하는지 설명이 충분한가?
- 도구가 실패했을 때 에이전트가 다음 행동을 결정할 정보를 받는가?
- 각 에이전트가 한 번에 비교해야 하는 도구 수가 과도하게 많지 않은가?
Domain 2 Concept Primer
이 도메인은 단순히 “도구를 붙이는 법”을 묻지 않는다.
더 정확히는:
- 도구가 모델에게 어떻게 보이는지
- 여러 도구 사이 경계를 어떻게 나눌지
- 결과와 에러를 어떻게 구조화할지
- MCP로 그 도구들을 어떻게 연결할지
- 어떤 capability를 어디까지 노출할지
를 함께 묻는다.
특히 아래 개념 문서를 같이 보면 흐름이 훨씬 잘 잡힌다.
- Tool Interface Design: 개별 도구가 모델에게 어떻게 보이는가
- Tool Selection & Disambiguation: 여러 도구 사이의 경계를 어떻게 나눌 것인가
- Structured Outputs & Error Design: 도구 결과를 다음 행동 신호로 어떻게 설계할 것인가
- MCP Integration: 도구 생태계를 런타임에 어떻게 일관되게 연결할 것인가
- Tool Safety Boundaries: 어떤 capability를 어떤 조건 아래 노출할 것인가
즉 Domain 2의 핵심은:
도구를 많이 붙이는 것이 아니라, 모델이 도구를 올바르게 이해하고 선택하고 해석하며 안전하게 쓰게 만드는 것
이다.
2.1 — 도구 설명은 왜 이렇게 중요한가
관련 개념 문서: Tool Interface Design
도구 선택의 메커니즘
이 시험에서 반복적으로 등장하는 원칙이 있다. 모델이 어떤 도구를 사용할지 결정할 때, 가장 크게 의존하는 것은 도구 설명(tool description)이다. 시스템 프롬프트도, 함수 이름도 아니다. 설명문이 도구 선택을 좌우한다.
왜 이것이 문제를 일으킬까? 유사한 기능을 가진 두 도구가 최소한의 설명만 가지고 있으면 — 예를 들어 “고객 정보를 검색”과 “주문 정보를 검색” 수준이면 — 모델은 두 도구 사이에서 추측을 하게 된다. 입력 형식이 비슷하면 이 혼동은 더 심해진다.
실제로는 대체로 아래 순서로 판단한다고 생각하면 이해가 쉽다.
- 현재 요청과 가장 잘 맞는 입력/출력 설명을 가진 도구를 먼저 좁힌다.
- 예제 질의와 엣지 케이스 설명을 보고, 지금 상황과 얼마나 가까운지 비교한다.
- 시스템 프롬프트나 함수 이름은 이 선택을 보조하거나 왜곡할 수 있지만, 보통 도구 설명이 가장 직접적인 신호로 작동한다.
좋은 도구 설명에 들어가야 하는 것
효과적인 도구 설명에는 다음 네 가지가 반드시 포함되어야 한다:
| 요소 | 설명 | 예시 |
|---|---|---|
| 입력 형식 | 어떤 형태의 데이터를 받는지 | ”고객 이메일 주소 또는 고객 ID (CUS-XXXXX 형식)“ |
| 예제 질의 | 이 도구를 호출하는 전형적인 상황 | ”고객의 계정 상태를 확인해야 할 때 사용” |
| 엣지 케이스 | 특수한 상황에서의 동작 | ”고객 ID가 유효하지 않으면 validation 에러를 반환” |
| 유사 도구와의 경계 | 이 도구와 비슷한 다른 도구와의 차이 | ”주문 조회에는 이 도구를 사용하지 말 것 — 주문 관련은 lookup_order를 사용” |
네 번째 요소가 특히 중요하다. 도구 간 경계가 설명에 명시되어 있지 않으면, 모델은 가장 “그럴듯한” 도구를 선택하는데, 이것이 종종 틀린 선택이 된다.
도구 이름 변경과 분리
도구 설명을 개선하는 것만으로 부족할 때, 다음 단계는 도구 자체를 재설계하는 것이다.
이름 변경: analyze_content 같은 범용적인 이름은 모델에게 모호한 신호를 준다. 이것을 extract_web_results로 변경하고 웹 전용 설명을 붙이면, 모델이 이 도구를 웹 관련 작업에만 사용하도록 유도할 수 있다.
도구 분리: 하나의 범용 도구가 너무 많은 일을 하고 있다면, 목적별로 나누는 편이 효과적이다.
| 구분 | 도구 | 설명 |
|---|---|---|
| 분리 전 | analyze_document | 문서를 분석한다 |
| 분리 후 | extract_data_points | 문서에서 구조화된 데이터 포인트를 추출한다 |
| 분리 후 | summarize_content | 문서의 핵심 내용을 요약한다 |
| 분리 후 | verify_claim | 문서 속 특정 주장을 출처와 대조해 검증한다 |
각 도구가 명확한 입출력 계약을 가지면, 모델의 선택 정확도가 올라간다.
빠른 결정 규칙은 이렇다.
- 기능은 사실상 같은데 이름과 설명만 모호하면
이름 변경 + 설명 강화 - 하나의 도구가 서로 다른 목적과 출력 형식을 함께 다루고 있으면
도구 분리 - 첫 단계는 대체로 분리보다 설명 개선이다. 그래도 혼동이 남을 때 구조를 바꾼다.
시스템 프롬프트가 도구 선택을 방해하는 경우
여기서 한 가지 함정이 있다. 시스템 프롬프트에 특정 키워드에 과하게 반응하도록 쓰인 문구가 있으면, 잘 작성된 도구 설명을 무시하고 의도하지 않은 도구 연관이 생길 수 있다. 예를 들어, 시스템 프롬프트에 “고객 관련 질문은 반드시 고객 도구를 사용하라”는 지시가 있으면, “주문번호 12345를 확인해 주세요”라는 요청에서도 lookup_order 대신 get_customer를 호출할 수 있다.
따라서 도구 설명을 개선할 때는 시스템 프롬프트도 함께 검토해야 한다. 키워드 기반의 과도한 지시가 도구 설명의 효과를 상쇄하고 있지 않은지 확인해야 한다.
시험에서 이것이 어떻게 출제되는가
공식 샘플 문제 2번이 정확히 이 주제를 다룬다:
에이전트가 주문 관련 질문(“주문 #12345 확인해 주세요”)에서도
lookup_order대신get_customer를 자주 호출합니다. 두 도구 모두 최소한의 설명을 가지고 있고, 유사한 식별자 형식을 받습니다. 가장 효과적인 첫 단계는 무엇입니까?
선택지들:
- (A) 시스템 프롬프트에 5~8개의 few-shot 예시를 추가 → ✗ 토큰 오버헤드가 크고, 근본 원인을 해결하지 않음
- (B) 각 도구 설명을 확장하여 입력 형식, 예제 질의, 엣지 케이스, 유사 도구와의 경계를 포함 → ✓ 정답
- (C) 키워드 기반 라우팅 레이어를 구현 → ✗ 과도한 설계(over-engineering). LLM의 자연어 이해를 우회
- (D) 두 도구를 하나로 통합 → ✗ 유효한 선택이지만 “첫 단계”로는 과도한 노력
핵심은 (B)가 가장 적은 노력으로 가장 큰 효과를 내는 접근이라는 것이다. 도구 통합(D)은 맞는 방향일 수 있지만, “첫 단계(first step)“로서는 설명 개선이 우선이다.
여기까지가 “도구를 잘 고르게 만드는 문제”였다면, 다음은 “도구가 실패했을 때도 복구 가능하게 만드는 문제”다. 둘 다 좋은 도구 설계의 핵심이지만, 전자는 선택 정확도, 후자는 복구 가능성을 다룬다.
2.2 — 에러가 발생하면 에이전트에게 무엇을 알려줘야 하는가
관련 개념 문서: Structured Outputs & Error Design
구조화된 에러 응답의 필요성
도구가 실패했을 때 “Operation failed”라는 메시지만 반환하면 어떻게 될까? 에이전트는 왜 실패했는지, 재시도할 수 있는지, 어떤 대안이 있는지 알 수 없다. 결과적으로 에이전트는 잘못된 재시도를 하거나, 아예 포기하거나, 사용자에게 무의미한 에러 메시지를 전달하게 된다.
이것이 구조화된 에러 응답(structured error response)이 필요한 이유다.
예를 들어 아래처럼 반환하면 에이전트는 다음 행동을 꽤 정확히 결정할 수 있다.
{
"isError": true,
"errorCategory": "transient",
"isRetryable": true,
"message": "CRM API timed out after 10 seconds"
}이 정보만 있어도 에이전트는 “사용자 입력이 잘못된 것”인지, “잠시 후 다시 시도할 문제”인지를 구분할 수 있다.
MCP의 isError 플래그
MCP 도구에서 에러를 통신할 때는 isError 플래그 패턴을 사용한다. 이 플래그가 true로 설정되면, 에이전트는 해당 결과가 정상적인 출력이 아니라 에러 상황임을 구조적으로 인식할 수 있다.
에러 유형별 처리 전략
모든 에러가 같지 않다. 에이전트가 올바른 복구 결정을 내리려면, 에러 유형에 따라 다른 메타데이터를 받아야 한다.
| 에러 유형 | errorCategory | isRetryable | 에이전트의 올바른 대응 |
|---|---|---|---|
| 일시적 오류 (타임아웃, 서비스 일시 불가) | transient | true | 잠시 후 재시도 |
| 입력 검증 오류 (잘못된 형식, 누락 필드) | validation | false | 입력을 수정하여 재시도 |
| 비즈니스 규칙 위반 (환불 한도 초과, 정책 위반) | business | false | 사용자에게 이유를 설명하거나, 에스컬레이션 |
| 권한 오류 (접근 권한 부족) | permission | false | 에스컬레이션 또는 대안 경로 |
에러 응답에는 다음 세 가지가 최소한으로 포함되어야 한다:
errorCategory: 에러의 종류isRetryable: 재시도 가능 여부 (불리언)- 사람이 읽을 수 있는 설명 메시지
비즈니스 규칙 위반의 경우에는 isRetryable: false와 함께 고객에게 전달할 수 있는 설명을 포함해야 한다. 이렇게 하면 에이전트가 사용자에게 적절한 안내를 할 수 있다.
빈 결과와 접근 실패를 구분해야 하는 이유
이것은 미묘하지만 시험에서 중요하게 다뤄지는 구분이다.
| 상황 | 의미 | 에이전트의 대응 |
|---|---|---|
| 질의는 성공했으나 매칭 항목이 없음 | 유효한 빈 결과 — 시스템은 정상 작동 | ”해당 조건에 맞는 결과가 없습니다”라고 사용자에게 안내 |
| 서비스에 접근하지 못함 (타임아웃 등) | 접근 실패 — 데이터가 있을 수도 있으나 확인 불가 | 재시도하거나 대안 경로를 시도 |
이 둘을 구분하지 않으면, 에이전트는 접근 실패를 “결과가 없다”고 해석하거나, 반대로 진짜 빈 결과에 대해 불필요한 재시도를 수행하게 된다.
짧게 비교하면 이렇다.
search_orders("C123") -> []는 “찾았지만 없음”이다.search_orders("C123") -> timeout은 “있는지조차 아직 모름”이다.
시험에서는 이 둘을 같은 빈 응답으로 처리하는 선택지가 자주 오답으로 나온다.
멀티 에이전트 시스템에서의 에러 전파
서브에이전트에서 에러가 발생했을 때, 코디네이터에게 어떻게 전달해야 할까? 두 가지 안티패턴을 먼저 알아야 한다:
✗ 빈 결과를 성공으로 위장: 서브에이전트가 실패했는데 빈 결과를 isError: false로 반환하면, 코디네이터는 “이 주제에 대한 정보가 없다”고 판단한다. 복구의 기회가 완전히 사라진다.
✗ 단일 실패로 전체 워크플로 종료: 하나의 서브에이전트가 실패했다고 나머지 연구 전체를 포기하는 것은 과잉 반응이다.
✓ 올바른 접근: 서브에이전트는 일시적 오류에 대해 먼저 로컬 복구를 시도하고, 해결할 수 없는 에러만 코디네이터에게 전파한다. 이때 다음을 포함해야 한다:
- 실패 유형
- 시도한 쿼리
- 부분적으로 수집된 결과 (있는 경우)
- 가능한 대안 접근법
코디네이터는 이 정보를 바탕으로 대안 쿼리로 재시도, 다른 서브에이전트에 위임, 또는 부분 결과로 진행하고 최종 보고서에 커버리지 갭을 주석 처리하는 결정을 내릴 수 있다.
2.3 — 에이전트에게 도구를 몇 개나 줘야 하는가
관련 개념 문서: Tool Selection & Disambiguation, Tool Safety Boundaries
도구 수가 많으면 왜 문제인가
직관적으로는 에이전트에게 더 많은 도구를 주면 더 다양한 작업을 할 수 있으므로 좋을 것 같다. 그러나 현실은 정반대다. 도구가 너무 많으면 선택의 정확도가 떨어진다.
구체적인 수치로 보면, 에이전트에 18개의 도구를 부여하는 것과 4~5개만 부여하는 것 사이에 선택 신뢰도의 차이가 크다. 또한 역할 외의 도구에 접근할 수 있는 에이전트는 그 도구를 오용하는 경향이 있다. 예를 들어, 종합 에이전트에게 웹 검색 도구를 주면, 종합에 집중하지 않고 직접 검색을 시도하는 현상이 발생한다.
중요한 것은 시스템 전체에 도구가 몇 개 있느냐보다, 각 에이전트가 한 번의 판단에서 비교해야 하는 후보가 몇 개냐다. 전체 시스템에는 많은 도구가 있어도 되지만, 개별 에이전트의 선택 공간은 작게 유지하는 편이 낫다.
도구 분배의 원칙
| 원칙 | 설명 |
|---|---|
| 역할 기반 할당 | 각 에이전트가 자기 역할에 필요한 도구만 가진다 (4~5개 목표) |
| 교차 역할 도구는 제한적으로 | 자주 필요한 간단한 확인 도구(예: verify_fact)만 교차 제공 |
| 복잡한 교차 작업은 코디네이터 경유 | 종합 에이전트가 깊은 검색이 필요하면, 직접 하지 않고 코디네이터를 통해 검색 에이전트에 위임 |
| 범용 도구를 제한된 대체물로 교체 | fetch_url(범용) 대신 문서 URL만 허용하는 load_document(제한)으로 교체 |
tool_choice 설정의 실전 활용
tool_choice는 모델이 도구를 어떻게 선택하는지를 제어하는 API 파라미터다.
tool_choice | 동작 | 사용 시점 |
|---|---|---|
"auto" (기본값) | 모델이 판단. 도구를 호출하지 않고 텍스트만 반환할 수도 있음 | 일반적인 대화형 상호작용 |
"any" | 반드시 도구를 하나 이상 호출하되, 어떤 도구인지는 모델이 선택 | 문서 유형을 모르는 상태에서 여러 추출 스키마 중 하나를 사용해야 할 때 |
{"type": "tool", "name": "..."} | 반드시 지정된 특정 도구를 호출 | 특정 도구가 먼저 실행되어야 할 때 (예: 메타데이터 추출 후 보강 도구 실행) |
강제 선택의 실전 패턴: tool_choice로 extract_metadata를 강제한 첫 번째 턴에서 메타데이터를 추출하고, 그 결과를 기반으로 후속 턴에서 보강(enrichment) 도구를 호출한다. 하나의 턴에서 순서를 강제하는 것이 아니라, 턴을 나눠서 순서를 보장하는 패턴이다.
짧은 흐름으로 보면 더 명확하다.
| 턴 | 설정 | 기대 동작 |
|---|---|---|
| 1 | tool_choice = {"type":"tool","name":"extract_metadata"} | 메타데이터 추출 도구를 반드시 먼저 호출 |
| 2 | tool_choice = "auto" 또는 "any" | 1턴 결과를 보고 보강 도구나 검증 도구를 선택 |
핵심은 tool_choice가 한 턴의 선택을 제어한다는 점이다. 여러 단계의 순서를 보장하려면, 한 턴 안에서 억지로 해결하지 말고 턴을 분리해야 한다.
시험에서 이것이 어떻게 출제되는가
공식 샘플 문제 9번이 도구 분배와 교차 역할 도구를 다룬다:
종합 에이전트가 주장을 검증하기 위해 코디네이터에게 돌아가고, 코디네이터가 웹 검색 에이전트를 호출하고, 다시 종합 에이전트를 호출하는 과정이 태스크당 2~3회 반복됩니다. 지연이 40% 증가했습니다. 검증의 85%는 단순한 사실 확인이고, 15%는 깊은 조사가 필요합니다.
정답은 (A) 종합 에이전트에 범위가 제한된 verify_fact 도구를 부여하고, 복잡한 검증은 기존대로 코디네이터를 경유하는 것이다. 이것이 **최소 권한 원칙(principle of least privilege)**의 적용이다 — 85%의 일반적 경우를 위한 간단한 도구를 제공하되, 기존 구조는 유지한다.
✗ (C) “종합 에이전트에 모든 웹 검색 도구를 부여”는 역할 분리(separation of concerns)를 위반하므로 오답이다.
2.4 — MCP 서버를 어떻게 설정하는가
관련 개념 문서: MCP Integration
프로젝트 설정 vs 사용자 설정
MCP 서버 설정은 두 가지 범위로 나뉜다. 이 구분은 시험에서 직접적인 함정으로 등장한다.
| 범위 | 파일 | VCS로 공유? | 용도 |
|---|---|---|---|
| 프로젝트 | .mcp.json | ✓ 예 | 팀 전체가 사용하는 공식 통합 |
| 사용자 | ~/.claude.json | ✗ 아니오 | 개인 실험, 테스트용 서버 |
환경 변수 확장: .mcp.json에서 ${GITHUB_TOKEN} 같은 환경 변수를 사용할 수 있다. 이렇게 하면 인증 토큰을 코드에 하드코딩하지 않고도 팀 전체에서 같은 설정 파일을 공유할 수 있다. 비밀 정보를 버전 관리 시스템에 커밋하는 것은 절대 해서는 안 된다.
빠르게 고르는 기준은 간단하다.
- 팀이 같이 써야 하고 저장소와 함께 재현돼야 하면
.mcp.json - 내 로컬 실험이나 임시 서버면
~/.claude.json
도구 발견 시점
설정된 모든 MCP 서버의 도구는 연결 시점(connection time)에 발견되어 즉시 사용 가능하다. 즉, 에이전트가 특정 MCP 서버의 도구를 사용하기 위해 별도의 로딩이나 활성화 과정을 거칠 필요가 없다 — 설정되어 있으면 자동으로 사용 가능하다.
MCP Resources: 탐색적 도구 호출을 줄이는 방법
MCP Resources는 읽기 전용 콘텐츠 카탈로그를 에이전트에게 노출하는 메커니즘이다. 도구(tool)가 “행동을 수행하는 것”이라면, 리소스(resource)는 “정보를 조회하는 것”이다.
리소스가 해결하는 문제는 이것이다: 리소스 없이는 에이전트가 “어떤 이슈가 있는지”, “어떤 문서가 있는지”, “데이터베이스 스키마가 어떻게 생겼는지”를 알기 위해 탐색적인 도구 호출을 반복해야 한다. 리소스가 이슈 요약, 문서 계층 구조, 데이터베이스 스키마 같은 카탈로그를 미리 노출하면, 에이전트는 불필요한 탐색 없이 바로 적절한 도구를 호출할 수 있다.
| 구분 | 도구 | 리소스 |
|---|---|---|
| 목적 | 행동 수행 | 정보 조회 |
| 예시 | 이슈 생성, 코멘트 작성, DB 조회 실행 | 이슈 목록, 문서 트리, 스키마 카탈로그 |
| 언제 우선하나 | 실제 작업을 실행해야 할 때 | 먼저 뭐가 있는지 파악해야 할 때 |
커뮤니티 서버 vs 커스텀 구현
표준적인 외부 서비스 통합(Jira, GitHub, Slack 등)에는 이미 존재하는 커뮤니티 MCP 서버를 우선 사용하는 것이 권장된다. 커스텀 서버는 팀 고유의 워크플로에 한정하여 구현해야 한다. 이미 잘 작동하는 커뮤니티 서버를 처음부터 다시 만드는 것은 불필요한 노력이다.
MCP 도구 설명 강화
한 가지 실전 문제가 있다. MCP 서버의 도구 설명이 빈약하면, 에이전트가 MCP 도구 대신 빌트인 도구(Grep, Read 등)를 선호하는 경향이 생긴다. 빌트인 도구의 설명은 이미 잘 정의되어 있기 때문이다.
이 문제의 해결책은 MCP 도구의 설명을 빌트인 도구 수준으로 상세하게 강화하는 것이다. 도구가 무엇을 할 수 있는지, 어떤 출력을 반환하는지를 명확히 기술하면, 에이전트가 MCP 도구를 적절히 선택하게 된다.
2.5 — 빌트인 도구를 올바르게 선택하는 법
관련 개념 문서: Tool Interface Design, Tool Selection & Disambiguation, Tool Safety Boundaries
6가지 빌트인 도구
Claude Code에는 기본으로 제공되는 도구들이 있다. 각 도구의 용도를 정확히 알고, 상황에 맞는 도구를 선택하는 것이 시험에서 평가된다.
여기서도 빠른 선택 기준이 있다. 로컬 코드베이스 탐색, 읽기, 수정처럼 작업 대상이 현재 워크스페이스 안에 있으면 빌트인 도구가 우선이다. 반대로 GitHub, Jira, 사내 DB처럼 외부 시스템의 의미 있는 데이터나 동작이 필요하면 MCP 도구가 우선이다. 둘 다 가능해 보일 때는, 더 구체적인 설명과 더 제한된 권한 범위를 가진 쪽을 먼저 고른다.
| 도구 | 하는 일 | 언제 쓰는가 | 언제 쓰지 않는가 |
|---|---|---|---|
| Grep | 파일 내용에서 패턴을 검색 | 함수명, 에러 메시지, import 문, 특정 문자열을 코드베이스 전체에서 찾을 때 | 파일 이름을 찾을 때 (→ Glob) |
| Glob | 파일 경로와 이름을 패턴으로 매칭 | 특정 확장자의 파일을 찾을 때 (예: **/*.test.tsx) | 파일 내용을 검색할 때 (→ Grep) |
| Edit | 고유한 텍스트 매칭을 이용한 대상 수정 | 정밀한 코드 변경. 수정할 위치를 고유하게 식별할 수 있을 때 | 고유한 앵커 텍스트가 없을 때 (→ Read + Write) |
| Read | 파일 전체 내용을 읽기 | Edit의 앵커가 불분명할 때, 파일 구조를 이해할 때 | 대규모 코드베이스의 모든 파일을 한꺼번에 읽을 때 |
| Write | 파일 전체를 (새로) 쓰기 | Read로 읽은 후 전체를 수정해야 할 때, Edit가 실패했을 때 | 부분 수정이 가능한 경우 (→ Edit 우선) |
| Bash | 셸 명령을 실행 | 테스트 실행, 빌드, 스크립트 실행 | 파일 내용 검색 (→ Grep이 더 효율적) |
Grep과 Glob의 구분
이 둘의 혼동은 흔한 실수다. 기억하는 방법은 간단하다:
-
Grep = 파일 안을 본다 (content search)
-
Glob = 파일 이름을 본다 (path matching)
-
“getUserById 함수를 호출하는 모든 곳을 찾아라” →
Grep -
“모든 테스트 파일을 찾아라 (
*.test.tsx)” →Glob
Edit가 실패할 때
Edit 도구는 고유한 텍스트 매칭을 기반으로 작동한다. 수정할 위치를 정확히 하나로 식별할 수 있어야 한다. 동일한 텍스트가 파일 내에 여러 번 존재하면 Edit는 어디를 수정해야 할지 결정할 수 없어 실패한다.
이때의 대안은 Read로 전체 파일을 읽고, 필요한 수정을 적용한 후, Write로 전체 파일을 다시 쓰는 것이다.
코드베이스를 점진적으로 이해하는 패턴
대규모 코드베이스를 탐색할 때 가장 흔한 실수는 모든 파일을 한꺼번에 Read로 읽으려는 것이다. 이것은 문맥 창을 빠르게 소진시키고, “중간 부분 손실” 효과로 인해 정보의 일부를 놓치게 만든다.
올바른 접근은 점진적으로 이해를 넓혀 가는 것이다.
Grep으로 진입점을 찾는다. 예:grep "export function"→ 주요 API 엔드포인트 식별Read로 진입점 파일을 읽고import를 추적한다. 예:read src/api/orders.ts→OrderServiceimport 확인Grep으로 의존 모듈에서 관련 함수를 찾는다. 예:grep "class OrderService"→ 구현체 위치 확인- 필요한 깊이까지 같은 패턴을 반복한다.
래퍼 모듈을 통한 함수 추적
실전에서 자주 마주치는 패턴이 하나 더 있다. 코드베이스에서 특정 함수의 사용처를 추적할 때, 해당 함수가 **래퍼 모듈(wrapper module)**을 통해 재수출(re-export)되는 경우가 있다. 이때는 단순히 함수 이름을 Grep하는 것만으로는 부족하다.
올바른 접근:
- 먼저 래퍼 모듈의 모든 **내보낸 이름(exported names)**을 식별한다
- 각 내보낸 이름을 코드베이스 전체에서 Grep으로 검색한다
이렇게 하면 직접 호출과 래퍼를 통한 간접 호출을 모두 찾을 수 있다.
자주 나오는 오답 패턴 정리
| # | 안티패턴 | 왜 틀린가 | 올바른 접근 |
|---|---|---|---|
| 1 | 도구 설명이 겹칠 때 첫 단계로 도구 통합 | 통합은 설명 개선보다 더 큰 노력이 필요 | 먼저 설명을 개선하고, 그래도 부족하면 통합 |
| 2 | 일반적인 “Operation failed” 에러 반환 | 에이전트가 에러 유형을 알 수 없어 복구 불가 | errorCategory, isRetryable, 설명을 포함한 구조화된 에러 |
| 3 | 모든 에이전트에 모든 도구 부여 | 18개 도구는 선택 정확도를 저하시키고 오용 유발 | 역할별 4~5개만 할당 |
| 4 | 빈 결과를 성공으로 위장하여 반환 | 코디네이터가 복구할 기회를 완전히 차단 | 실패 유형 + 시도한 내용 + 부분 결과를 포함한 에러 반환 |
| 5 | 모든 파일을 Read로 한꺼번에 읽기 | 문맥 창 소진 + 중간 부분 손실 | Grep → Read → Grep의 점진적 이해 패턴 |
| 6 | MCP 도구 대신 빌트인 도구만 사용 | MCP 도구가 더 적합한 경우에도 빌트인이 선호됨 | MCP 도구 설명을 빌트인 수준으로 강화 |
직접 해보기
연습: MCP 도구 설계와 에러 처리
-
유사한 기능을 가진 3개의 MCP 도구를 정의한다. 의도적으로 최소한의 설명만 달고 테스트 → 에이전트가 도구를 혼동하는지 확인 → 설명을 상세하게 개선한 후 선택 정확도 변화를 비교한다.
-
4가지 에러 유형(transient/validation/business/permission)에 대한 구조화된 에러 응답을 구현한다. 각 유형에 대해 에이전트가 적절히 대응하는지 테스트한다 — 일시적 에러는 재시도, 비즈니스 에러는 사용자에게 설명, 권한 에러는 에스컬레이션.
-
빈 결과와 접근 실패를 구분하는 로직을 구현한다. “매칭 항목 없음”과 “서비스 타임아웃”이 동일한 빈 응답으로 처리되지 않도록 구조를 설계한다.
-
.mcp.json에 프로젝트 레벨 MCP 서버를 설정하고,~/.claude.json에 실험용 서버를 추가한다. 환경 변수 확장(${GITHUB_TOKEN})을 사용하여 인증을 처리한다. 두 서버의 도구가 동시에 사용 가능한지 확인한다. -
Grep → Read → Grep 패턴으로 낯선 코드베이스를 탐색한다. 진입점을 찾고, import를 추적하고, 의존 모듈에서 관련 함수를 찾는 과정을 실습한다.
참고 링크
| 주제 | 링크 |
|---|---|
| 도구 사용 기본 | Tool Use with Claude — Anthropic Docs |
| 에이전트를 위한 도구 작성법 | Writing Tools for Agents — Anthropic Engineering |
| 고급 도구 사용 패턴 | Advanced Tool Use — Anthropic Engineering |
| MCP 아키텍처 | MCP Architecture — Model Context Protocol |
| MCP 발표 | Model Context Protocol — Anthropic |
| 문맥 공학 (도구 문맥 포함) | Effective Context Engineering — Anthropic Engineering |
| 공식 시험 가이드 PDF | CCAF Exam Guide |