본 콘텐츠의 이미지 및 내용은 AI로 생성되었습니다.
본 콘텐츠의 이미지 및 내용을 무단으로 복제, 배포, 수정하여 사용할 경우 저작권법에 의해 법적 제재를 받을 수 있습니다.
이미지 로딩 중...
AI Generated
2025. 12. 27. · 0 Views
프로덕션 워크플로 배포 완벽 가이드
LLM 기반 애플리케이션을 실제 운영 환경에 배포하기 위한 워크플로 최적화, 캐싱 전략, 비용 관리 방법을 다룹니다. Airflow와 서버리스 아키텍처를 활용한 실습까지 포함하여 초급 개발자도 프로덕션 수준의 배포를 할 수 있도록 안내합니다.
목차
1. 워크플로 최적화
김개발 씨는 드디어 첫 LLM 프로젝트를 완성했습니다. 로컬에서는 완벽하게 돌아갔습니다.
그런데 팀장님이 한마디 하셨습니다. "이거 프로덕션에 올리면 어떻게 되지?" 갑자기 머릿속이 하얘졌습니다.
워크플로 최적화란 LLM 파이프라인의 각 단계를 효율적으로 연결하고 관리하는 것입니다. 마치 공장의 컨베이어 벨트처럼, 데이터가 흘러가는 각 단계를 최적화하면 전체 처리 시간이 줄어들고 안정성이 높아집니다.
이를 제대로 구성하면 수천 건의 요청도 거뜬히 처리할 수 있습니다.
다음 코드를 살펴봅시다.
from concurrent.futures import ThreadPoolExecutor
import asyncio
class LLMWorkflow:
def __init__(self, max_workers=5):
# 동시 처리 워커 수 설정
self.executor = ThreadPoolExecutor(max_workers=max_workers)
self.batch_size = 10
async def process_batch(self, prompts: list) -> list:
# 배치 단위로 LLM 호출 최적화
results = []
for i in range(0, len(prompts), self.batch_size):
batch = prompts[i:i + self.batch_size]
# 병렬 처리로 처리량 향상
batch_results = await asyncio.gather(
*[self.call_llm(p) for p in batch]
)
results.extend(batch_results)
return results
김개발 씨는 입사 6개월 차 주니어 개발자입니다. 지난 두 달간 열심히 만든 LLM 챗봇이 드디어 완성되었습니다.
로컬 환경에서 테스트할 때는 모든 것이 완벽했습니다. 응답도 빠르고, 결과도 정확했습니다.
그런데 팀장님이 던진 한마디가 그를 고민에 빠뜨렸습니다. "하루에 만 건 정도 요청이 들어올 텐데, 이 구조로 버틸 수 있을까요?" 선배 개발자 박시니어 씨가 다가와 화면을 살펴봅니다.
"아, 요청을 하나씩 순차적으로 처리하고 있네요. 이러면 프로덕션에서 병목이 생길 수밖에 없어요." 그렇다면 워크플로 최적화란 정확히 무엇일까요?
쉽게 비유하자면, 워크플로 최적화는 마치 대형 마트의 계산대 시스템과 같습니다. 손님이 한 명씩 올 때는 계산대 하나로 충분합니다.
하지만 점심시간에 손님이 몰리면 여러 계산대를 동시에 열어야 합니다. 그리고 바코드 스캐너, 카드 결제기, 영수증 프린터가 매끄럽게 연동되어야 빠른 계산이 가능합니다.
LLM 워크플로도 마찬가지입니다. 단일 요청을 처리하는 것과 수천 건의 요청을 동시에 처리하는 것은 완전히 다른 문제입니다.
병렬 처리, 배치 처리, 비동기 처리라는 세 가지 핵심 전략이 필요합니다. 먼저 위의 코드를 살펴보겠습니다.
ThreadPoolExecutor는 여러 작업을 동시에 처리할 수 있는 워커 풀을 만듭니다. 마트로 치면 계산대를 여러 개 여는 것입니다.
max_workers=5는 동시에 5개의 작업을 처리할 수 있다는 의미입니다. batch_size는 한 번에 묶어서 처리할 요청의 수입니다.
개별 요청마다 LLM API를 호출하면 네트워크 오버헤드가 커집니다. 10개씩 묶어서 처리하면 이 오버헤드를 줄일 수 있습니다.
asyncio.gather는 비동기 마법을 부리는 부분입니다. 배치 내의 모든 LLM 호출을 동시에 시작하고, 모든 결과가 돌아올 때까지 기다립니다.
순차 처리였다면 10초 걸릴 작업이 2초 만에 끝날 수 있습니다. 실제 현업에서는 이보다 더 복잡한 최적화가 필요합니다.
예를 들어 뉴스 요약 서비스를 운영한다고 가정해봅시다. 아침 9시에 수백 개의 뉴스 기사가 한꺼번에 들어옵니다.
이때 워크플로가 최적화되어 있지 않으면 사용자들은 한참을 기다려야 합니다. 하지만 주의할 점도 있습니다.
워커 수를 무작정 늘린다고 성능이 좋아지지는 않습니다. LLM API에는 보통 Rate Limit이 있습니다.
분당 요청 수가 제한되어 있다는 뜻입니다. 워커를 100개로 늘려봤자 API 제한에 걸리면 오히려 에러만 늘어납니다.
박시니어 씨가 조언합니다. "API 제한을 확인하고, 그에 맞게 워커 수를 조절해야 해요.
그리고 실패한 요청을 재시도하는 로직도 꼭 넣어두세요." 김개발 씨는 고개를 끄덕였습니다. 단순히 코드를 작성하는 것과 프로덕션에서 안정적으로 운영하는 것은 전혀 다른 차원의 문제였습니다.
워크플로 최적화는 그 첫걸음이었습니다.
실전 팁
💡 - API Rate Limit을 항상 확인하고 워커 수를 그에 맞게 조절하세요
- 실패한 요청에 대한 재시도 로직과 지수 백오프를 구현하세요
- 배치 크기는 API 응답 시간과 메모리 사용량을 고려해서 조정하세요
2. 캐싱 전략
김개발 씨의 LLM 서비스가 드디어 프로덕션에 올라갔습니다. 그런데 이상한 일이 벌어졌습니다.
같은 질문이 하루에 수십 번씩 들어오는데, 매번 LLM API를 호출하고 있었던 것입니다. 이번 달 API 비용 청구서를 보고 팀장님 얼굴이 하얘졌습니다.
캐싱 전략은 이미 계산한 결과를 저장해두고 재사용하는 기법입니다. 마치 자주 찾는 책을 책상 위에 꺼내두는 것과 같습니다.
매번 서재까지 가서 책을 찾는 대신, 손닿는 곳에 두면 시간을 절약할 수 있습니다. LLM 호출은 비용과 시간이 많이 드는 작업이므로 캐싱이 특히 중요합니다.
다음 코드를 살펴봅시다.
import hashlib
import redis
import json
class LLMCache:
def __init__(self, redis_url="redis://localhost:6379"):
self.redis = redis.from_url(redis_url)
self.ttl = 3600 # 캐시 유효 시간: 1시간
def _generate_key(self, prompt: str, model: str) -> str:
# 프롬프트와 모델 조합으로 고유 키 생성
content = f"{model}:{prompt}"
return f"llm:{hashlib.sha256(content.encode()).hexdigest()}"
def get_or_call(self, prompt: str, model: str, llm_func):
cache_key = self._generate_key(prompt, model)
# 캐시 히트 확인
cached = self.redis.get(cache_key)
if cached:
return json.loads(cached)
# 캐시 미스: LLM 호출 후 저장
result = llm_func(prompt)
self.redis.setex(cache_key, self.ttl, json.dumps(result))
return result
김개발 씨는 API 비용 청구서를 보며 한숨을 쉬었습니다. 예상보다 세 배나 많은 금액이었습니다.
도대체 어디서 이렇게 많은 호출이 발생한 걸까요? 로그를 분석해보니 원인이 명확했습니다.
"오늘 날씨 어때?", "회사 주소 알려줘", "영업시간이 어떻게 돼?" 같은 질문이 하루에 수백 번씩 반복되고 있었습니다. 그리고 매번 LLM API를 호출하고 있었습니다.
박시니어 씨가 다가왔습니다. "캐싱을 안 했구나.
같은 질문에 같은 답변을 할 건데, 굳이 매번 LLM을 부를 필요가 있을까요?" 캐싱이란 무엇일까요? 쉽게 말해 자주 쓰는 정보를 가까운 곳에 저장해두는 것입니다.
도서관을 떠올려 보세요. 어떤 책이 특히 인기가 많다면, 사서는 그 책을 대출 데스크 바로 옆에 비치해둡니다.
매번 서고 깊숙한 곳까지 가서 찾아오는 것보다 훨씬 빠르니까요. 캐싱도 같은 원리입니다.
LLM 호출은 비싸고 느린 작업입니다. GPT-4 한 번 호출에 수십 원이 들고, 응답을 받는 데 몇 초가 걸립니다.
그런데 "영업시간이 몇 시야?"라는 질문에 대한 답변은 매번 똑같습니다. 이런 요청을 캐싱하면 비용은 거의 0원, 응답 시간은 밀리초 단위로 줄어듭니다.
위 코드에서 Redis를 사용하는 이유가 여기 있습니다. Redis는 인메모리 데이터베이스입니다.
모든 데이터가 메모리에 저장되어 있어서 디스크 기반 데이터베이스보다 수십 배 빠릅니다. _generate_key 메서드를 살펴보겠습니다.
프롬프트와 모델 이름을 조합해서 해시값을 생성합니다. 왜 해시를 쓸까요?
프롬프트가 수백 글자일 수도 있는데, 그대로 키로 쓰면 메모리 낭비입니다. 해시로 변환하면 항상 고정된 길이의 짧은 문자열이 됩니다.
get_or_call 메서드가 캐싱의 핵심 로직입니다. 먼저 캐시에서 결과를 찾아봅니다.
있으면 바로 반환합니다. 이것을 캐시 히트라고 합니다.
없으면 LLM을 호출하고, 그 결과를 캐시에 저장한 뒤 반환합니다. 이것을 캐시 미스라고 합니다.
TTL(Time To Live)도 중요합니다. 캐시된 데이터를 영원히 보관하면 안 됩니다.
정보가 바뀔 수 있으니까요. 코드에서는 3600초, 즉 1시간으로 설정했습니다.
1시간이 지나면 캐시가 자동으로 삭제되고, 다음 요청 때 새로운 결과를 가져옵니다. 하지만 주의할 점이 있습니다.
모든 요청을 캐싱하면 안 됩니다. "오늘 주식 시세 알려줘"처럼 실시간 정보가 필요한 요청은 캐싱하면 안 됩니다.
또한 개인화된 응답도 캐싱하기 어렵습니다. 같은 질문이라도 사용자마다 다른 답변을 해야 할 수 있으니까요.
박시니어 씨가 덧붙였습니다. "캐싱 전략을 잘 세우면 비용을 80퍼센트까지 줄일 수 있어요.
하지만 어떤 요청을 캐싱할지, TTL은 얼마로 할지 신중하게 결정해야 합니다." 김개발 씨는 캐싱을 적용한 후 다음 달 청구서를 확인했습니다. 비용이 4분의 1로 줄어 있었습니다.
팀장님의 얼굴에 드디어 미소가 돌아왔습니다.
실전 팁
💡 - 자주 반복되는 정적 질문은 TTL을 길게, 동적 정보는 짧게 설정하세요
- 캐시 키 생성 시 프롬프트뿐 아니라 모델 버전, 파라미터도 포함하세요
- 캐시 히트율을 모니터링해서 캐싱 전략의 효과를 측정하세요
3. 비용 관리
캐싱 덕분에 비용이 많이 줄었지만, 팀장님은 여전히 걱정이었습니다. "다음 달에 사용자가 열 배로 늘면 어쩌지?" LLM 서비스는 사용량에 비례해서 비용이 늘어납니다.
예산 내에서 서비스를 운영하려면 체계적인 비용 관리가 필요했습니다.
비용 관리는 LLM API 사용량을 추적하고, 예산 한도 내에서 서비스를 운영하는 전략입니다. 마치 가계부를 쓰며 생활비를 관리하는 것처럼, 토큰 사용량과 API 호출 횟수를 모니터링하고 적절한 제한을 두어야 합니다.
예상치 못한 비용 폭탄을 막는 안전장치이기도 합니다.
다음 코드를 살펴봅시다.
from datetime import datetime, timedelta
from dataclasses import dataclass
import threading
@dataclass
class UsageStats:
tokens_used: int = 0
api_calls: int = 0
estimated_cost: float = 0.0
class CostManager:
def __init__(self, daily_budget: float = 100.0):
self.daily_budget = daily_budget
self.usage = UsageStats()
self.lock = threading.Lock()
self.cost_per_1k_tokens = 0.03 # GPT-4 기준
def can_proceed(self, estimated_tokens: int) -> bool:
# 예상 비용 계산
estimated_cost = (estimated_tokens / 1000) * self.cost_per_1k_tokens
with self.lock:
# 일일 예산 초과 여부 확인
if self.usage.estimated_cost + estimated_cost > self.daily_budget:
return False
return True
def record_usage(self, tokens: int):
with self.lock:
self.usage.tokens_used += tokens
self.usage.api_calls += 1
self.usage.estimated_cost += (tokens / 1000) * self.cost_per_1k_tokens
김개발 씨는 서비스가 인기를 끌기 시작하면서 새로운 고민이 생겼습니다. 사용자가 늘어날수록 API 비용도 함께 늘어났습니다.
이대로 가다간 회사 예산을 초과할 것 같았습니다. 팀장님이 말했습니다.
"한 달 예산이 300만 원인데, 지금 추세면 500만 원 나오겠어. 뭔가 대책이 필요해." 박시니어 씨가 화이트보드 앞에 섰습니다.
"비용 관리 시스템을 만들어야 해요. 자동차에 연료 게이지가 있는 것처럼, 우리 서비스에도 비용 게이지가 필요합니다." 비용 관리란 무엇일까요?
간단히 말해 가계부입니다. 수입과 지출을 기록하고, 예산을 초과하지 않도록 관리하는 것입니다.
LLM API는 토큰 단위로 요금을 부과합니다. 토큰은 대략 영어 기준 4글자, 한글 기준 2글자 정도입니다.
"안녕하세요"라는 문장을 보내고 "반갑습니다"라는 답변을 받으면, 입력 토큰과 출력 토큰을 합쳐서 비용이 계산됩니다. 문제는 이 비용이 예측하기 어렵다는 것입니다.
사용자가 어떤 질문을 할지, LLM이 얼마나 긴 답변을 할지 알 수 없으니까요. 그래서 실시간 모니터링과 예산 한도가 필요합니다.
위 코드의 CostManager 클래스를 살펴보겠습니다. 핵심은 두 가지입니다.
can_proceed는 "이 요청을 처리해도 예산 내인가?"를 확인합니다. record_usage는 실제 사용량을 기록합니다.
daily_budget은 하루 예산입니다. 한 달 예산이 300만 원이라면, 하루 예산은 약 10만 원입니다.
이 한도를 넘어가면 can_proceed가 False를 반환하고, 추가 요청을 차단합니다. threading.Lock은 왜 필요할까요?
여러 요청이 동시에 들어오면, 동시에 usage를 수정하려고 할 수 있습니다. 이러면 숫자가 꼬입니다.
락을 사용하면 한 번에 하나의 스레드만 usage를 수정할 수 있어서 안전합니다. 실무에서는 더 정교한 전략이 필요합니다.
예를 들어 티어별 제한을 둘 수 있습니다. 무료 사용자는 하루 10회, 유료 사용자는 하루 100회처럼요.
시간대별 가중치도 유용합니다. 피크 시간대에는 간단한 모델을 쓰고, 한가한 새벽에는 고성능 모델을 쓰는 식입니다.
알림 시스템도 중요합니다. 예산의 80%를 사용하면 경고 알림을 보내고, 100%에 도달하면 긴급 알림을 보내는 것입니다.
이러면 갑자기 서비스가 중단되는 사태를 예방할 수 있습니다. 주의할 점도 있습니다.
너무 빡빡하게 제한하면 사용자 경험이 나빠집니다. "요청 한도를 초과했습니다"라는 메시지를 너무 자주 보면 사용자가 떠나갑니다.
적절한 균형을 찾는 것이 중요합니다. 김개발 씨는 비용 관리 시스템을 적용한 후, 대시보드에서 실시간으로 사용량을 확인할 수 있게 되었습니다.
예산의 90%에 도달하면 팀 슬랙에 알림이 오도록 설정했습니다. 더 이상 월말에 청구서를 보고 놀랄 일은 없었습니다.
실전 팁
💡 - 일일 예산뿐 아니라 시간당, 사용자당 제한도 함께 설정하세요
- 예산 80%, 90%, 100% 각 단계별 알림 시스템을 구축하세요
- 비용 초과 시 완전 차단보다 저비용 모델로 폴백하는 전략을 고려하세요
4. 실습 Airflow for LLM
김개발 씨의 서비스가 점점 복잡해졌습니다. 데이터 수집, 전처리, LLM 호출, 후처리, 결과 저장까지 여러 단계가 있었습니다.
수동으로 관리하다 보니 실수가 잦았습니다. "이거 자동화할 수 없을까요?" 박시니어 씨가 Airflow를 소개해주었습니다.
Apache Airflow는 워크플로를 자동화하고 스케줄링하는 플랫폼입니다. 마치 공장의 생산 라인처럼, 각 작업이 순서대로 또는 병렬로 실행되도록 관리합니다.
LLM 파이프라인을 DAG(방향성 비순환 그래프)로 정의하면, 복잡한 작업도 안정적으로 자동 실행할 수 있습니다.
다음 코드를 살펴봅시다.
from airflow import DAG
from airflow.operators.python import PythonOperator
from datetime import datetime, timedelta
default_args = {
'owner': 'llm-team',
'retries': 3,
'retry_delay': timedelta(minutes=5)
}
# DAG 정의: LLM 뉴스 요약 파이프라인
with DAG(
'llm_news_summarizer',
default_args=default_args,
schedule_interval='0 9 * * *', # 매일 오전 9시 실행
start_date=datetime(2024, 1, 1),
catchup=False
) as dag:
fetch_news = PythonOperator(
task_id='fetch_news',
python_callable=fetch_news_articles
)
summarize = PythonOperator(
task_id='summarize_with_llm',
python_callable=call_llm_summarizer
)
# 작업 순서 정의
fetch_news >> summarize
김개발 씨는 매일 아침 같은 일을 반복하고 있었습니다. 뉴스 기사를 수집하고, LLM으로 요약하고, 결과를 데이터베이스에 저장하는 작업이었습니다.
처음에는 수동으로 스크립트를 실행했습니다. 하지만 점점 지쳐갔습니다.
"어제 출장 가서 실행을 못 했더니 하루치 데이터가 빠졌어요." 김개발 씨가 한숨을 쉬었습니다. 박시니어 씨가 말했습니다.
"Airflow를 써보는 건 어때요? 워크플로 자동화의 표준이나 다름없어요." Apache Airflow란 무엇일까요?
한마디로 작업 스케줄러입니다. 하지만 단순한 cron job과는 다릅니다.
작업 간의 의존성을 관리하고, 실패 시 재시도하고, 전체 파이프라인의 상태를 모니터링할 수 있습니다. Airflow의 핵심 개념은 DAG입니다.
Directed Acyclic Graph, 즉 방향성 비순환 그래프입니다. 어려운 용어 같지만 사실 간단합니다.
작업들이 화살표로 연결되어 있고, 순환하지 않는다는 뜻입니다. 레스토랑 주방을 떠올려 보세요.
스테이크를 만들려면 먼저 고기를 꺼내고, 양념하고, 굽고, 플레이팅해야 합니다. 이 순서를 거꾸로 할 수 없습니다.
굽기 전에 플레이팅할 수 없으니까요. DAG도 마찬가지입니다.
뉴스를 수집하기 전에 요약할 수 없습니다. 위 코드를 살펴보겠습니다.
default_args에서 retries=3은 작업이 실패하면 3번까지 재시도한다는 뜻입니다. LLM API가 일시적으로 응답하지 않을 수 있으니, 재시도 로직은 필수입니다.
retry_delay는 재시도 간격입니다. 5분 후에 다시 시도합니다.
schedule_interval의 **'0 9 * * *'**는 cron 표현식입니다. 매일 오전 9시에 이 DAG를 실행하라는 뜻입니다.
출장을 가든, 휴가를 가든, Airflow가 알아서 실행합니다. catchup=False는 중요한 설정입니다.
True로 하면 과거 실행하지 못한 모든 날짜에 대해 DAG를 실행합니다. 1년 전부터 시작하면 365번 실행되는 것입니다.
보통은 False로 설정해서 이를 방지합니다. 마지막 줄의 fetch_news >> summarize가 의존성을 정의합니다.
fetch_news가 완료된 후에 summarize가 실행됩니다. 화살표 방향이 실행 순서입니다.
실무에서는 더 복잡한 DAG가 필요합니다. 예를 들어 여러 뉴스 소스를 병렬로 수집하고, 결과를 합치고, LLM으로 요약하고, 슬랙으로 알림을 보내는 식입니다.
Airflow는 이런 복잡한 워크플로도 깔끔하게 관리할 수 있습니다. Airflow의 또 다른 장점은 웹 UI입니다.
브라우저에서 DAG 실행 현황을 확인하고, 실패한 작업을 재실행하고, 로그를 확인할 수 있습니다. 터미널에서 로그 파일을 뒤지는 것보다 훨씬 편합니다.
김개발 씨는 Airflow를 도입한 후 아침마다 스크립트를 실행하는 일에서 해방되었습니다. 출근하면 이미 오늘의 뉴스 요약이 준비되어 있었습니다.
실패한 작업이 있으면 슬랙으로 알림이 왔고, 웹 UI에서 로그를 확인하고 재실행할 수 있었습니다.
실전 팁
💡 - 민감한 정보는 Airflow의 Variables나 Connections에 저장하세요
- 각 태스크는 idempotent하게, 즉 여러 번 실행해도 같은 결과가 나오도록 설계하세요
- 모니터링 알림을 설정해서 실패한 DAG를 빠르게 감지하세요
5. 실습 서버리스 배포
김개발 씨가 Airflow로 백엔드 파이프라인을 자동화하는 동안, 새로운 요구사항이 들어왔습니다. "사용자가 직접 API를 호출할 수 있게 해주세요.
그런데 서버 관리는 하고 싶지 않아요." 팀장님의 요청이었습니다. 이럴 때 딱 맞는 해결책이 있었습니다.
서버리스 배포는 서버 인프라를 직접 관리하지 않고 코드만 배포하는 방식입니다. 마치 택시를 이용하는 것처럼, 차를 직접 소유하고 관리하는 대신 필요할 때만 호출해서 사용합니다.
AWS Lambda, Google Cloud Functions 같은 서비스가 대표적입니다. 요청이 없으면 비용도 없습니다.
다음 코드를 살펴봅시다.
import json
from mangum import Mangum
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class PromptRequest(BaseModel):
prompt: str
max_tokens: int = 100
@app.post("/generate")
async def generate_text(request: PromptRequest):
# 입력 검증
if len(request.prompt) > 1000:
raise HTTPException(400, "Prompt too long")
# LLM 호출 (캐싱 및 비용 관리 적용)
result = await call_llm_with_cache(
prompt=request.prompt,
max_tokens=request.max_tokens
)
return {"response": result}
# AWS Lambda 핸들러로 변환
handler = Mangum(app)
김개발 씨는 고민에 빠졌습니다. API 서버를 만들어야 하는데, 서버를 직접 운영하려면 할 일이 너무 많았습니다.
EC2 인스턴스 설정, 보안 그룹 구성, 로드 밸런서 연결, 자동 스케일링 설정... 박시니어 씨가 말했습니다.
"서버리스로 가면 어때요? 코드만 올리면 나머지는 AWS가 알아서 해줘요." 서버리스란 무엇일까요?
이름과 달리 서버가 없는 것은 아닙니다. 다만 우리가 서버를 관리하지 않는 것입니다.
택시와 자가용의 차이를 생각해 보세요. 자가용을 소유하면 주차 공간, 보험, 정비, 세차까지 신경 써야 합니다.
반면 택시는 필요할 때 부르고, 목적지에 도착하면 내리면 됩니다. 차량 관리는 택시 회사 몫입니다.
서버리스도 마찬가지입니다. 서버 관리는 클라우드 업체가 하고, 우리는 코드만 작성합니다.
AWS Lambda가 대표적인 서버리스 서비스입니다. 코드를 올려두면, 요청이 들어올 때만 실행됩니다.
요청이 없으면 아무것도 실행되지 않고, 비용도 청구되지 않습니다. 이것을 pay-per-use 모델이라고 합니다.
위 코드에서 FastAPI는 파이썬 웹 프레임워크입니다. 빠르고 현대적이며, 타입 힌트를 활용한 자동 문서화가 장점입니다.
Mangum은 FastAPI를 Lambda와 연결해주는 어댑터입니다. 마지막 줄의 **handler = Mangum(app)**이 마법을 부리는 부분입니다.
PromptRequest 클래스는 Pydantic 모델입니다. 요청 데이터의 형식을 정의하고 자동으로 검증합니다.
prompt가 문자열이 아니거나 max_tokens가 정수가 아니면 자동으로 에러를 반환합니다. 입력 검증도 중요합니다.
len(request.prompt) > 1000 체크는 너무 긴 프롬프트를 막습니다. 악의적인 사용자가 엄청나게 긴 문자열을 보내 비용을 폭증시키는 것을 방지합니다.
서버리스의 또 다른 장점은 자동 스케일링입니다. 요청이 갑자기 100배로 늘어도 Lambda가 알아서 인스턴스를 늘려 처리합니다.
요청이 줄어들면 다시 줄입니다. 우리가 신경 쓸 필요가 없습니다.
하지만 단점도 있습니다. 콜드 스타트라는 문제입니다.
한동안 요청이 없으면 Lambda 인스턴스가 종료됩니다. 다음 요청이 들어오면 새로 인스턴스를 시작해야 해서 첫 응답이 느립니다.
보통 1-2초 정도 지연됩니다. 이를 해결하는 방법으로 Provisioned Concurrency가 있습니다.
미리 인스턴스를 켜두는 것입니다. 콜드 스타트는 없지만 비용이 추가됩니다.
트레이드오프를 고려해서 결정해야 합니다. 김개발 씨는 서버리스로 API를 배포한 후 한숨 돌렸습니다.
EC2 인스턴스를 모니터링하고 패치를 적용하는 일에서 해방되었습니다. 덕분에 더 중요한 일, 즉 서비스 자체를 개선하는 데 집중할 수 있게 되었습니다.
실전 팁
💡 - 콜드 스타트가 문제라면 Provisioned Concurrency를 고려하세요
- Lambda의 타임아웃(기본 3초, 최대 15분)과 메모리 설정을 적절히 조정하세요
- API Gateway와 함께 사용하면 인증, Rate Limiting 등을 쉽게 추가할 수 있습니다
이상으로 학습을 마칩니다. 위 내용을 직접 코드로 작성해보면서 익혀보세요!
댓글 (0)
함께 보면 좋은 카드 뉴스
Context Fundamentals - AI 컨텍스트의 기본 원리
AI 에이전트 개발의 핵심인 컨텍스트 관리를 다룹니다. 시스템 프롬프트 구조부터 Attention Budget, Progressive Disclosure까지 실무에서 바로 적용할 수 있는 컨텍스트 최적화 전략을 배웁니다.
Phase 1 보안 사고방식 구축 완벽 가이드
초급 개발자가 보안 전문가로 성장하기 위한 첫걸음입니다. 해커의 관점에서 시스템을 바라보는 방법부터 OWASP Top 10, 포트 스캐너 구현, 실제 침해사고 분석까지 보안의 기초 체력을 다집니다.
워크플로 모니터링과 디버깅 완벽 가이드
LLM 기반 워크플로의 실행 상태를 추적하고, 문제를 진단하며, 성능을 최적화하는 방법을 다룹니다. LangSmith 통합부터 커스텀 모니터링 시스템 구축까지 실무에서 바로 적용할 수 있는 내용을 담았습니다.
LlamaIndex Workflow 완벽 가이드
LlamaIndex의 워크플로 시스템을 활용하여 복잡한 RAG 파이프라인을 구축하는 방법을 알아봅니다. 이벤트 기반 워크플로부터 멀티 인덱스 쿼리까지 단계별로 학습합니다.
LangChain LCEL 완벽 가이드
LangChain Expression Language(LCEL)를 활용하여 AI 체인을 우아하게 구성하는 방법을 배웁니다. 파이프 연산자부터 커스텀 체인 개발까지, 실무에서 바로 활용할 수 있는 핵심 개념을 다룹니다.