Aller au contenu principal
William Balance
← 블로그 목록

Claude Agent SDK — 운영에서 살아남는 오케스트레이션 패턴

게시일 2026년 4월 22일 · 5분 읽기
  • Claude
  • Agents
  • MCP
  • TypeScript
  • Orchestration

지난 1년 동안 “에이전트(agent)“는 만능 단어가 됐습니다. 현장에서 보면 에이전트라고 부르는 것의 90%는 LLM 호출을 둘러싼 while 루프입니다. 본질적으로 문제는 아닙니다 — 운영 환경 빼고는요. 거기서는 모든 지름길이 비용으로 돌아옵니다. B2B 클라이언트들에 Claude Agent SDK를 여러 번 도입하면서 얻은 교훈을 정리합니다.

자체 루프 대신 SDK를 쓰는 이유

while 루프 + tool_use 조합은 범위가 좁을 때만 버팁니다. 도구 3개, 턴 2번, 타임아웃 하나. 그 이상으로 넘어가면 컨텍스트 관리, 압축(compaction), 토큰 예산, 도구별 재시도, 구조화 로깅을 손으로 다시 만들게 됩니다. SDK는 이걸 기본으로 주고, 무엇보다 사고방식을 강제합니다. 서브 에이전트(sub-agents), 도구 권한(tool permissions), 세션(sessions) 단위로 생각하게 됩니다. “API 다시 호출해야지”가 아니라요.

진짜 이득은 성능이 아닙니다. 운영 가독성입니다. 새벽 2시에 에이전트가 죽었을 때, 어떤 도구가 어떤 입력으로, 어느 세션에서, 모델은 왜 그걸 호출했는지 알 수 있어야 합니다. 이 최소한이 없으면 디버깅은 점치기가 됩니다.

운영에서 살아남은 세 가지 패턴

도구 예산이 정해진 단일 에이전트

가장 과소평가된 패턴입니다. 에이전트 1개, 도구는 최대 3~5개, 명시적인 턴 예산. 대부분의 비즈니스 케이스(추출, 검색 결합 분류, 한정된 어시스턴트)에는 이걸로 충분합니다 — 더 정교한 구조보다 낫습니다.

import { query } from "@anthropic-ai/claude-agent-sdk";

const result = await query({
  prompt: userRequest,
  options: {
    model: "claude-sonnet-4-6",
    maxTurns: 6,
    allowedTools: ["search_crm", "read_contract", "create_ticket"],
    systemPrompt: TICKET_AGENT_PROMPT,
  },
});

maxTurns는 과한 게 아닙니다. 이게 없으면 도구 에러에 걸려 무한 루프 도는 에이전트가 조용히 토큰 200k를 태웁니다. 청구서로 본 적 있습니다.

오케스트레이터 + 팬아웃 서브 에이전트

작업이 독립적인 하위 작업으로 쪼개지는 순간, 오케스트레이터(orchestrator) 패턴이 수지가 맞기 시작합니다. 메인 에이전트가 전문화된 서브 에이전트들에게 위임하고, 각자 자기 컨텍스트와 도구를 가집니다. 이득은 병렬화가 아닙니다. 컨텍스트 격리(context isolation) 입니다. 문서 50페이지를 통째로 삼킨 서브 에이전트가 오케스트레이터의 추론을 오염시키지 않습니다.

제 기준: 두 하위 작업이 중간 컨텍스트를 공유할 필요가 없다면, 두 개의 서브 에이전트로 분리합니다. 아니면 컨텍스트 비용을 두 번 내고 모델 추론도 흐려집니다.

결정적 지점의 휴먼 인 더 루프(human-in-the-loop)

비즈니스 시스템에 쓰기를 하는 모든 작업 — 이메일 발송, 인보이스 생성, CRM 수정 — 은 명시적인 사람 확인을 거칩니다. “사용자가 나중에 취소할 수 있다”가 아니라요. SDK는 도구별 권한 훅을 노출하는데, 저는 무조건 씁니다.

const result = await query({
  prompt: userRequest,
  options: {
    allowedTools: ["draft_email", "send_email"],
    canUseTool: async (toolName, input) => {
      if (toolName === "send_email") {
        return await askUserConfirmation(input);
      }
      return { behavior: "allow", updatedInput: input };
    },
  },
});

엉뚱한 메일을 클라이언트에게 보내는 에이전트는 한바탕 욕먹을 일입니다. 초안만 띄워두고 확인을 기다리는 에이전트는 생산성 향상입니다. 이 차이는 코드 10줄입니다.

MCP — 쓸모 있나, 거품인가

MCP(Model Context Protocol)는 단순한 케이스에서는 과대평가되고, 진짜 빛나는 단 하나의 경우에서는 과소 활용됩니다. 여러 에이전트와 여러 클라이언트가 같은 도구 인벤토리를 공유해야 할 때가 그 경우입니다.

에이전트가 Node 서비스 하나 안에서 내부 도구 3개로만 돌아간다면, TypeScript로 직접 쓰세요. 그걸 위해 MCP 서버를 두는 건 프로토타이핑을 늦추는 배관 작업입니다. 반대로 같은 도구가 Claude Code, 커스텀 에이전트, 미래의 서포트 도구에서 동시에 돌아야 한다면 MCP가 수지가 맞습니다. 기준선은 명확합니다 — 소비자가 여럿이거나 안 쓰거나.

타협 불가능한 가드레일(guardrails)

이게 없으면 출시 못 한다고 거절하는 세 가지입니다.

  1. 세션 단위 구조화 로깅. 모든 도구 호출, 입력, 출력, 지연시간, 토큰. 이게 없으면 폭주하는 에이전트를 디버그할 방법이 없습니다.
  2. 세션 단위 토큰 예산. maxTurns에 더해서 애플리케이션 쪽 하드 캡. 프롬프트 인젝션과 끈질긴 도구 루프를 막아줍니다.
  3. 권한 격리. “읽기 전용” 에이전트는 쓰기 도구에 접근 못 합니다. 설정이 복잡해져도요. SDK의 권한 트리는 이걸 위해 만들어진 겁니다 — 쓰세요.

여기에 OpenTelemetry, 사용자별 레이트 리밋, 단순 작업용 작은 모델 폴백을 더 쌓을 수 있습니다. 그래도 위 세 개는 운영 최소 요건입니다. 협상 없습니다.

어디서부터 시작할까

망설인다면:

  • 범위가 잘 잡힌 사내 워크플로우를 고르세요(티켓 분류, 리드 자격 확인, 계약서 추출 등).
  • 일단 도구 3개에 maxTurns: 6 단일 에이전트로 짜세요.
  • 실제 케이스 50~100건에서 측정하세요 — 성공률, 평균 비용, p95 지연시간.
  • 단순 패턴의 명확한 한계가 보일 때까지는 오케스트레이터 + 서브 에이전트로 넘어가지 마세요.

운영에서 깨지는 에이전트 프로젝트 대부분은 정교함이 부족해서가 아닙니다. 너무 일찍 정교해서 깨집니다. 단순한 에이전트에 제대로 된 계측, 그리고 적절한 지점에 사람을 배치한 구조는 가드레일 없는 멀티 에이전트 아키텍처를 거의 항상 이깁니다.

구체적인 유스케이스가 있는데 어떤 패턴이 맞을지 망설이고 있다면, 한 페이지로 정리해 보세요(입력, 기대 출력, 사용 가능한 도구, 에이전트가 틀렸을 때의 비즈니스 리스크). 같이 얘기해 봅시다.

진행 중인 프로젝트가 있나요?

AI든 풀스택이든, 무료 30분 동안 함께 이야기해 봅니다.