Skip to main content

8장. Claude API로 확장하기

첫 번째 LLM API를 붙였을 때는 보통 이런 기분이 든다. "생각보다 간단하네?" 입력 보내고, 답변 받고, 화면에 뿌리면 끝처럼 보인다. 그런데 두 번째 API를 붙이는 순간 분위기가 달라진다. 비슷해 보여서 더 헷갈리고, 조금만 다르다고 생각했는데 운영 코드에서는 그 작은 차이가 꽤 크게 터진다.

바로 여기서 개발자의 감각이 바뀐다. 이제부터는 "어떤 모델을 쓸까?"보다 "우리 서비스의 내부 계약을 어떻게 설계할까?"가 더 중요해진다. 한 벤더에만 붙어 있을 때는 SDK 사용법을 익히는 정도로 끝나지만, Claude 같은 두 번째 벤더를 붙이면 비로소 모델과 서비스 사이의 경계선을 의식하게 된다.

이 장에서는 Anthropic Claude API를 실전 기준으로 정리한다. 메시지 형식은 어떻게 생겼는지, 어디서 강점이 드러나는지, OpenAI 계열 코드에서 옮길 때 무엇이 가장 자주 깨지는지, 그리고 왜 이 과정이 멀티 벤더 설계의 출발점이 되는지를 차근차근 살펴보자.

2부. LLM API의 공통 원리 이해하기 / 8장. Claude API로 확장하기

두 번째 API를 붙이는 순간 설계가 바뀐다

Claude API를 붙인다고 해서 완전히 새로운 세계가 열리는 것은 아니다. 큰 흐름은 익숙하다. 모델을 고르고, 메시지를 보내고, 응답을 받는다. 하지만 세부 구조는 분명히 다르다. 시스템 프롬프트를 어디에 두는지, 응답 텍스트를 어디서 꺼내는지, 툴 정의가 어떤 모양인지, 시간 초과를 어디서 조정해야 하는지 같은 부분이 조금씩 어긋난다.

이 어긋남이 중요한 이유는 단순하다. 서비스는 "조금 다른" API 때문에 깨진다. 프런트에서 눌렀더니 백엔드가 500을 내는 이유가 대단한 AI 이론 때문이 아니라, response.choices[0].message.content를 기대했는데 Claude 응답은 블록 배열로 오기 때문인 경우가 훨씬 많다.

그래서 Claude를 붙이는 작업은 모델 하나를 더 추가하는 작업이 아니다. 사실은 내부 LLM 계층을 다시 설계하는 작업에 가깝다. 이 장을 그렇게 읽으면 훨씬 실전적이다.

Claude API를 머릿속에 먼저 그려 보자

Claude의 직접 API는 https://api.anthropic.com을 기준으로 동작한다. 기본적으로 x-api-key, anthropic-version, content-type 헤더가 필요하고, 핵심 엔드포인트는 Messages API다. 여기에 대량 비동기 처리용 Message Batches API, 사전 비용 계산에 유용한 Token Counting API, 모델 목록을 조회하는 Models API가 붙는다. 또한 Claude는 직접 API뿐 아니라 Amazon Bedrock, Google Cloud Vertex AI, Azure 같은 파트너 플랫폼에서도 사용할 수 있지만, 기능 도입 시점이나 세부 차이가 있을 수 있다.1

이 구조를 보면 Claude는 단순한 채팅 API가 아니라, 꽤 분명한 플랫폼 형태를 갖고 있다는 것을 알 수 있다. 특히 배치 처리, 토큰 계산, 파일 업로드, 프롬프트 캐싱 같은 기능은 "데모"보다 "운영"에 가까운 도구들이다. 즉 Claude를 붙인다는 것은 모델 호출만 배우는 일이 아니라, 호출 주변의 운영 장치를 함께 이해하는 일이다.

Messages API의 형식은 비슷하지만 결정적으로 다르다

Claude를 처음 붙일 때 가장 먼저 익혀야 할 것은 Messages API의 모양이다. 핵심은 간단하다. messages 배열에 대화 턴을 넣고, system은 최상위 필드로 별도로 둔다. 그리고 각 메시지의 content는 단순 문자열일 수도 있고, 여러 블록의 배열일 수도 있다.2

이 차이는 처음엔 사소해 보인다. 하지만 서비스 코드에서는 꽤 중요하다. OpenAI 계열에 익숙한 팀은 종종 system을 메시지 배열 안에 넣는 습관이 있는데, Claude에서는 그 습관을 그대로 가져오면 구조가 어긋난다. 또 텍스트만 온다고 가정하고 응답을 문자열 하나로 파싱하면, 나중에 이미지 입력이나 툴 관련 블록이 섞일 때 바로 깨진다.

가장 단순한 호출은 이런 느낌이다.

from anthropic import Anthropic

client = Anthropic(api_key="YOUR_API_KEY")

response = client.messages.create(
    model="claude-sonnet-4-6-20250219",
    system="You are a concise product analyst.",
    messages=[
        {"role": "user", "content": "이 회의록을 세 문장으로 요약해 줘."}
    ],
    max_tokens=500,
)

text = "".join(
    block.text for block in response.content if block.type == "text"
)
print(text)

여기서 눈여겨볼 부분은 세 가지다.

  • system이 메시지 배열 밖에 있다.

  • max_tokens를 명시적으로 넣는다.

  • 응답은 보통 content 블록 배열로 다룬다.

또 하나 흥미로운 점은 Claude가 마지막 assistant 메시지에서 이어 쓰는 방식, 즉 부분 응답을 미리 깔아 두고 뒤를 이어 생성하는 패턴도 지원한다는 점이다. 이런 방식은 출력 형식을 강하게 유도할 때 유용하다.2

OpenAI 스타일 코드에서 Claude로 옮길 때 가장 자주 깨지는 지점

실무에서는 "개념적으로 비슷하다"는 말보다 "어디서 깨지느냐"가 더 중요하다. Claude 마이그레이션에서 자주 부딪히는 지점은 꽤 반복적이다.3

항목OpenAI 계열에서 익숙한 방식Claude에서 확인할 포인트
시스템 지시messages 안의 system 역할 메시지최상위 system 필드로 분리
기본 호출 메서드chat.completions.create(...)messages.create(...)
응답 텍스트 위치response.choices[0].message.contentresponse.content의 블록들에서 추출
출력 토큰 설정기본값에 기대는 경우가 있음max_tokens를 명시하는 습관이 안전
다중 후보 생성n 같은 파라미터를 쓰는 경우가 있음요청당 단일 완성 기준으로 생각
툴 스키마function.parameters 중심name, description, input_schema 중심

이 표를 보면 차이가 커 보이지 않을 수 있다. 그런데 운영 코드에서는 이 작은 차이들이 전부 중요하다. 예를 들어 지금까지의 내부 대화 로그를 그대로 벤더별 SDK에 넣는 구조였다면, Claude용 어댑터는 먼저 system을 끌어올려 따로 분리해야 한다. 응답 정규화 계층이 없다면 프런트나 비즈니스 로직 곳곳에서 벤더별 파싱 코드가 자라나기 시작한다.

툴 정의도 비슷한 함정이 있다. 개념은 같지만 모양이 다르다. OpenAI 스타일에서는 함수 설명이 function 아래 들어가고, Claude에서는 도구 이름과 설명, 입력 스키마가 조금 더 직접적인 구조로 표현된다.3

이쯤 되면 중요한 결론이 하나 나온다. 벤더를 바꿀 때 바뀌어야 하는 것은 프롬프트만이 아니다. 요청 조립기와 응답 정규화기까지 같이 바뀌어야 한다.

Claude는 어떤 프롬프트에 특히 잘 반응하는가

Claude를 붙이고 나면 많은 팀이 처음 며칠 동안 이런 느낌을 받는다. "왜 답이 좀 다르지?" 성능이 떨어진다는 뜻은 아니다. 반응하는 방식이 조금 다르다는 뜻이다.

Claude는 대체로 직접적이고 맥락이 먼저 주어진 지시를 잘 받는다. 가벼운 잡담형 요청보다, 역할과 상황, 제약을 먼저 설명한 뒤 원하는 작업을 또렷하게 적어 주는 편이 안정적이다. 또한 섹션 구분이나 XML 비슷한 태그처럼 구조를 눈에 띄게 만든 프롬프트와도 잘 맞는 편이다.34

예를 들어 "이 코드 좀 더 좋게 바꿔 줘"보다는 "당신은 Django REST API 리뷰어다. 현재 코드는 N+1 쿼리 문제와 입력 검증 누락이 있다. 이 두 문제를 우선 해결하고, 엣지 케이스도 반영해 리팩터링하라"처럼 맥락과 목표를 먼저 주는 편이 좋다.

이건 6장에서 다룬 프롬프트 기본기와도 연결되지만, 여기서는 벤더별 조정 감각이 핵심이다. 같은 프롬프트를 복붙하는 것보다, Claude가 좋아하는 구조로 약간 다듬는 것만으로도 응답 품질이 꽤 안정된다.

Claude의 강점은 어디에서 체감되는가

Claude를 서비스에 붙였을 때 강점이 가장 또렷하게 느껴지는 장면은 대체로 세 가지다.

첫째는 긴 문맥이다. Anthropic은 최신 Claude 라인업에서 긴 컨텍스트를 강하게 밀고 있고, 현재 모델군도 속도와 가격대에 따라 꽤 분명하게 나뉜다. 예를 들어 Opus 4.7은 가장 강한 추론과 에이전트 성격의 작업에, Sonnet 4.6은 실무 기본값에, Haiku 4.5는 빠르고 저렴한 대량 작업에 잘 맞는다.4

둘째는 장문 작성과 정리다. 길고 복잡한 입력을 한 번에 읽고, 비교적 차분하고 구조적인 문장으로 재구성하는 데 강점을 느끼는 팀이 많다. 특히 보고서 초안, 긴 회의록 정리, 정책 문서 요약, 코드베이스 설명처럼 "짧은 한 방"보다 "긴 호흡의 정리"가 필요한 곳에서 잘 어울린다.

셋째는 운영 도구가 붙어 있다는 점이다. Message Batches API는 대량 요청을 비동기로 처리하면서 비용을 절감하는 데 유용하고, 프롬프트 캐싱은 길고 반복적인 시스템 지시나 공통 컨텍스트를 재사용할 때 비용과 지연을 줄이는 데 도움이 된다.14

즉 Claude의 강점은 단순히 "말을 잘한다"가 아니다. 문맥이 길고, 작업이 길고, 운영도 길게 갈 때 체감되는 타입에 가깝다.

하지만 좋아 보이는 점만 보고 바로 올인하면 곤란하다

Claude를 붙일 때 자주 생기는 오해가 하나 있다. 컨텍스트가 길다거나 모델이 좋다거나 하는 장점만 보고, 기존 제품에 거의 그대로 꽂아 넣어도 된다고 생각하는 것이다. 실제로는 몇 가지 주의점이 있다.

응답은 문자열 하나가 아니라 블록일 수 있다

이건 정말 자주 터진다. 데모 단계에서는 텍스트 한 덩어리만 오니 문자열처럼 다루고 싶어진다. 하지만 Claude는 처음부터 content 블록 구조를 중심에 둔다. 텍스트만 본다고 가정한 파서는 나중에 멀티모달 입력, 인용, 툴 관련 결과가 섞일 때 곧장 흔들린다.2

큰 컨텍스트와 큰 요청은 같은 말이 아니다

긴 컨텍스트를 지원한다고 해서 아무 파일이나 막 보내도 된다는 뜻은 아니다. 직접 Claude API의 메시지 요청 크기 제한은 32MB이고, 파트너 플랫폼은 더 낮을 수도 있다. 예를 들어 Vertex AI는 30MB, Amazon Bedrock은 20MB 제한이 문서에 명시되어 있다.1

즉 "토큰은 되는데 HTTP 요청 크기에서 막히는" 일이 생길 수 있다. 긴 문서를 다루는 서비스라면 토큰 한도만 보지 말고 바이트 크기와 업로드 전략도 함께 봐야 한다.

파트너 플랫폼은 항상 똑같지 않다

같은 Claude라도 직접 API와 Bedrock, Vertex AI는 운영 감각이 조금 다르다. 기능 도입 속도, 헤더 체계, 제한, 인증 방식이 다르다. 특히 Bedrock 문서에서는 Claude 3.7 Sonnet과 Claude 4 계열 추론 호출의 타임아웃을 최대 60분까지 고려하라고 안내하며, AWS SDK 기본 타임아웃이 1분인 점을 주의하라고 밝히고 있다.2

이 말은 곧, 모델이 느리다는 뜻이 아니라 인프라 기본값이 AI 호출에 맞지 않을 수 있다는 뜻이다. 기존 웹 API 감각으로 30초, 60초만 잡아 두면 멀쩡한 요청도 중간에 죽는다.

레이트 리밋과 사용 티어를 제품 설계에 반영해야 한다

Claude API는 요청 수와 토큰 수, 월간 사용 한도에 따라 제한이 걸린다. 콘솔에서 현재 한도를 확인할 수 있고, 사용량에 따라 티어가 올라간다.1 여기서 중요한 포인트는 간단하다. 컨텍스트 윈도우 수치만 보고 기능을 설계하면 안 된다는 것이다. 실제 운영에서는 RPM, TPM, 월간 지출, 배치 처리 여부까지 같이 계산해야 한다.

마이그레이션의 핵심은 "Claude용 코드"가 아니라 "벤더 중립 계층"이다

이 장의 가장 중요한 부분은 여기다. Claude로 확장할 때 진짜 목표는 Anthropic SDK를 잘 쓰는 것이 아니다. Claude를 붙여도 내부 애플리케이션 코드가 벤더 차이에 휘둘리지 않게 만드는 것이다.

가장 실전적인 방법은 내부 표준 요청/응답 구조를 먼저 만드는 것이다. 예를 들면 이런 식이다.

export type AppLlmRequest = {
  system?: string;
  messages: Array<{ role: 'user' | 'assistant'; content: string }>;
  tools?: AppTool[];
  maxOutputTokens: number;
  metadata?: Record<string, string>;
};

export type AppLlmResponse = {
  text: string;
  toolCalls: AppToolCall[];
  finishReason?: string;
  usage?: {
    inputTokens?: number;
    outputTokens?: number;
  };
  raw?: unknown;
};

그다음 벤더별 어댑터를 둔다. Claude 어댑터는 내부 system을 최상위 system으로 옮기고, 메시지를 Anthropic 형식으로 바꾸고, 응답 블록들을 합쳐 text로 정규화한다. OpenAI 어댑터는 또 그쪽 형식에 맞게 바꾼다.

이렇게 해 두면 애플리케이션의 나머지 부분은 text, toolCalls, usage 같은 내부 공통 구조만 보면 된다. 바뀌는 것은 어댑터뿐이다. 이 구조가 없으면 모델을 하나 더 붙일 때마다 서비스 전체가 벤더별 분기문으로 가득 찬다.

여기서 특히 분리해 두면 좋은 것도 있다.

  • 메시지 변환기: 내부 메시지 형식을 벤더 형식으로 바꾸는 층

  • 응답 정규화기: 텍스트, 툴 호출, 사용량을 공통 형태로 바꾸는 층

  • 기능 레지스트리: 각 모델이 지원하는 비전, 툴 사용, JSON 출력, 긴 컨텍스트 여부를 기록하는 층

  • 재시도/로깅 정책: 벤더 SDK 바깥에서 공통 처리하는 층

두 번째 API를 붙이는 순간부터는 이 분리가 개발 속도를 결정한다.

OpenAI에서 Claude로 옮길 때 실무 체크리스트

배포 전에 아래 항목만 확인해도 꽤 많은 사고를 줄일 수 있다.

  • system 메시지를 Claude용 최상위 필드로 분리했는가

  • max_tokens를 명시적으로 넣고 있는가

  • 응답을 문자열 하나가 아니라 content 블록 기준으로 파싱하는가

  • 툴 정의를 input_schema 형태까지 포함해 변환하고 있는가

  • 타임아웃, 재시도, 레이트 리밋 처리를 벤더별로 확인했는가

  • 직접 API와 Bedrock/Vertex 같은 파트너 경로를 혼동하지 않았는가

  • 장문 입력에서 토큰 한도뿐 아니라 HTTP 요청 크기 제한도 체크하는가

  • 회귀 테스트용 대표 프롬프트 세트를 벤더별로 돌려 보는가

이 마지막 항목이 의외로 중요하다. 모델을 바꿀 때는 단순 성공/실패 테스트만으로는 부족하다. 같은 입력에 대해 요약 길이, 말투, 구조화 출력 안정성, 툴 선택 성향이 어떻게 달라지는지 비교해야 한다. 그래야 "Claude를 붙였더니 어쩐지 다르다"를 감으로 말하지 않고, 실제 제품 기준으로 판단할 수 있다.

Claude 확장은 곧 멀티 벤더 감각의 시작이다

Claude를 붙이고 나면 신기하게도, 벤더 비교보다 내부 설계가 더 눈에 들어오기 시작한다. "어느 모델이 더 좋지?"보다 "우리 서비스는 시스템 프롬프트를 어디서 관리하지?" "응답을 어떤 내부 객체로 통일하지?" "모델별 차이를 어디까지 흡수하고 어디서 드러내지?" 같은 질문이 중요해진다.

이 질문들이 바로 멀티 벤더 아키텍처의 출발점이다. 그리고 사실 이 질문을 한 번 제대로 해 두면, 이후 Gemini를 붙이든 다른 벤더를 붙이든 훨씬 덜 흔들린다.

Claude API는 좋은 두 번째 API다. 첫 번째 API가 "LLM 호출"을 배우게 해 줬다면, Claude는 "LLM 서비스 설계"를 배우게 해 준다. 비슷한데 다르고, 쉬워 보이는데 운영에서는 섬세함이 필요한 그 미묘한 차이가 바로 개발자를 다음 단계로 끌고 간다.

정리

이 장의 핵심은 분명하다. Claude를 붙인다는 것은 모델 하나를 더 붙이는 일이 아니라, 벤더 차이를 견디는 내부 구조를 만들기 시작하는 일이다.

Claude의 Messages API는 익숙하면서도 다르다. system은 최상위 필드에 있고, 응답은 블록 단위로 오며, 툴 정의의 모양도 조금 다르다. 프롬프트도 직접적이고 맥락 중심으로 다듬을수록 잘 맞는 편이다. 긴 문맥, 장문 처리, 배치 운영 같은 장점은 분명하지만, 요청 크기 제한, 타임아웃, 파트너 플랫폼 차이, 레이트 리밋 같은 현실적인 제약도 함께 따라온다.

결국 중요한 것은 모델 이름이 아니다. 내부 요청 형식, 응답 정규화, 기능 레지스트리, 테스트 세트, 재시도와 로그 같은 운영 구조다. 이걸 갖춰 두면 Claude는 강력한 확장 포인트가 된다. 반대로 이걸 생략하면 두 번째 API는 곧 두 번째 복잡성이 된다.

다음 장에서 Gemini API를 붙여 보면 이 감각이 더 또렷해질 것이다. 그때쯤이면 아마 이런 생각이 들기 시작할 것이다. "아, 모델을 붙이는 게 아니라 추상화를 설계하는 거였구나."

참고

1API Overview - Claude API Docs

2Anthropic Claude Messages API - Amazon Bedrock

3ChatGPT에서 Claude로 전환하는 방법: 완전 마이그레이션 가이드 (2026) | NxCode

4Build on the Claude Platform

책 목록

실전 LLM 서비스 개발: ChatGPT·Claude·Gemini API로 만드는 AI 웹 서비스의 설계부터 운영까지

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기