LLM 애플리케이션 아키텍처: 오케스트레이션·모델·데이터·관측성 4계층으로 나누는 법
LLM 애플리케이션이 실패하는 이유는 대부분 모델 하나로 설명되지 않습니다. 응답이 느린 이유는 모델이 느려서가 아니라 검색, reranking, tool call, fallback이 직렬로 묶였기 때문일 수 있습니다. 답이 틀린 이유도 모델 성능이 아니라 data layer의 chunking과 retrieval이 망가졌기 때문일 수 있습니다.
최근 MLflow가 정리한 2026년 LLM application architecture 글은 구조를 네 계층으로 나눕니다. orchestration, model, data, observability and guardrail layer입니다. 이 분류는 실무에서 꽤 쓸 만합니다. “AI 기능을 붙인다”가 아니라 “운영 가능한 분산 시스템을 만든다”로 문제를 바꾸기 때문입니다.
1계층: orchestration layer
오케스트레이션 계층은 요청이 들어온 뒤 어떤 순서로 작업을 실행할지 결정합니다. 단순 챗봇이라면 prompt 조립 후 모델 호출 한 번이면 끝날 수 있습니다. 하지만 실무 기능은 보통 더 복잡합니다.
- 사용자 권한 확인.
- 검색 쿼리 생성.
- vector DB와 keyword search 동시 호출.
- reranker 적용.
- tool call 실행.
- 모델 응답 생성.
- 정책 검사와 후처리.
이 흐름이 코드 여기저기에 흩어지면 장애 분석이 어렵습니다. 그래서 orchestration layer는 workflow engine, state machine, graph 구조로 관리하는 것이 좋습니다. LangGraph 같은 도구를 쓰든 자체 구현을 하든 핵심은 단계별 입력, 출력, 실패 처리를 명확히 남기는 것입니다.
좋은 오케스트레이션은 “모델이 알아서 하게 둔다”가 아닙니다. 모델이 할 일과 코드가 할 일을 나눕니다.
2계층: model layer
모델 계층은 어떤 모델을 어떻게 호출할지 관리합니다. 여기에는 provider SDK, API gateway, 모델 라우팅, 캐싱, fallback, rate limit 처리가 포함됩니다.
초기 구현에서 자주 나오는 실수는 서비스 코드가 특정 모델 이름에 직접 묶이는 것입니다.
const response = await openai.responses.create({
model: 'gpt-5.6-sol',
input,
});
작은 실험이면 괜찮습니다. 하지만 production에서는 모델 교체, 가격 변경, 장애, latency 요구가 계속 생깁니다. 그래서 호출부는 “summarize-ticket”, “classify-risk”, “generate-answer” 같은 capability 기준으로 요청하고, 실제 모델 선택은 model layer에서 결정하는 편이 낫습니다.
모델 계층에서 관리해야 할 정책은 다음입니다.
- 작업 유형별 기본 모델.
- latency SLA별 fast/standard 경로.
- 실패 시 fallback 모델.
- prompt caching 사용 여부.
- max token과 reasoning effort.
- 비용 상한과 rate limit.
이 계층이 없으면 비용 최적화도 매번 전체 코드를 뒤져야 합니다.
3계층: data layer
데이터 계층은 모델이 참고할 맥락을 만드는 영역입니다. RAG 시스템에서는 문서 수집, chunking, embedding, vector DB 저장, retrieval, reranking, prompt assembly가 여기에 들어갑니다.
많은 팀이 “문서를 vector DB에 넣었으니 RAG가 됐다”고 생각합니다. 하지만 실제 품질은 세부 설계에서 갈립니다.
- chunk 크기가 질문 단위와 맞는가.
- metadata filter가 권한과 최신성을 반영하는가.
- keyword search와 vector search를 같이 쓰는가.
- 검색 결과가 너무 많거나 적지 않은가.
- reranker가 비용 대비 효과가 있는가.
- prompt에 넣은 context가 출처를 보존하는가.
특히 한국어 문서나 코드 문서에서는 semantic search만으로 부족한 경우가 많습니다. 함수명, 에러 코드, 상품명처럼 exact match가 중요한 쿼리는 keyword search가 더 강합니다. 그래서 hybrid search가 production 기본값에 가깝습니다.
4계층: observability and guardrail layer
관측성과 가드레일 계층은 prototype과 production을 가르는 지점입니다. 일반 API 로그만으로는 부족합니다. LLM 기능에서는 최소한 다음이 보여야 합니다.
- 어떤 prompt와 context가 들어갔는가.
- 어떤 모델과 버전이 호출됐는가.
- token과 비용이 얼마였는가.
- retrieval 결과가 무엇이었는가.
- tool call이 몇 번 실행됐는가.
- 응답이 어떤 policy check를 통과했는가.
- 사용자가 최종 답변을 어떻게 평가했는가.
이 정보가 없으면 장애가 났을 때 “모델이 이상했다”는 말밖에 못 합니다. 반대로 trace가 있으면 원인을 나눌 수 있습니다. 검색 결과가 틀렸는지, prompt 조립이 깨졌는지, 모델 fallback이 잘못 탔는지, 후처리에서 정보가 날아갔는지 볼 수 있습니다.
분산 시스템 관점으로 봐야 하는 이유
LLM 기능은 latency-heavy하고 non-deterministic합니다. 전통적인 CRUD API보다 실패 양상이 다릅니다.
- 같은 입력도 다른 출력이 나올 수 있습니다.
- context window overflow로 오래된 대화가 잘릴 수 있습니다.
- embedding, retrieval, reranking 중 하나만 느려도 전체 응답이 느려집니다.
- provider rate limit이 특정 시간대에 충돌할 수 있습니다.
- 장기 agent session은 stale memory를 쌓아 품질이 떨어질 수 있습니다.
따라서 LLM 애플리케이션은 “모델 API 하나 붙이는 작업”이 아니라 queue, cache, gateway, timeout, retry, circuit breaker를 가진 분산 시스템으로 설계해야 합니다.
작은 팀을 위한 최소 아키텍처
처음부터 거대한 플랫폼을 만들 필요는 없습니다. 하지만 계층은 작게라도 분리하는 편이 좋습니다.
API route
-> orchestration service
-> retrieval service
-> model gateway
-> policy checker
-> trace logger
각 계층은 처음에는 하나의 모듈이어도 됩니다. 중요한 것은 경계입니다. 나중에 vector DB를 바꾸거나 모델을 교체하거나 observability 도구를 붙일 때 전체 서비스를 다시 쓰지 않게 만드는 것이 목적입니다.
실행 체크리스트
- 현재 AI 기능을 orchestration, model, data, observability 계층으로 그려본다.
- 모델 이름을 서비스 코드에 직접 박아둔 곳을 찾는다.
- retrieval 단계의 입력, 결과, 점수를 trace로 남긴다.
- token, 비용, latency를 요청 단위로 기록한다.
- timeout과 retry 정책을 모델 호출, 검색 호출, tool call별로 분리한다.
- context window overflow와 session memory drift를 테스트 케이스에 넣는다.
- keyword search와 vector search를 함께 쓰는 hybrid retrieval을 검토한다.
- LLM-as-a-judge 평가는 production 로그의 샘플부터 시작한다.
LLM 애플리케이션의 품질은 모델 선택만으로 결정되지 않습니다. 오케스트레이션이 흐름을 통제하고, 모델 계층이 비용과 장애를 흡수하고, 데이터 계층이 근거를 만들고, 관측성 계층이 실패를 설명할 때 production 시스템이 됩니다.