프로덕션 LLM 앱 옵저버빌리티: 무엇을 관측하고 어떻게 개선할까
LLM 앱은 기존 서비스와 다르게 실패합니다. 트레이싱, 토큰·비용 추적, 품질 평가, 회귀 테스트까지 — 프로덕션 LLM 운영의 필수 관측 체계를 정리했습니다.
전통적인 서비스는 500 에러가 나거나 응답이 느려지면 문제를 압니다. 그런데 LLM 앱은 200 OK를 반환하면서 완전히 틀린 답을 줄 수 있습니다. 기존 APM만으로는 이 실패를 절대 잡을 수 없습니다.
이 글에서는 프로덕션 LLM 앱에서 무엇을 관측해야 하고, 그것으로 어떻게 품질을 개선하는지 다룹니다.
1. LLM 앱은 다르게 실패한다
| 구분 | 일반 서비스 | LLM 앱 |
|---|---|---|
| 실패 신호 | 5xx, 타임아웃 | 200 OK인데 답이 틀림 |
| 재현성 | 같은 입력 → 같은 출력 | 같은 입력 → 다른 출력 |
| 비용 | 요청 수에 비례 | 토큰 수에 비례 (예측 어려움) |
| 회귀 | 코드 변경 시 | 프롬프트·모델 변경 시 |
따라서 관측 대상도 달라져야 합니다. 레이턴시·에러율에 더해 토큰·비용·품질을 함께 봐야 합니다.
2. 최소한 이것만은 기록하라
화려한 도구를 도입하기 전에, 모든 LLM 호출에 대해 다음을 로깅하는 것부터 시작하세요. 이것만 있어도 대부분의 문제를 진단할 수 있습니다.
await logLLMCall({
// 식별
traceId, // 요청 단위 추적 ID
feature: "doc_summarize", // 어떤 기능인지
userId,
// 입출력 (디버깅의 핵심)
model: "claude-sonnet-5",
promptVersion: "v3", // 프롬프트 버전 추적 필수!
input: truncate(prompt),
output: truncate(response),
// 비용
inputTokens: usage.input_tokens,
outputTokens: usage.output_tokens,
cachedTokens: usage.cache_read_input_tokens ?? 0,
// 성능
latencyMs,
ttftMs, // 첫 토큰까지 시간 (스트리밍이면)
// 결과
finishReason: response.stop_reason, // max_tokens에서 잘렸나?
error: err?.message,
});
가장 중요한 필드는 promptVersion입니다.
프롬프트를 바꿨는데 품질이 떨어졌을 때,
버전 기록이 없으면 원인을 영원히 못 찾습니다.
프롬프트는 코드처럼 버전 관리하세요.
3. 트레이싱: 에이전트는 한 번에 안 끝난다
단순 호출은 로그로 충분하지만, RAG나 에이전트는 하나의 사용자 요청이 검색 → LLM → 도구 호출 → LLM 처럼 여러 단계로 이어집니다. 어느 단계에서 틀렸는지 알려면 트레이싱이 필요합니다.
OpenTelemetry 기반 도구(Langfuse, Phoenix, LangSmith 등)를 쓰면 단계별로 입출력·레이턴시·토큰을 나눠 볼 수 있습니다.
const span = tracer.startSpan("rag_pipeline");
const docs = await withSpan("retrieve", () => vectorSearch(query));
// 검색이 엉뚱한 문서를 가져왔나? → 여기서 확인
const answer = await withSpan("generate", () => llm.call(docs, query));
// 문서는 맞는데 답이 틀렸나? → 여기서 확인
span.end();
RAG 품질 문제의 대부분은 LLM이 아니라 검색 단계에서 발생합니다. 트레이싱이 없으면 프롬프트만 계속 고치며 헛수고하게 됩니다.
4. 품질을 자동으로 평가하기
비용과 레이턴시는 쉽게 재지만, "답이 좋은가"는 어떻게 잴까요? 실무에서는 세 가지를 조합합니다.
① 사용자 피드백 (가장 정확, 가장 적게 모임)
👍/👎 버튼, 답변 복사 여부, 재질문 발생률 같은 암묵적 신호를 수집하세요. "사용자가 같은 질문을 다시 했다"는 건 강력한 실패 신호입니다.
② LLM-as-judge (자동, 대규모)
LLM에게 다른 LLM의 답변을 평가시키는 방법입니다. 정확성·관련성·근거 유무 등을 점수화합니다.
const judgment = await judge.call({
question,
answer,
retrievedDocs,
criteria: "답변이 제공된 문서에 근거하는가? 문서에 없는 내용을 지어냈는가?",
});
// → { grounded: false, score: 2 } 같은 결과를 메트릭으로 수집
완벽하진 않지만 대량 트래픽에서 품질 추세를 보는 데 충분히 유용합니다.
③ 골든 세트 회귀 테스트 (배포 전 필수)
가장 중요합니다. 정답이 있는 질문 50~200개를 모아두고, 프롬프트나 모델을 바꿀 때마다 CI에서 자동으로 돌리세요.
# CI 파이프라인
- name: LLM regression test
run: npm run eval -- --dataset golden-set.jsonl
# 통과 기준: 정확도 85% 미만이면 배포 차단
이것이 LLM 앱의 유닛 테스트입니다. "프롬프트 한 줄 고쳤는데 다른 기능이 깨졌다"는 사고를 막을 수 있는 유일한 방법입니다.
5. 반드시 걸어야 할 알림
- 비용 급증 — 시간당 토큰 사용량이 평소의 N배 (무한루프 에이전트 조기 발견)
- 캐시 히트율 급락 — 프롬프트 앞부분이 바뀌어 캐싱이 깨진 신호
finish_reason: max_tokens증가 — 답변이 잘리고 있다는 뜻- 품질 점수 하락 — LLM-as-judge 점수의 일간 추세
- 재질문율 증가 — 사용자가 만족하지 못하고 있다는 강한 신호
특히 비용 급증 알림은 반드시 넣으세요. 에이전트가 무한루프에 빠지면 하룻밤에 수천 달러가 나갈 수 있습니다. 토큰 사용량 상한(하드 리밋)도 함께 걸어두는 것이 안전합니다.
6. 개인정보 — 로깅의 함정
LLM 관측의 핵심은 입출력을 저장하는 것인데, 바로 그 입출력에 개인정보가 들어있을 수 있습니다.
- 마스킹 — 저장 전 이메일·전화번호·주민번호 등을 치환
- 샘플링 — 전량이 아닌 일부만 저장 (예: 1~5%)
- 보관 기간 — 짧게 설정하고 자동 삭제 (예: 30일)
- 접근 제어 — 원문 열람은 최소 인원으로 제한
금융·의료처럼 규제가 강한 도메인이라면 원문 대신 메트릭만 수집하는 방안도 검토하세요.
마치며
LLM 옵저버빌리티는 도구를 사는 문제가 아니라 습관을 만드는 문제입니다. 순서는 이렇습니다:
- 모든 호출에 토큰·비용·프롬프트 버전 로깅
- 골든 세트 회귀 테스트를 CI에 추가
- 비용 급증 알림 설정
- RAG·에이전트라면 단계별 트레이싱 도입
1번과 2번만 해도 프로덕션 사고의 상당수를 막을 수 있습니다. 측정하지 않으면 개선할 수 없습니다 — 이 원칙은 LLM 시대에도 그대로 유효합니다.
참고 자료
- Claude 공식 문서 — 토큰 사용량(usage), stop_reason, 프롬프트 캐싱 등 이 글에서 다룬 API 필드의 공식 레퍼런스
- OpenAI Platform 공식 문서 — OpenAI API 기준의 사용량 추적·평가(evals) 관련 공식 가이드
- Hugging Face 공식 블로그 — LLM 평가·벤치마크·LLM-as-judge 관련 기술 글이 꾸준히 올라오는 채널
관련 스토리
AI 워크로드를 위한 Kubernetes GPU 스케줄링 실전 가이드
GPU는 비싸고 놀리면 손해입니다. Device Plugin, MIG 분할, 노드 어피니티, 우선순위 기반 선점까지 — GPU 활용률을 끌어올리는 실전 설정을 정리했습니다.
Platform Engineering: 2026년 가장 주목받는 DevOps 역할
80% IDP 도입률. Platform Engineering이 주류 관행으로 자리잡은 2026년. 개발자 경험을 혁신하는 내부 개발자 플랫폼 구축 가이드.
클라우드 네이티브 아키텍처 완전 가이드 (2026년판)
마이크로서비스, 제로 트러스트 보안, GitOps, 옵저버빌리티, 엣지 컴퓨팅, GreenOps까지. 2026년 클라우드 네이티브의 모든 것.
Comments