Published on

LLM으로 만드는 실제 서비스

LLM API 몇 줄이면 끝나던 시대는 지났습니다. 이제는 어떤 문제를 어떤 방식으로 풀 것인가를 설계하는 것이 핵심입니다.

이 글은 실제 서비스를 만들 때 마주하는 의사결정 포인트와 구현 방법을 사례 중심으로 정리합니다.


1. LLM 서비스 개발의 3가지 패턴

모든 LLM 앱은 결국 아래 3가지 패턴 중 하나로 수렴합니다.

패턴핵심 메커니즘적합한 사례
Prompt Engineering프롬프트 설계만으로 출력 제어요약, 번역, 분류, 포맷 변환
RAG (Retrieval-Augmented Generation)외부 지식 검색 후 생성에 주입FAQ 챗봇, 사내 문서 검색, 고객 지원
AgentLLM이 도구를 직접 호출하며 루프예약 시스템, 데이터 분석, 워크플로우 자동화

2. 패턴별 실제 서비스 사례와 개발 방법

2.1 Prompt Engineering: 가장 빠른 시작

가장 접근성이 높지만, 잘 설계하면 생각보다 강력합니다.

서비스 사례: 이메일 자동 분류 및 요약 서비스

import openai

client = openai.OpenAI()

def classify_email(email_text: str) -> dict:
    response = client.chat.completions.create(
        model="gpt-4o",
        response_format={"type": "json_object"},
        messages=[
            {"role": "system", "content": """
            이메일을 분석하여 JSON으로 반환하세요.
            카테고리: [urgent, marketing, spam, notification, personal]
            요약: 1줄 요약
            우선순위: high, medium, low
            """},
            {"role": "user", "content": email_text}
        ]
    )
    return json.loads(response.choices[0].message.content)

개발 포인트:

  • response_format으로 구조화된 출력 보장
  • System Prompt에 분류 기준을 명확히 정의
  • Few-shot 예시 2~3개 추가하면 정확도 대폭 향상

2.2 RAG: 지식이 있는 서비스의 기본 뼈대

LLM의 가장 큰 약점인 "업데이트되지 않은 지식"과 "환각(Hallucination)"을 해결하는 핵심 기술입니다.

서비스 사례: 기업 내부 정책 Q&A 챗봇

RAG 시스템은 크게 네 단계로 동작합니다. 첫 번째 단계는 사용자의 질문이 들어왔을 때 그 의도를 파악하고 적절한 키워드를 추출하는 쿼리 전처리 과정입니다. 이후 두 번째 단계에서는 벡터 검색과 키워드 검색을 동시에 수행하는 하이브리드 검색 방식을 활용합니다. 벡터 검색은 의미적 유사성을 기반으로 하고, BM25와 같은 키워드 검색은 용어의 정확한 일치를 기반으로 하여 서로의 약점을 보완합니다.

세 번째 단계는 리랭킹입니다. 1차 검색에서 수십 개의 후보 문서가 나열되지만, 이 중에서 실제로 사용자 질문에 가장 관련성 높은 Top-K 문서를 선별하는 과정입니다. Cohere Rerank나 Cross-Encoder 모델을 활용하여 각 문서의 관련성을 재점수화하면, LLM에 전달되는 컨텍스트의 품질이 크게 향상됩니다. 이를 통해 흔히 말하는 "Lost in the Middle" 문제, 즉 긴 컨텍스트 중간에 위치한 중요한 정보가 무시되는 현상을 효과적으로 완화할 수 있습니다.

마지막 네 번째 단계에서는 시스템 프롬프트와 함께 검색된 문서, 그리고 사용자 질문을 LLM에 전달하여 최종 답변을 생성합니다. 이때 답변의 근거가 된 문서의 출처를 반드시 함께 제시하도록 프롬프트에 명시하면, 사용자가 답변의 신뢰성을 자체 판단할 수 있게 됩니다.

핵심 기술 스택:

구성요소추천 도구대안
문서 파싱LlamaParseUnstructured, PyMuPDF
임베딩OpenAI text-embedding-3-smallCohere Embed v3, BGE-M3
벡터 DBQdrantPinecone, pgvector, Weaviate
리랭킹Cohere RerankBGE-Reranker, ColBERT
오케스트레이션LlamaIndexLangChain

실전 구현 예시:

from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.llms.openai import OpenAI

# 1. 문서 로드 및 인덱싱
documents = SimpleDirectoryReader("./policies").load_data()
index = VectorStoreIndex.from_documents(documents)

# 2. 쿼리 엔진 생성 (리랭킹 포함)
query_engine = index.as_query_engine(
    similarity_top_k=10,
    node_postprocessors=[
        SentenceTransformerRerank(model="cross-encoder/ms-marco-MiniLM-L-6-v2", top_n=3)
    ],
    text_template="""다음<context>를 참고하여 질문에 답하세요.
규칙: 답변 근거가 된 문서 제목을 반드시 함께 제시하세요.

<context>
{context_str}</context>

질문: {query_str}

답변:"""
)

# 3. 질문 실행
response = query_engine.query("연차 사용 규정은 어떻게 되나요?")
print(response)

2.3 Agent: LLM이 직접 판단하고 실행하는 시스템

Agent는 LLM이 "무엇을 해야 할지" 스스로 결정하고, 도구를 호출하여 결과를 얻는 패턴입니다.

서비스 사례: 자연어 기반 데이터 분석 에이전트

사용자가 "지난 3분기 매출 상위 5개 제품의 카테고리별 평균 마진율을 보여줘"라고 말하면, 에이전트는 내부적으로 여러 단계를 거칩니다. 먼저 사용자의 자연어 질의를 분석하여 어떤 데이터를 조회해야 하는지 파악하고, 그에 맞는 SQL 쿼리를 생성합니다. 생성된 쿼리를 데이터베이스에 실행하여 실제 결과를 얻은 뒤, 그 수치를 해석하여 사용자가 이해하기 쉬운 형태로 정리합니다. 마지막으로 결과 데이터를 바 차트나 라인 차트 등으로 시각화하여 최종 답변과 함께 제시합니다.

이 과정에서 LLM은 단순한 텍스트 생성기가 아닙니다. 어떤 도구를 어떤 순서로 호출할지 스스로 판단하는 의사결정 주체 역할을 합니다. 이것이 바로 Agent 패턴의 핵심적인 차별점입니다.

Function Calling을 활용한 구현:

import openai
import json

tools = [
    {
        "type": "function",
        "function": {
            "name": "execute_sql",
            "description": "SQL 쿼리를 실행하여 데이터를 조회합니다.",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": "실행할 SQL 쿼리"
                    }
                },
                "required": ["query"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "create_chart",
            "description": "데이터를 시각화하여 차트를 생성합니다.",
            "parameters": {
                "type": "object",
                "properties": {
                    "chart_type": {
                        "type": "string",
                        "enum": ["bar", "line", "pie", "scatter"]
                    },
                    "data": {
                        "type": "object",
                        "description": "차트에 사용할 데이터"
                    },
                    "title": {
                        "type": "string"
                    }
                },
                "required": ["chart_type", "data", "title"]
            }
        }
    }
]

def run_agent(user_query: str):
    messages = [
        {"role": "system", "content": """
        당신은 데이터 분석 에이전트입니다.
        사용자의 질문을 분석하고, SQL을 실행하고, 결과를 시각화하세요.
        반드시 단계적으로 진행하세요.
        """},
        {"role": "user", "content": user_query}
    ]

    while True:
        response = client.chat.completions.create(
            model="gpt-4o",
            messages=messages,
            tools=tools,
            tool_choice="auto"
        )

        msg = response.choices[0].message

        if msg.tool_calls:
            for tool_call in msg.tool_calls:
                result = execute_tool(tool_call)
                messages.append(msg)
                messages.append({
                    "role": "tool",
                    "tool_call_id": tool_call.id,
                    "content": json.dumps(result)
                })
        else:
            return msg.content

Agent 개발 시 주의사항:

항목권장사항
루프 제어max_iterations=10 설정 필수 (무한 루프 방지)
타임아웃전체 실행 시간 30초 이내 제한
에러 핸들링도구 실패 시 폴백 로직 준비
비용 관리토큰 사용량 로깅, 일일 한도 설정

3. 모델 선택 가이드

어떤 LLM을 쓸 것인지는 성능, 비용, latency, 보안의 트레이드오프입니다.

사용 사례추천 모델이유
간단한 분류/요약GPT-4o mini, Claude 3.5 Haiku빠르고 저렴
복잡한 추론/분석GPT-4o, Claude Opus높은 정확도
코드 생성Claude Sonnet, GPT-4o코딩 벤치마크 상위
한국어 특화GPT-4o, HyperCLOVA X한국어 성능 우수
오픈소스 (셀프호스팅)Llama 3.1, Qwen 2.5, Mistral데이터 프라이버시, 비용 제어
온프레미스 필수Llama 3.1 70B (vLLM)외부 API 의존 없음

비용 비교 (2026년 기준):

모델입력 ($/1M tokens)출력 ($/1M tokens)특징
GPT-4o mini$0.15$0.60가성비 최고
GPT-4o$2.50$10.00범용 최강
Claude 3.5 Sonnet$3.00$15.00긴 컨텍스트, 코딩
Claude Opus$15.00$75.00최고 품질 추론
Llama 3.1 70BSelf-hostedGPU 비용만데이터 통제

4. 프로덕션 구축 체크리스트

튜토리얼에서 프로덕션까지의 격차는 큽니다. 반드시 확인하세요.

4.1 모니터링

모니터링 항목도구기준값
응답 레이턴시 (P95)Datadog, Grafana< 3초
토큰 사용량자체 대시보드일일 한도 초과 알림
에러율Sentry, CloudWatch< 1%
환각 감지LLM-as-Judge정기 평가 파이프라인

4.2 보안

프롬프트 인젝션은 사용자 입력이 시스템 명령어로 위장하여 LLM의 지침을 무력화하는 공격입니다. 이를 방어하기 위해서는 시스템 프롬프트와 사용자 입력을 물리적으로 분리하는 것이 첫 번째 원칙입니다. 사용자 입력이 들어오면 PII를 마스킹하는 전처리 과정을 거치고, LLM이 생성한 출력에서 민감 정보가 유출되지 않았는지를 다시 검증하는 후처리 레이어를 두는 것이 좋습니다. API 키는 절대 코드에 직접 기록하지 말고 환경 변수나 시크릿 매니저를 통해 관리해야 합니다.

4.3 비용 최적화

비용 최적화는 크게 세 단계로 접근할 수 있습니다. 첫 번째 단계는 프롬프트 캐싱입니다. 시스템 프롬프트가 길거나 RAG 문서가 반복적으로 전달될 때, Anthropic이나 OpenAI가 제공하는 프롬프트 캐싱 기능을 활용하면 동일한 프롬프트에 대한 비용을 50~90%까지 절감할 수 있고, 첫 토큰 응답 속도(TTFT)도 크게 개선됩니다.

두 번째 단계는 모델 라우팅입니다. 단순한 분류 작업이나 정형된 포맷 변환에는 GPT-4o mini나 Claude Haiku 같은 소형 모델을, 깊은 추론이나 복잡한 분석이 필요한 작업에는 GPT-4o나 Claude Opus 같은 대형 모델을 라우팅하면, 전체적인 토큰 단가를 효과적으로 낮출 수 있습니다.

세 번째 단계는 시맨틱 캐싱입니다. 사용자의 질문이 들어올 때마다 매번 LLM API를 호출하는 대신, Redis와 임베딩 유사도를 활용하여 이전에 유사한 질문이 있었는지 확인합니다. 유사도 임계치를 넘는 질문이 감지되면 저장된 답변을 즉시 반환하여 API 비용을 절감하고 응답 속도를 높입니다.


5. 기술 스택 종합 비교

구분초급 (MVP)중급 (서비스)고급 (프로덕션)
LLMOpenAI API멀티 모델 (GPT + Claude)모델 라우팅 + 오픈소스
프레임워크OpenAI SDK 직접LlamaIndex / LangChainLangGraph / 커스텀
벡터 DB-Qdrant / pgvectorPinecone + 리랭킹
모니터링print 로깅LangSmith / HeliconeDatadog + 자체 대시보드
배포StreamlitFastAPI + DockerK8s + Canary 배포

6. 시작하기 위한 추천 로드맵

학습은 네 주 단위로 진행하는 것이 효율적입니다. 첫 주에는 OpenAI API의 기본기인 Chat Completion과 Function Calling을 익히고, 프롬프트 엔지니어링을 실습하면서 간단한 챗봇을 만들어봅니다. 이 단계의 목표는 LLM의 동작 방식을 체감하는 것입니다.

두 번째 주에는 RAG에 집중합니다. 문서를 파싱하고, 임베딩하여 벡터 데이터베이스에 저장하는 것부터 시작하여 LlamaIndex를 활용한 전체 파이프라인을 구축합니다. 리랭킹을 적용하고 검색 품질을 평가하는 과정을 통해 RAG의 핵심 트레이드오프를 체득할 수 있습니다.

세 번째 주는 Agent 영역입니다. Function Calling을 심화하고, LangGraph를 활용하여 에이전트가 반복적인 루프를 돌면서 문제를 해결하는 구조를 구현합니다. 여러 에이전트가 협업하는 멀티 에이전트 패턴까지 경험하면, 복잡한 워크플로우를 설계하는 역량이 확보됩니다.

네 번째 주는 프로덕션 수준으로의 도약입니다. FastAPI로 API 서버를 구축하고, 모니터링과 로깅을 설정하며, 실제로 배포하여 비용 최적화를 수행합니다. 이 단계에서 튜토리얼과 실무의 격차를 가장 체감하게 될 것입니다.


마무리

LLM 애플리케이션 개발의 핵심은 어떤 패턴을 선택할 것인가입니다.

빠르게 검증하고 싶다면 Prompt Engineering으로 시작하세요. 지식이 필요한 서비스라면 RAG의 기본 뼈대를 갖추고, 자율적으로 움직여야 하는 복잡한 워크플로우라면 Agent 패턴을 적용하면 됩니다. 이 세 가지를 조합하는 것이 현실적인 서비스 개발 방법입니다.

처음부터 완벽한 시스템을 만들려 하지 말고, 작은 단위로 시작하여 점진적으로 확장하세요. 프롬프트 몇 줄로 시작한 서비스가 언젠가 멀티 에이전트 시스템으로 진화하는 그 여정이, 결국 LLM 개발의 진짜 재미입니다.


참고 리소스