← All guides

Claude API Tool Use (함수 호출) 완전 가이드 2026

Claude API tool use로 외부 API 호출, DB 쿼리, 계산 실행하는 방법. Python/TypeScript 예제, 병렬 tool call, 에러 핸들링, 실제 프로덕션 패턴 포함.

🇺🇸 Read in English →

Claude API tool use는 Claude가 외부 도구(API, DB, 계산기 등)를 호출할 수 있게 해주는 기능입니다. Claude가 직접 실행하는 게 아니라, 어떤 도구를 어떤 파라미터로 호출할지 알려주면 여러분의 코드가 실제 실행합니다. 이 패턴으로 Claude를 실제 비즈니스 시스템과 연결할 수 있습니다.

기본 개념

사용자: "오늘 서울 날씨 알려줘"
Claude: [get_weather 도구를 location="서울"로 호출하겠습니다]
여러분 코드: 실제 날씨 API 호출 → 결과 반환
Claude: "오늘 서울은 맑고 기온은 18도입니다"

Claude가 판단 → 코드가 실행 → Claude가 결과 해석의 3단계 구조입니다.

기본 구현 (Python)

import anthropic
import json

client = anthropic.Anthropic()

# 1. 도구 정의
tools = [
    {
        "name": "get_weather",
        "description": "특정 위치의 현재 날씨를 가져옵니다",
        "input_schema": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "도시 이름 (예: 서울, 부산)"
                },
                "unit": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "description": "온도 단위"
                }
            },
            "required": ["location"]
        }
    }
]

# 2. Claude에 도구 제공
response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    tools=tools,
    messages=[{"role": "user", "content": "서울 날씨 어때?"}]
)

# 3. Claude가 도구 호출을 원하는지 확인
if response.stop_reason == "tool_use":
    tool_call = next(b for b in response.content if b.type == "tool_use")
    print(f"Claude가 요청한 도구: {tool_call.name}")
    print(f"파라미터: {tool_call.input}")
    
    # 4. 실제 실행 (여러분의 코드)
    result = {"temperature": 18, "condition": "맑음", "humidity": 60}
    
    # 5. 결과를 Claude에게 전달
    final_response = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=1024,
        tools=tools,
        messages=[
            {"role": "user", "content": "서울 날씨 어때?"},
            {"role": "assistant", "content": response.content},
            {
                "role": "user",
                "content": [{
                    "type": "tool_result",
                    "tool_use_id": tool_call.id,
                    "content": json.dumps(result, ensure_ascii=False)
                }]
            }
        ]
    )
    print(final_response.content[0].text)

실전 도구 패턴

데이터베이스 조회 도구

import sqlite3

tools = [
    {
        "name": "query_database",
        "description": "주문 데이터베이스를 SQL로 조회합니다",
        "input_schema": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "실행할 SELECT SQL 쿼리"
                }
            },
            "required": ["query"]
        }
    }
]

def execute_tool(name: str, input_data: dict) -> str:
    if name == "query_database":
        # 안전: SELECT만 허용
        sql = input_data["query"].strip()
        if not sql.upper().startswith("SELECT"):
            return "오류: SELECT 쿼리만 허용됩니다"
        
        conn = sqlite3.connect("orders.db")
        cursor = conn.execute(sql)
        rows = cursor.fetchall()
        cols = [desc[0] for desc in cursor.description]
        conn.close()
        return json.dumps({"columns": cols, "rows": rows[:50]}, ensure_ascii=False)

외부 API 호출 도구

import httpx

tools = [
    {
        "name": "search_products",
        "description": "제품 카탈로그에서 상품을 검색합니다",
        "input_schema": {
            "type": "object",
            "properties": {
                "keyword": {"type": "string"},
                "max_price": {"type": "number"},
                "category": {"type": "string"}
            },
            "required": ["keyword"]
        }
    },
    {
        "name": "place_order",
        "description": "주문을 생성합니다",
        "input_schema": {
            "type": "object",
            "properties": {
                "product_id": {"type": "string"},
                "quantity": {"type": "integer"},
                "user_id": {"type": "string"}
            },
            "required": ["product_id", "quantity", "user_id"]
        }
    }
]

def execute_tool(name: str, inputs: dict) -> str:
    if name == "search_products":
        resp = httpx.get("https://api.shop.com/products", params=inputs)
        return resp.text
    elif name == "place_order":
        resp = httpx.post("https://api.shop.com/orders", json=inputs)
        return resp.text

병렬 Tool Call

Claude가 여러 도구를 동시에 호출할 수 있습니다:

# Claude가 두 도구를 동시에 요청하는 경우
# stop_reason = "tool_use"
# response.content = [TextBlock, ToolUseBlock, ToolUseBlock]

def handle_response(response, messages, tools):
    """병렬 tool call 처리"""
    if response.stop_reason != "tool_use":
        return response.content[0].text
    
    tool_calls = [b for b in response.content if b.type == "tool_use"]
    
    # 병렬로 모든 도구 실행
    tool_results = []
    for call in tool_calls:
        result = execute_tool(call.name, call.input)
        tool_results.append({
            "type": "tool_result",
            "tool_use_id": call.id,
            "content": result
        })
    
    messages.append({"role": "assistant", "content": response.content})
    messages.append({"role": "user", "content": tool_results})
    
    next_response = client.messages.create(
        model="claude-sonnet-4-5",
        max_tokens=1024,
        tools=tools,
        messages=messages
    )
    
    # 재귀: Claude가 또 도구를 원할 수 있음
    return handle_response(next_response, messages, tools)

tool_choice로 도구 사용 강제

# Claude가 반드시 특정 도구를 사용하도록 강제
response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    tools=tools,
    tool_choice={"type": "tool", "name": "get_weather"},  # 강제
    messages=[{"role": "user", "content": "날씨 알려줘"}]
)

# 또는 도구를 전혀 쓰지 않도록
response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    tools=tools,
    tool_choice={"type": "none"},  # 텍스트만 반환
    messages=[{"role": "user", "content": "날씨에 대해 일반적으로 설명해줘"}]
)

에러 처리

def execute_tool_safely(name: str, inputs: dict) -> str:
    try:
        result = execute_tool(name, inputs)
        return json.dumps({"success": True, "data": result})
    except PermissionError as e:
        return json.dumps({"success": False, "error": "권한 없음", "detail": str(e)})
    except Exception as e:
        return json.dumps({"success": False, "error": str(e)})

# tool_result에서 에러 전달
tool_results = [{
    "type": "tool_result",
    "tool_use_id": call.id,
    "content": execute_tool_safely(call.name, call.input),
    "is_error": not result.startswith('{"success": true')
}]

프로덕션 체크리스트

# ✅ 입력 검증
def validate_tool_input(name: str, inputs: dict) -> bool:
    if name == "query_database":
        return inputs.get("query", "").strip().upper().startswith("SELECT")
    if name == "place_order":
        return inputs.get("quantity", 0) > 0
    return True

# ✅ 비용 추적
def track_tool_usage(name: str, duration_ms: int):
    print(f"Tool: {name} | Duration: {duration_ms}ms")

# ✅ 타임아웃
import asyncio
async def execute_with_timeout(name: str, inputs: dict, timeout: float = 10.0):
    return await asyncio.wait_for(
        asyncio.to_thread(execute_tool, name, inputs),
        timeout=timeout
    )

Frequently Asked Questions

tool use와 일반 프롬프트의 차이는?

일반 프롬프트는 Claude가 텍스트로만 대답합니다. tool use는 Claude가 외부 시스템을 호출해서 실시간 데이터(DB, API, 계산 결과)를 가져와 답변에 반영합니다. 날씨, 주가, 재고 현황 같은 실시간 정보가 필요할 때 tool use가 필수입니다.

Claude가 도구를 잘못된 파라미터로 호출할 수 있나요?

가능합니다. 스키마를 명확하게 정의하고 서버측에서 입력 검증을 추가하는 것을 권장합니다. is_error: true로 에러를 Claude에게 돌려주면 Claude가 수정하거나 대안을 제시합니다.

한 번의 대화에서 도구를 몇 번까지 호출할 수 있나요?

API 제한은 없지만, 각 호출마다 토큰이 소비됩니다. 복잡한 에이전트 워크플로우에서는 최대 호출 횟수를 코드에서 제한하는 것을 권장합니다 (예: 10회).

tool use가 Agent SDK와 다른 점은?

tool use는 단일 API 호출 수준의 기능입니다. Agent SDK는 여러 tool use 호출을 포함한 복잡한 agentic loop를 관리하는 상위 레벨 추상화입니다.


관련 가이드: Claude Agent SDK 한국어 가이드 · Claude API Python 한국어 · Claude MCP 서버 한국어

P2 Claude Agent SDK Cookbook — tool use 기반 프로덕션 에이전트 15개 레시피. 재시도 미들웨어, 비용 가드레일 포함. 지금 확인 →

도구와 자료