모든 기록
Capstone · 2026.09.06

문서 요약 서비스의 토큰 비용을 줄이고 응답 시간은 그대로 유지한 방법

AI 문서 편집·요약 서비스의 토큰 비용을 70% 줄이면서 응답 시간은 그대로 유지한 트러블슈팅 기록.

문서 요약 서비스의 토큰 비용을 줄이고 응답 시간은 그대로 유지한 방법 대표 이미지

캡스톤 프로젝트에서 AI 문서 편집·요약 서비스를 만들며 겪은 트러블슈팅 기록입니다. "우리가 파는 건 LLM이 아니라 문서"라는 정의에서 출발해, 전처리 책임 분리와 모델 이중화, 병렬 호출까지 도달한 과정을 정리했습니다.

성과

해당 포스팅에서 진행한 결과 Sonnet 4.5의 토큰량을 기존 0.01$ 에서 0.003$로 약 70% 감소한 것으로 확인하였습니다.

들어가며

캡스톤 프로젝트로 AI를 통한 문서 편집 및 요약 서비스를 만들었습니다. 사용자가 내용 문서와 양식 문서를 넣으면, 양식에 맞춰 정리된 문서를 생성해주는 서비스입니다.

프로젝트에서 제가 맡은 범위는 다음과 같습니다.

  • 기획 / PM — 회의 진행과 주제 선정, UX 구조 개편, 일정 관리를 통한 목표 관리
  • 백엔드 — CRUD 구현, N+1 문제 등 호출 로직 점검, ERD 설계, 문서 편집·관리 파이프라인 설계, 문서 버전 일관성 문제 해결, DI 구조 도입을 통한 의존성 분리, 에러 처리, DB FK 대신 관계 관리 테이블 형태로 전환
  • AI 챗봇 — LangGraph 기반 문서 편집 챗봇 아키텍처 설계, 룰 베이스 챗봇을 LangChain tool 호출 방식으로 전환, RedisSaver를 통한 대화 내역 관리
  • CI/CD — 백엔드와 챗봇의 Docker + Jenkins + GitHub 파이프라인 구축, EC2 Static IP와 Gabia 도메인 연동

이 글에서는 그중 요약 기능의 토큰 비용 문제를 어떻게 풀었는지 다룹니다.

문제 상황: 한 번 돌릴 때마다 토큰이 너무 많이 나간다

초기 구조는 단순했습니다. 사용자가 올린 PDF를 그대로 Claude Sonnet 4.5에 넘기고, 결과 문서를 받아오는 방식이었습니다.

측정해보니 이랬습니다.

항목
모델Claude Sonnet 4.5
InputPDF 논문 2편 + 양식 활용 자료 1편
1회 소비 비용약 $0.01

한 번에 $0.01이면 작아 보이지만, 요약은 이 서비스의 핵심 기능입니다. 사용자가 반복해서 누를 기능의 단가가 이 정도라면 서비스 규모가 커질수록 그대로 비용 구조가 됩니다.

원인 추적: 이 토큰은 어디에 쓰이고 있는가

가장 먼저 한 일은 "토큰을 줄이자"가 아니라 **"토큰이 어디에서 소비되는가"**를 확인하는 것이었습니다.

확인 결과, PDF라는 확장자 자체가 문제였습니다. PDF를 통째로 넘기면 레이아웃, 폰트, 좌표 같은 정보까지 함께 들어갑니다. 그런데 요약 서비스에 필요한 정보는 텍스트와 양식뿐입니다. 토큰 소비는 크게 늘리면서 결과물에는 기여하지 않는 데이터가 상당량 섞여 있었던 겁니다.

해결 1: 전처리 책임을 AI 서버에서 백엔드로 옮기다

그렇다면 PDF를 분해해서 내용과 양식을 추출하는 단계가 필요합니다. 문제는 이 책임을 어디에 둘 것인가였습니다.

저는 이 작업이 AI 파트의 책임이 아니라고 봤습니다. 이유는 두 가지입니다.

  1. PDF 파싱은 CPU Bound일 가능성이 높은 연산입니다. 이걸 AI 서버 인스턴스가 떠안으면 AI 기능 자체의 성능 저하와 서버 불안정으로 이어집니다.
  2. 애초에 전처리 과정에는 AI의 기능이 전혀 사용되지 않습니다. AI가 하지 않는 일을 AI 서버가 책임질 이유가 없습니다.

그래서 백엔드 로직에서 PDF를 분해해 텍스트와 양식을 추출하도록 파이프라인을 변경했습니다.

해결 2: 요약에 Sonnet은 과했다 — 모델 이중화

전처리를 분리하고 나니 다음 질문이 생겼습니다. 이 단계에 Claude Sonnet 4.5가 정말 필요한가?

이 파트의 역할은 최종 결과물이 아니라 초안입니다. 전체 레이아웃과 내용의 방향성을 잡는 것이 중요하지, 단순 요약을 수행하기에 Sonnet 4.5는 크기가 비대하다고 판단했습니다.

그래서 구조를 이렇게 나눴습니다.

  • 1단계 (Gemini 1.5 Flash) — 내용 문서를 요약해 핵심만 추출
  • 2단계 (Claude Sonnet 4.5) — 요약된 내용 + 문서 특징 + 양식 추출 데이터를 받아 사용자에게 보여질 최종 문서 생성

여기서 효과를 크게 본 건 프롬프트를 문서 종류별로 나눈 것이었습니다. 문서의 특징에 맞는 프롬프트를 쓰면 가벼운 모델로도 내용이 충분히 살아있다는 것을 확인했고, 보고서 / 논문 / 회의록 각각에 다른 프롬프트를 적용할 수 있게 변경했습니다.

이 판단은 UX에도 반영됐습니다.

  1. 사용자가 문서를 업로드한다
  2. 해당 문서가 양식인지 내용인지 선택한다
  3. 내용이라면 보고서 / 회의록 / 논문 중 종류를 고른다

해결 3: 느려진 응답 시간을 병렬화로 되돌리다

토큰 비용과 생성 안정성은 잡았지만, 새로운 문제가 생겼습니다. 모델을 순차적으로 두 번 태우다 보니 대기 시간이 1분 이상 늘어난 것입니다.

원인은 명확했습니다. 입력 문서가 여러 개인데 이를 하나씩 처리하고 있었습니다. 기존 입력 기준으로 내용 문서 2개, 양식 1개가 들어오는 구조였으니, 요약 단계를 굳이 줄 세울 이유가 없었습니다.

그래서 Gemini API 호출을 병렬 구조로 변경했습니다. 각 문서의 요약이 동시에 진행되고, 그 결과가 모여 Claude Sonnet의 입력으로 넘어갑니다.

결과적으로 사용자 체감 시간은 모델 하나만 순차로 돌리던 기존 구조와 동일한 수준으로 돌아왔습니다. 토큰 비용은 줄었고, 생성 안정성은 올라갔으며, 시간은 그대로인 상태가 된 겁니다.

회고: 문제를 어떻게 정의하느냐가 해결책을 결정한다

이 해결책의 출발점은 결국 **"우리가 제공하려는 것이 무엇인가"**였습니다.

프로젝트를 하며 가장 많이 받은 질문은 "ChatGPT도 문서 편집하고 요약하는데 너희 서비스는 뭐가 다르냐"는 것이었습니다. 저는 이렇게 답했습니다.

우리가 파는 것은 LLM이 아니라 문서입니다.

같은 결과물을 낼 수 있다면, 그다음 경쟁력은 토큰 비용이 더 적고, 내부 로직을 바꾸기 쉽고, 확장이 용이한 쪽에서 나온다고 생각했습니다. 그게 우리의 해자였습니다.

만약 제가 문제를 **"LLM 모델의 토큰량을 줄여야 한다"**고 정의했다면 어땠을까요. Output 토큰은 구조상 줄이기 어려우니, 모델 크기를 줄이면서 안정적인 출력을 확보하기 위해 양자화 같은 LLM 자체에 대한 조작을 고민했을 겁니다. 훨씬 어려운 챌린지가 됐을 테고, 아마 이런 단순한 방법에는 도달하지 못했을 것 같습니다.

문제 정의를 어디에 두느냐가 해결의 난이도와 방향을 통째로 바꾼다는 걸 체감한 경험이었습니다.

이어 읽으면 좋은 기록