LLM을 "잘 쓴다"는 것은 모델의 특성과 파라미터를 이해하고, 비용까지 예측하며 설계할 수 있다는 뜻입니다. 이 모듈에서는 Claude를 주축으로 한 우리 강의 스택이 왜 이 선택을 했는지, 그 근거를 LLM 내부 동작부터 짚어봅니다.
주요 LLM 비교
현시점(2025~2026 기준)에서 프로덕션에서 자주 선택되는 모델들의 특성을 정리합니다.
| 모델 | 제공사 | 강점 | Context Window | 비고 |
|---|---|---|---|---|
| Claude Sonnet/Opus | Anthropic | 긴 문서 이해, 지시 준수, 안전성 | 200k | 이 강의의 주력 |
| GPT-4o / o-series | OpenAI | 생태계, 멀티모달, Function Calling 성숙 | 128k | 보조 선택지 |
| Gemini 1.5/2.0 | 초장문 컨텍스트, 멀티모달 | 1M | 대용량 문서 처리 특화 | |
| Llama 3.x | Meta (OSS) | 자체 서버 배포, 데이터 통제 | 128k | 비용 0, 운영 부담 있음 |
이 강의에서는 Claude를 주축으로 두되, callLLM() 함수 하나로 OpenAI도 통합합니다. Vercel AI Gateway의 "provider/model" 문자열(예: "anthropic/claude-sonnet-4-5", "openai/gpt-4o")을 파라미터로 넘기면 됩니다. 모델을 바꿔도 코드는 그대로입니다.
Token과 한국어 토큰 효율
LLM은 문자가 아닌 토큰 단위로 텍스트를 처리하고, 비용도 토큰 수로 청구됩니다.
- 영어: 평균 1단어 ≈ 1~1.3 토큰
- 한국어: 평균 1어절 ≈ 2~3 토큰 (영어 대비 약 2.5배)
"Hello world" → 2 tokens
"안녕하세요" → 5~6 tokens
즉, 한국어 서비스를 만들 때는 동일한 내용이라도 영어 대비 약 2.5배 토큰이 소모됩니다. 프롬프트 최적화(불필요한 조사/어미 제거, 영문 약어 활용)가 비용에 직접 영향을 줍니다.
팁: 시스템 프롬프트처럼 반복 전송되는 텍스트는 한국어보다 영어로 작성하면 토큰을 절약할 수 있습니다. 단, 출력 언어 지시는 명확히 남겨두세요.
Context Window
Context Window는 모델이 한 번의 API 호출에서 처리할 수 있는 최대 토큰 수입니다. 입력(프롬프트 + 대화 이력 + RAG 청크) + 출력을 합산한 값이 이 한도를 넘으면 오류가 납니다.
- Claude: 200k 토큰 → 약 A4 150페이지 분량
- Gemini 1.5 Pro: 1M 토큰 → 초장문 처리 가능하지만 비용도 그만큼
이 강의의 RAG는 별도 벡터 DB 없이 코사인 유사도를 직접 구현(in-memory / Vercel Blob) 합니다. 200k 컨텍스트면 청크 몇 개를 넣어도 충분하며, 외부 인프라 의존성을 줄이는 것이 이 강의의 설계 원칙입니다.
파라미터 제어
LLM 호출 시 동작을 조정하는 핵심 파라미터입니다.
| 파라미터 | 역할 | 권장 범위 |
|---|---|---|
temperature | 출력 다양성. 높을수록 창의적, 낮을수록 결정적 | 창작: 0.7~1.0 / 보고서·분류: 0.2 이하 |
top_p | 누적 확률 토큰 풀 제한. temperature와 함께 조정 | 보통 0.9, temperature 조정 시 고정 권장 |
max_tokens | 출력 최대 토큰 수. 비용 상한선 역할 | 용도별 명시 필수 |
seed | 동일 seed → 재현 가능한 출력(OpenAI 지원) | 테스트·디버깅 시 유용 |
이 강의에서 보고서 생성에는 temperature: 0.2를 사용합니다. 결정적 Markdown 조립이 목표이기 때문입니다.
System vs User 권한 분리
프롬프트는 역할에 따라 반드시 분리해야 합니다.
System: 서비스 운영자 지시 (규칙, 페르소나, 보안 경계)
User: 실제 사용자 입력 (신뢰 낮음)
System 프롬프트는 모델이 더 높은 권위로 처리합니다. 사용자 입력을 System에 섞으면 Prompt Injection 취약점이 생깁니다. 이 강의는 4가지 방어 기법을 다루며, 그 전제가 바로 이 권한 분리입니다.
Structured Output (JSON Mode)
Agent가 LLM 출력을 파싱해서 다음 액션을 결정하려면 구조화된 응답이 필수입니다.
- OpenAI:
response_format: { type: "json_object" }또는json_schema지정 - Anthropic (Claude): 공식 JSON Mode 없음 → 시스템 프롬프트에 스키마를 명시하고, 출력에서 JSON 블록을 파싱하는 방식 사용
// 두 모델 모두에 통용되는 패턴
systemPrompt: `반드시 아래 JSON 형식으로만 응답하세요:
{"intent": "string", "slots": {"key": "value"}}
다른 텍스트는 포함하지 마세요.`
주의: Claude는 JSON 앞뒤에 설명 문구를 붙이는 경향이 있습니다. 정규식이나
JSON.parse()전에 코드 블록 파싱을 추가하세요.
Native Function Calling을 왜 쓰지 않는가
OpenAI와 Claude 모두 Native Function Calling(Tool Use)을 지원합니다. 그럼에도 이 강의는 명시적 라우트 Dispatcher 패턴을 선택했습니다.
- Native Function Calling은 모델이 "언제 어떤 함수를 부를지"를 결정합니다 → 비결정적
- Dispatcher는 개발자가 라우팅 로직을 코드로 명시합니다 → 결정적, 디버깅 가능, 비용 예측 가능
- 멀티 LLM 환경(Claude + OpenAI 혼용)에서 Function Calling 스키마가 달라 추상화 비용이 큼
사용자 입력 → callLLM(intent 분류) → dispatcher switch(intent) → 각 핸들러
프레임워크(LangChain 등) 없이 이 흐름을 직접 짜는 것이 이 강의의 핵심 학습 포인트입니다.
비용 모델 + Prompt Caching
API 비용 구조: (입력 토큰 × 입력 단가) + (출력 토큰 × 출력 단가)
출력 토큰이 입력보다 단가가 높습니다. 긴 출력을 요청할수록 비용이 급증합니다.
**Prompt Caching (Claude)**은 동일한 프롬프트 앞부분이 반복될 때 캐시 히트 시 최대 90% 비용 절감이 가능합니다.
- System 프롬프트, RAG 청크처럼 매 요청마다 반복되는 부분을 캐시 대상으로 지정
cache_control: { type: "ephemeral" }마킹으로 활성화- 캐시 유효 시간: 약 5분 (TTL은 변경될 수 있음)
설계 원칙: 자주 바뀌지 않는 컨텍스트(시스템 프롬프트, 공통 지식 청크)는 프롬프트 앞부분에 배치하고 캐시 마킹을 합니다. 사용자별 동적 데이터는 뒤에 붙입니다.
핵심 정리
- 한국어는 영어 대비 약 2.5배 토큰을 소모하므로, 반복 프롬프트의 언어 선택이 비용에 직결됩니다.
temperature: 0.2는 결정적 보고서·분류에, 높은 값은 창의적 생성에 씁니다. 두 값을 혼용하지 마세요.- System / User 역할 분리는 Prompt Injection 방어의 가장 기본 전제입니다.
- Structured Output은 Claude에서 공식 JSON Mode 대신 프롬프트 스키마 명시 + 파싱으로 처리합니다.
- Native Function Calling 대신 Dispatcher 패턴을 선택한 이유는 결정성과 멀티 LLM 추상화입니다.
- Prompt Caching으로 반복 컨텍스트 비용을 최대 90% 절감할 수 있으며, 캐시 대상은 프롬프트 앞부분에 배치합니다.