[4개월 개발기] 에피소드 7 — 증류와 서빙: PLE → LightGBM → Lambda + 5 Bedrock 에이전트
EN English version →“4개월간의 금융 AI 개발기” 7편. 에피소드 4에서 6까지 땀 흘려 깎고 다듬은 모델 아키텍처의 이론적 뼈대를 정리했다면, 이번 편은 그 거창한 모델이 실제 고객의 스마트폰 화면 앞까지 도달하는 생생한 경로, 즉 증류(Distillation)와 서빙(Serving)에 대한 이야기다. 대형 서버 하나 굴릴 예산도 벅찬 3인 규모의 소형 팀이, 어떻게 AWS Lambda라는 제약투성이 환경 위에서 실시간 금융 추천을 원활하게 쏘아댈 수 있었는지 그 지속적인 엔지니어링의 민낯을 공개한다.
무거운 교사(PLE)와 날쌘 학생(LightGBM)
우리가 훈련시킨 챔피언 모델의 스펙은 화려하다. PLE 뼈대 위에 7개의 이질적인 전문가(Expert) 네트워크를 얹고, CGC 게이팅으로 묶어낸 뒤, 무려 13개의 태스크 타워를 세웠다. 에피소드 4에서 자랑했듯 파라미터 수는 2M(200만) 미만이라 동급 딥러닝 모델치고는 꽤나 날씬한 편이다. 하지만 이 녀석을 프로덕션 서빙(Serving) 환경에 그대로 밀어 넣기엔 넘어야 할 주요한 산이 세 개나 버티고 있었다.
문제 1 — PyTorch 추론(Inference) 런타임의 저주. AWS Lambda 같은 서버리스 환경에서 무거운 PyTorch 런타임을 초기화하는 데 걸리는 콜드 스타트(Cold Start) 시간은 가볍게 2–3초를 잡아먹는다. 모바일 앱을 켠 고객이 단 하나의 상품 추천을 받기 위해 3초 동안 단순히 로딩 스피너를 바라보게 할 수는 없는 노릇이다.
문제 2 — 메모리 점유율(Footprint)의 압박. 7개의 전문가 모듈과 CGC 게이트, 13개의 태스크 타워를 모두 메모리에 우겨넣으면 전체 모델의 로딩 용량이 약 150MB에 달한다. Lambda 환경에서 동시다발적으로 수십, 수백 개의 인스턴스가 뜰 때마다 이 용량을 복제해야 한다면, 우리의 단순한 메모리 예산으로는 턱도 없는 일이었다.
문제 3 — 해석 가능성(Interpretability)의 장벽. 고객이 불만을 제기하거나 금융감독원이 감사를 나왔을 때, “도대체 왜 이 고객에게 이 상품을 추천했는가?”라는 뾰족한 질문에 명쾌하게 답할 수 있어야 한다. PLE의 전문가 게이트 가중치(Weight)를 뜯어보면 어느 정도 해석은 가능하지만, 금융권의 딱딱한 보고서에 담기에는 직관적이지 못하다. 반면 트리 기반의 LightGBM은 개별 트리와 피처들의 기여도(Feature Attribution)를 즉석에서 깔끔하게 쪼개서 보여주는 데 특화되어 있다.
이 세 가지 골치 아픈 문제를 해결하는 유일한 정답. 그것은 바로 **‘각 태스크별로 분리된 LightGBM 모델로 지식을 증류(Distillation)하는 것’**이었다. 교사(Teacher) 역할인 묵직한 PLE 모델이 13개의 태스크 각각에 대해 정교한 소프트 확률값(Soft Probability)을 생성해 내면, 날쌘 학생(Student) 역할인 LightGBM이 이 확률값을 타겟(Target)으로 삼아 빠르게 피팅(Fitting)하며 지식을 쏙쏙 빨아먹는 구조다. 그 결과는 우수했다. Lambda의 콜드 스타트는 300ms 이내로 줄어들었고, 메모리 점유율은 30MB 수준으로 쪼그라들었으며, 그토록 원하던 피처 기여도 해석 기능은 보너스(Built-in)로 딸려왔다.
교사-학생 간의 최소 충실도 기준(Fidelity Floor)
지식 증류 과정에서 목숨을 걸고 사수해야 하는 가장 핵심적인 지표는 바로 **충실도(Fidelity)**다. 쉽게 말해 ‘학생이 교사의 지혜를 얼마나 충실하게 모방하고 있는가’를 따지는 척도다. 에피소드 2에서 챔피언-챌린저 게이트를 설명할 때, “최소 충실도 기준(Fidelity Floor)을 통과하지 못하면 성능 경쟁을 시작하기도 전에 챌린저를 즉시 폐기한다”라고 으름장을 놓았던 바로 그 기준이다.
충실도는 13개 태스크 각각에 대해 학생 모델과 교사 모델 간의 쿨백-라이블러 발산(KL Divergence)을 계산하여 측정된다. 이 까다로운 임계치는 설정(Config) 파일에 distillation.fidelity_floor라는 이름으로 기본 0.20 값이 세팅되어 있다. 만약 13개의 태스크 중 단 하나라도 이 0.20이라는 선을 넘어가면, 그 챌린저 묶음 전체의 승격이 그 자리에서 즉각 거부(Reject)된다.
왜 이렇게까지 편집증적으로 충실도에 집착할까? 학생 모델이 제아무리 훌륭한 학습 메트릭(AUC 등)을 뽑아낸다 한들, 애초에 교사 모델과 질적으로 다른 함수 경로를 타버렸다면 그건 더 이상 우리가 의도한 모델이 아니기 때문이다.
기존의 지식 증류 관련 논문들을 뒤적이다 보면, 종종 “결과적으로 학생 모델이 교사 모델의 성능을 뛰어넘는(Surpass) 기현상이 발생했다!”라며 호들갑을 떠는 연구 결과들을 볼 수 있다. 하지만 규제가 겹겹이 쳐진 금융 추천의 세계에서는 이것이 축포를 터뜨릴 일이 아니라, 등골이 서늘해지는 성공적인 **위험 신호(Red Flag)**다. 교사 모델인 PLE는 공정성, 규제 준수, 다중 태스크 간의 미묘한 균형이라는 묵직한 제약 조건들을 온몸으로 견뎌내며 조심스럽게 빚어낸 예술 작품이다. 그런데 철없는 학생 모델이 단지 눈앞의 정확도(Accuracy) 성능을 쥐어짜기 위해 이런 제약 조건들을 제멋대로 *우회(Bypass)*해 버렸다면? 애초에 우리가 설계했던 방어선 전체가 붕괴되는 꼴이다.
그러므로 학생 모델의 존재 이유는 오직 교사 모델의 훌륭한 결정을 ‘최대한 가깝고 값싸게 복제(Clone)‘하는 것이지, 눈치 없이 교사를 능가하며 튀어 오르는 것이 절대 아니다. 최소 충실도 기준(Fidelity Floor)은 이 철학적인 원칙을 코드의 레벨에서 물리적, 구조적으로 강제하는 가장 강력한 족쇄다.
교사 임계치 게이팅(Teacher Threshold Gating) — 최소 2배의 랜덤 베이스라인
하지만 안타깝게도 세상 모든 태스크가 예쁘게 증류(Distillation)되는 것은 아니다. 교사 모델(PLE) 자신의 예측 성능조차 매우 낮은 복잡한 태스크를 억지로 학생에게 가르치려 들면, 학생 모델은 의미 있는 지식이 아니라 교사가 흘리는 오류 노이즈(Noise)까지 맹목적으로 학습해 버리는 참사가 발생한다.
이 비극을 막기 위해 우리는 설정 파일에 distillation.teacher_threshold라는 깐깐한 수문장을 세워두었다. 기본값은 ‘랜덤 베이스라인의 최소 2배 성능’이다. 이 허들을 넘지 못하는 열등한 태스크에 대해서는 과감하게 증류(Soft Label Learning)를 포기해 버린다. 대신 학생인 LightGBM이 엉뚱한 교사를 쳐다보지 않고 원본 정답지(Hard Label)를 직접 보고 혼자서 독학(Direct Learning)하도록 경로를 강제로 비틀어 버린다. 일종의 MRM(모델 리스크 관리) 방어 기제다.
예를 들어, 찍어도 반은 맞히는 이진 분류(Binary) 태스크의 랜덤 베이스라인 AUC가 0.5라고 치자. 그렇다면 교사 모델의 AUC가 최소한 1.0이라는 높은 기준(실제로는 1.0 미만의 적절한 성능 배수를 사용)에 도달하거나 유의미한 성능 마진을 보여주어야만 비로소 학생에게 가르침을 전수할 자격(증류 적용)이 주어진다는 뜻이다. 그 자격에 미달하면 곧바로 하드 레이블(Hard Label) 학습 폴리곤으로 내동댕이쳐진다. 이것이 프로덕션 환경에서 “선무당 교사의 오류까지 달달 외우는 성능이 낮은 학생 모델”이 양산되는 것을 구조적으로 틀어막는 우리의 비법이다.
서비스 중단을 막는 3계층 폴백(Fallback) 방어선
치열한 운영(Operation) 환경에서 모든 시나리오가 톱니바퀴처럼 예쁘게 맞물려 돌아갈 것이라 믿는 건 삼류 소설에나 나올 법한 순진한 착각이다. 학생 모델의 증류가 실패할 수도 있고, API로 들어오는 입력 데이터의 스키마가 예고 없이 틀어질 수도 있으며, 모델을 담아둔 바이너리 파일이 깨져버릴(Corruption) 수도 있다. 그래서 우리는 단 1초의 서비스 중단(Downtime)도 허용하지 않기 위해 심각한 3계층 폴백(3-Layer Fallback) 방어선을 깔아두었다.
- Layer 1 (쾌속의 정문): 무거운 PLE에서 엑기스만 뽑아낸 LGBM 증류 모델이 처리한다. 정상적인 상황이라면 99% 이상의 우수한 트래픽이 이 막힘없는 고속도로를 탄다.
- Layer 2 (독학파의 샛길): 만약 특정 태스크에서 증류 과정이 실패하여(앞서 말한 충실도 기준 미달 등) 학생 모델이 뻗어버리면, 원본 데이터를 직접 독학한 투박한 LGBM 모델이 대타로 투입되어 예측을 뱉어낸다.
- Layer 3 (최후의 보루): 딥러닝이고 나발이고 AI 모델 전체가 통째로 뻗어버린 심각한 오류 상황이 닥치면, 사전에 안전하게 하드코딩해 둔 ‘금융 DNA 기반 비즈니스 룰(Rule)‘이 최후의 방패로 나선다. 고객의 연령대, 자산 규모, 최근 거래 이력 등만 훑어보고 극도로 보수적이고 방어적인 추천 결과라도 어떻게든 짜내어 반환한다.
에피소드 5에서 인간 상담원에게 토스해버리는 Layer 4(인적 폴백)를 잠시 언급했지만, 그것은 어디까지나 선택적(Opt-in) 도입 사항이다. 반면 이 Layer 1, 2, 3 방어선은 시스템이 켜지는 순간 무조건 기본으로 활성화(Default Active)된다. 이 견고한 계층 구조는 “가동률 99.9% 보장”이라는 어려운 SLA(서비스 수준 협약) 요구사항을 말장난이 아니라 ‘아키텍처의 레벨’에서 물리적으로 보증한다. 우리 시스템에는 단일 장애점(Single Point of Failure) 따위는 존재하지 않는다.
AWS Lambda와 5명의 Bedrock 에이전트 오케스트레이션
이제 모든 조각을 맞춘 서빙 스택의 전체적인 위용을 감상해 보자.
[은행 영업점의 클라이언트 시스템]
↓
[API Gateway] (요청 접수)
↓
[AWS Lambda] (Python 3.11, 1GB 메모리 할당)
↓
├─ LightGBM 학생 모델 로드 (든든한 3계층 폴백 대기)
├─ Feature Selector 에이전트 (Bedrock Claude Sonnet)
├─ Reason Generator 에이전트 (Bedrock Claude Sonnet)
├─ Safety Gate 에이전트 (Bedrock Claude Sonnet)
└─ 최종 예측값 반환 + 에피소드 3의 깐깐한 감사 로그 기록
[보이지 않는 비동기(Asynchronous) 경로]
├─ OpsAgent (Bedrock Claude Sonnet) — 야간 알람 시 CloudWatch 로그 씹어먹고 해석
└─ AuditAgent (Bedrock Claude Sonnet) — 매일 밤 외롭게 돌며 HMAC 해시 체인(Hash Chain) 위변조 검증
여기서 각자의 무대에서 활약하는 5명의 Bedrock 에이전트들의 치열한 역할 분담을 살펴보자.
1. Feature Selector (Claude Sonnet) LightGBM 모델이 기계적으로 뱉어낸 수십 개의 밋밋한 피처 기여도(Feature Attribution) 값들 중에서, **‘고객에게 설명했을 때 고개를 끄덕일 만한 가치가 있는 놈들’**만 예리하게 골라낸다. 예를 들어 “최근 4개월간 해외 직구 3회 발생”이라는 피처는 훌륭한 설명 근거가 되지만, “Feature #247 (의미를 알 수 없는 파생 변수)” 따위는 고객에게 들이밀어 봐야 욕만 먹을 뿐이다. 모델 내부의 투박한 숫자(Raw Attribution)를 비즈니스적으로 말이 되는 의미 있는 덩어리(Business-meaningful Set)로 세련되게 변환하는 1차 가공 단계다.
2. Reason Generator (Claude Sonnet) 앞서 선별된 핵심 피처들을 재료로 삼아, 은행 영업점 창구 직원이 눈앞의 고객에게 유창하게 읽어줄 수 있는 자연스럽고 정중한 금융 경어체 한국어 문장을 맛깔나게 지어낸다. 단순히 빈칸을 채우는 템플릿(Template) 따위가 아니다. *“고객님의 최근 4개월 소비 패턴을 분석한 결과, 해외 가맹점 결제 비중이 꾸준히 증가하고 계시어 환율 우대 혜택이 강력한 외화 예금 상품을 제안해 드립니다”*처럼, 고객의 세밀한 컨텍스트에 맞춰 문장의 톤과 매너를 정교하게 튜닝한다.
3. Safety Gate (Claude Sonnet) 생성된 유려한 설명 문구가 세상 빛을 보기 전, 마지막으로 거쳐야 하는 피도 눈물도 없는 혹독한 검열 관문이다. 이 깐깐한 에이전트는 다음 5가지 기준을 충족하는지 돋보기를 들이댄다. (a) 규제 요건: 자본시장법 등을 위반하는 위험한 광고 제한어(수익 보장, 절대 안전 등)가 없는가? (b) 적합성: 추천된 상품이 고객의 투자 리스크 허용도(Risk Tolerance)를 아슬아슬하게 넘어서진 않았는가? (c) 환각(Hallucination) 방지: 에이전트가 지어낸 그럴듯한 설명이 실제 LightGBM이 뱉어낸 피처 값의 팩트와 정확히 일치하는가? (d) 어조 적절성: 고객을 가르치려 드는 고압적이거나 부담스러울 정도로 과장된 톤은 아닌가? (e) 사실성 검증: 문장에 언급된 상품의 명칭과 금리 조건이 은행 내부 DB의 팩트와 한 치의 오차도 없이 일치하는가? 이 엄격한 5중 필터를 모두 통과해야만, 문장은 비로소 Lambda 핸들러의 문턱을 넘어 고객의 화면으로 향할 수 있다.
4. OpsAgent (Claude Sonnet) 메인 서빙 경로에서 벗어나 비동기(Asynchronous)로 조용히 움직인다. 매일 밤, 혹은 당직 엔지니어의 긴급 호출(On-call Trigger)이 떨어지면 깨어난다. 드리프트 모니터나 공정성 모니터가 토해내는 난해한 로그들을 빠르게 씹어먹고는, SRE(사이트 신뢰성 엔지니어링), MLOps, 비즈니스 관점에서 상황을 찰떡같이 요약해 브리핑한다. 만약 킬 스위치가 자동으로 즉시 떨어지는 비상사태가 발생하면, 당황한 엔지니어를 대신해 상황을 조리 있게 정리한 인시던트(Incident) 티켓 초안을 빠르게 써서 결재 큐에 올려둔다.
5. AuditAgent (Claude Sonnet) 에피소드 3에서 깊게 다루었던 그 외로운 밤의 파수꾼이다. 매일 자정이 넘으면 깨어나, 에피소드 3에서 엮어둔 그 기나긴 HMAC 해시 체인(Hash Chain)을 처음부터 끝까지 훑으며 위변조된 흔적이 없는지 재검증한다. 체인이 끊어진 지점이 발견되면 즉시 OpsAgent로 인시던트를 에스컬레이션한다.
운영(Ops)과 감사(Audit)의 분리 — 왜 굳이 두 명의 에이전트인가
처음 아키텍처를 스케치할 때는 효율을 핑계로 OpsAgent와 AuditAgent를 하나로 합치려는 단순한 유혹이 있었다. 어차피 둘 다 밤에 깨어나 “로그(Log) 더미를 읽고 분석하는” 비슷한 일을 하니까 말이다.
하지만 우리는 끝내 타협하지 않고 이 둘을 매몰차게 분리했다. 가장 큰 이유는 두 에이전트가 뱉어내는 보고서의 ‘독자(Reader)‘가 완전히 다르기 때문이다. OpsAgent가 요약해 주는 *“현재 추천 모델의 성능 드리프트 지표가 위험 임계치에 근접했습니다”*라는 운영 관점의 경고는 시스템을 고쳐야 할 SRE나 MLOps 엔지니어들이 읽는다. 반면, AuditAgent가 비상벨을 울리며 토해내는 *“어제 새벽 2시에 쌓인 해시 체인 엔트리 1,425번에서 암호화 검증 실패(위변조 의심)가 떴습니다”*라는 감사 관점의 경고는 규제 당국의 규제을 방어해야 할 컴플라이언스(Compliance)나 리스크 관리 팀이 읽는다.
더 본질적인 이유는 두 에이전트가 서 있는 ‘신뢰 경계(Trust Boundary)’ 자체가 근본적으로 다르다는 점이다. OpsAgent는 시스템이 뱉어낸 로그를 100% 진실이라 믿고 이를 친절하게 *‘읽고 해석’*하는 데 집중한다. 반면 AuditAgent는 그 누구도, 심지어 로그 자체도 믿지 않은 채 그 로그가 조작되지 않았는지를 지속적으로 *‘의심하고 재검증’*하는 것이 존재 이유다. 이 좁힐 수 없는 목적의 차이는 두 에이전트에게 쥐여주는 프롬프트(Prompt) 디자인 자체에 노골적으로 스며들어 있다. 섣불리 하나로 합쳐버렸다간, 친절한 운영 도우미가 될지 냉혹한 감사관이 될지 갈피를 못 잡고 둘 중 하나의 역할이 희석되고 말았을 것이다.
이 거대한 스택은 실제로 어떻게 코딩되었는가
위에서 쭉 읊어 내려온 이 매끄러운 서빙 스택의 구조를 읽다 보면, 마치 AWS 본사에서 파견 나온 수석 아키텍트 팀이 몇 달간 화이트보드에 그림을 그려가며 성공적으로 찍어낸 마스터플랜처럼 보일 것이다. 하지만 진실을 고백하자면, 이 거대한 스택의 뼈대와 살점은 오직 우리 3명의 팀원과 Claude Code가 뒤엉켜 싸운 수십 개의 터미널 세션 안에서 날것 그대로 깎이고 다듬어졌다.
Lambda 핸들러의 핵심 모듈은 무려 30회가 넘는 어려운 이터레이션(Iteration)을 거쳤다. 버전 1(v1)은 불과 60줄짜리 조악한 Claude Code 초안에 불과했다. 기껏해야 LightGBM 모델 하나를 끙끙대며 로드하고, 부정확한 예측값 하나 덜렁 뱉어내는 게 전부였다. 그러나 숱한 밤을 지새운 끝에 도달한 버전 30(v30)은 400줄이 훌쩍 넘는 거대한 요새가 되어 있었다. 3계층 폴백 방어선, 깐깐한 암호화 감사 로깅, 5명의 Bedrock 에이전트들을 지휘하는 오케스트레이션, 심지어 결정론적인 콜드 스타트 예열(Warming) 로직까지 전부 이 안에 들어찼다.
v1에서 v30으로 진화하는 그 험난한 과정은, 멋진 회의실에서의 기획이 아니라 프로덕션 환경이 토해내는 실패(Failure) 사례들의 연속이었다. 어느 날 오후에는 심각한 타임아웃(Timeout) 에러가 터졌고, 다음 날 새벽에는 AWS IAM 권한 거부(Access Denied)로 뻗어버렸으며, 주말에는 예기치 못한 엣지 케이스(Edge Case)의 스키마 변경이 시스템의 문제를 일으켰다. 이 심각한 실패 하나하나가 곧바로 새로운 Claude Code 세션의 치열한 주제가 되었고, 밤샘 토론 끝에 깃허브 PR(Pull Request)로 꿰매지며 종결되는 어려운 과정의 무한 반복이었다.
거창해 보이는 ‘3계층 폴백’ 방어선 역시 애초에 고상하게 설계된 것이 아니다. 프로젝트 초반 2주 동안은 오직 얇디얇은 Layer 1 하나만 덩그러니 서 있었다. 그러다 task_churn (이탈 예측) 태스크의 증류 과정이 완전히 실패해 버리면서 (에피소드 2에서 다루었던 그 심각한 최소 충실도 기준 위반으로 인한 강제 거부 사태), “이탈 예측 증류 모델이 뻗어버렸을 때 어떻게든 땜빵을 칠 샛길이 절실하다!”는 비명 속에서 부랴부랴 Layer 2가 긴급 공수되었다.
최후의 보루인 Layer 3의 탄생 비화는 더 어렵다. 어느 날 Phase 0의 데이터 스키마 변경 사항이 잘못 전파되는 대형 사고(Incident)가 터지면서, 딥러닝은 고사하고 LightGBM 모델 로딩 자체가 오후 내내 뻗어버린 심각한 오류이 있었다. “모델이 다 죽어자빠졌을 때, 차라리 멍청하고 투박한 비즈니스 룰 기반의 추천이라도 어떻게든 통합하여야 고객 화면에 에러를 안 띄운다!”라는 깨달음 속에서 눈물로 짜낸 코드가 바로 Layer 3다. 이토록 정교해 보이는 각 계층의 방어선들은, 사실 과거에 우리가 피터지게 두들겨 맞았던 어려운 실패들이 남긴 흉터 자국에 불과하다.
5명의 에이전트 오케스트레이션 역시 처음부터 성공적인 라인업을 갖춘 것이 아니라 진흙탕 속에서 점진적으로 자라났다. 가장 먼저 태어난 녀석들은 Feature Selector와 Reason Generator였다. 이는 Paper 2의 핵심 요구사항이었던 “고객에게 추천의 근거를 친절하게 설명하라”는 규제적 압박에서 직관적으로 도출된 녀석들이다.
반면 Safety Gate 에이전트의 탄생은 출시 직전의 엄격한 코드 리뷰 덕분이다. 인간 엔지니어가 (물론 AI의 매서운 도움을 받아) Reason Generator가 가끔씩 오류(사실 오류)를 뱉어내는 40여 개의 유의미한 엣지 케이스를 지속적으로 찾아냈고, 이 환각(Hallucination)들을 막아낼 방패가 시급했다. Safety Gate가 쥐고 있는 5가지 검열 규칙은 바로 그 어려운 리뷰 결과에서 날것 그대로 추출된 것이다. OpsAgent는 새벽 3시에 시끄럽게 울려대는 PagerDuty 알람을 끄고 비몽사몽간에 수동으로 상황을 파악(Triage)하느라 40분 넘게 낭비했던 어느 저주받은 새벽 직후에 분노 섞인 타이핑으로 추가되었다. 가장 마지막에 합류한 AuditAgent는 에피소드 3의 지속적인 ‘증거 보존 사슬(Chain-of-Custody)’ 요구사항과 톱니바퀴처럼 맞물리며 탄생했다.
이 유의미한 기능들 중 그 어느 하나도, 프로젝트 초기부터 우아하게 화이트보드에 선형적으로 계획된 것은 없었다. 기능 하나가 추가될 때마다 그것은 철저하게 현장에서 관측된 구체적이고 주요한 문제에 대한 발악적 해결책이었다. Claude Code가 묵묵히 구현체 코드를 쏟아내는 동안, 우리는 Opus와 함께 그 코드의 근본적인 설계 구조를 놓고 치열한 토론을 벌였다. [관측 → Opus와의 구조적 대화 → Claude Code의 엄격한 구현 → 프로덕션 배포 → 또 다른 문제의 관측]. 3명의 소형 팀이 단 4개월 만에 이 거대하고 촘촘한 서빙 스택을 성공적으로 완성해 낸 방식은, 바로 이 빠르게 회전하는 이터레이션(Iteration)의 톱니바퀴 그 자체였다.
Lambda 비용 — 왜 무조건 서버리스(Serverless)여야만 했는가
우리가 학습 단계에서 굴렸던 RTX 4070 GPU의 힘을 빌려 그 무거운 모델을 24시간 365일 쉴 새 없이 실시간 서빙(Serving)하려면? 척 봐도 값비싼 전용 GPU 서버 인프라를 대규모로 구축해야 한다는 답이 나온다. 3명 남짓한 팀의 쪼들리는 예산으로는 시작하기도 전에 파산 선고를 받을 짓이다. 하지만 AWS Lambda와 가벼운 LightGBM의 조합은 이 절망적인 풍경을 완전히 뒤바꿔 놓는다.
- 철저한 종량제 (요청 건당 과금): 새벽 시간대나 트래픽(RPS)이 저조한 구간에서는, 단순하게 돌아가는 고정 서버 비용이 사실상 ‘0원’에 수렴한다.
- 초고속 콜드 스타트 (300ms 이내): 무거운 파이토치 대신 가벼운 LightGBM을 로드하는 덕분에, 대부분의 금융 서비스가 요구하는 깐깐한 추천 응답 속도(SLA)를 가뿐히 방어해 낸다.
- 안전한 비용 상한선 (동시 실행 제한): 트래픽이 폭주하여 Lambda 인스턴스가 무한정 뻗어나가는 것을 막기 위해, 설정(Config) 값으로 동시 실행 수의 제어하여 예산 폭탄을 원천 차단한다.
계산기를 두드려보자. 월간 100만 건의 추천 요청이 몰아친다고 넉넉히 가정했을 때의 예상 비용 청구서다. AWS Lambda 연산 비용 약 3 + S3 스토리지 및 CloudWatch 로그 유지 비용 약 30 미만**이라는 뛰어난 숫자가 나온다. 값비싼 전용 GPU 서버를 임대해 굴리는 유지비의 무려 1/100 수준으로 쪼그라든 셈이다.
바로 이것이 **‘소비자용 저가 GPU(학습용) + 극단적인 서버리스 아키텍처(서빙용)‘**라는 특이하지만 뛰어난 조합이, 막대한 자본력을 갖추지 못한 한국 금융권의 중소 규모 ML 팀에게 허락된 유일한 구원의 동아줄인 이유다. 어려운 학습(Training)은 저가 GPU 위에서 딱 한 번만 고통스럽게 끝내버리고, 끝없이 이어질 서빙(Serving)은 깃털처럼 가벼운 서버리스 환경에 얹어 영구적으로 굴린다. 수억 원대 규모의 거대한 인프라 결재 기안을 올리지 않고도, 당당하게 프로덕션 레벨의 금융 AI를 실전에서 굴릴 수 있다는 짜릿한 증명이다.
다음 편
이어지는 **에피소드 8 (FinAI Build 프로젝트의 최종편)**에서는, 지난 4개월의 성과 이면에 묻혀 있던 **‘작동하지 않은 것들’**에 대한 솔직한 기록을 다룬다. 13개 태스크 규모에서 Null(무의미한) 효과로 수렴하여 미채택된 adaTT 구조, 12GB VRAM 한계를 넘지 못해 기각된 GradSurgery 기법, 여전히 WIP 상태로 남아 있는 Paper 3 (Loss Dynamics), 그리고 2026년 4월 30일부터 수집이 시작된 실 프로덕션 메트릭의 현재 상태까지. **‘정직한 실패(Honest Negative Results)‘**의 기록으로 4개월의 여정을 마무리한다.
원문 자료: Paper 2 (Zenodo) §3 “서빙 아키텍처” + §8 “에이전트 설계”, 구현은 오픈소스 레포.