NetPlus
Backend스포일러를 줄이는 타임라인 기반 OTT 시청 보조 RAG 챗봇
현재 회차와 시청 시간까지의 자막을 근거로 놓친 맥락과 인물 관계를 설명하는 OTT 시청 보조 챗봇입니다. 질문 분기와 pgvector 정렬, Redis 자막 캐싱으로 응답 시간을 줄이고, 서버에서 근거의 회차·시점을 다시 검증합니다. 답변은 SSE로 진행 상태와 생성 토큰을 순서대로 전달합니다.

프로젝트 개요
- 목표
- 사용자가 현재까지 시청한 내용을 바탕으로 질문에 답하고, 놓친 맥락을 타임라인과 자막 근거로 복구할 수 있도록 구현했습니다.
- 배경
- 이어보기나 잠깐의 주의 분산으로 대사를 놓치면 되감기가 필요합니다. 외부 검색이나 일반 챗봇을 사용하면 아직 보지 않은 내용이 답변에 포함될 위험이 있습니다.
- 주요 기능
- 현재 에피소드와 current_time_ms를 기준으로 작품 질의응답 및 RAG 검색
- 20초·1분·3분 프리셋과 일반·인물 중심·갈등 중심 모드의 요약, 질문별 기준 시점·자막 근거 제공
- 일상 질문과 작품 질문 분기, 질문 스타일 반영 및 채팅 기록 저장·복원
- SSE 스트리밍으로 질문 분석·근거 검색·답변 생성 진행 상태와 생성 토큰을 순서대로 전달
- 에피소드 선택 시 Redis 자막 청크 사전 적재와 TTL 캐싱
- 작품·에피소드·자막·영상 URL·썸네일을 등록하고 삭제하는 관리자 인제스트 API
역할·팀 구성
팀 내 역할 분담 · 2인 팀
- Frontend
- 로그인·회원가입·탐색·요금제 화면과 요약 패널, 공용 UI 컴포넌트 구현
- Backend / AI · 본인
- FastAPI API 서버, RAG 검색·검증·캐싱·스트리밍, 시청 화면·챗봇 패널
담당 역할
Backend Developer · AI Backend / Frontend
질문 수신부터 검색 범위 설정, 데이터 조회, 근거 검증, AI 응답 스트리밍까지 이어지는 백엔드 흐름을 담당했고, 시청 화면과 챗봇 패널, API 연동 계층도 직접 구현해 스트리밍 응답이 화면까지 이어지게 했습니다.
API와 데이터 처리
- FastAPI REST API
인증, 카탈로그, 인제스트, 질의응답, 요약, 채팅 기록 API를 설계하고 프론트엔드와 연동
- 인증
회원가입·로그인 API와 JWT 발급, PBKDF2 기반 비밀번호 해싱 처리
- PostgreSQL · SQLAlchemy
작품·에피소드·자막 등 관계형 데이터 조회와 처리
- 인제스트 API
작품·에피소드·자막 일괄 등록과 영상·썸네일 업로드 서명 발급, 등록 후 자막 청크 생성과 캐시 갱신
- 개발 환경
Docker Compose로 API·PostgreSQL 실행 환경 구성
RAG 검색·검증과 캐싱
- 질문 분기
규칙 기반으로 일반 질문과 작품 질문을 나눠 작품 질문에 RAG 실행
- pgvector 유사도 검색
별도 벡터 DB 없이 PostgreSQL pgvector 확장으로 현재 시점 이전 자막 청크를 코사인 거리순으로 조회하고, 확장이 없는 환경에서는 Redis 캐시·최근 자막 조회로 자동 대체
- 시청 범위 검증
회차와 현재 시점으로 검색 범위를 제한하고 실제 자막 근거를 서버에서 재검증
- Redis 자막 캐싱
에피소드별 자막 청크 TTL 캐싱, 선택 시 사전 적재 및 캐시가 없을 때 DB 조회
응답 전달과 품질 관찰
- SSE 스트리밍
질의응답을 SSE로 전달해 질문 분석·근거 검색·답변 생성 진행 상태와 생성 토큰을 먼저 보내고, 완료 시 근거가 포함된 최종 응답 전달
- LangSmith 추적
OpenAI 클라이언트를 LangSmith로 감싸고 질의응답·요약 파이프라인을 하나의 trace로 묶어 검색 결과와 생성 응답을 단계별로 추적
- 응답 스타일 제어
톤·길이 스타일 옵션을 프롬프트 지시로 반영하고, 일상 대화는 자막·검색을 언급하지 않도록 분리
시청 화면 프론트엔드
- 시청 화면·챗봇 패널
React·TypeScript(Vite)로 시청 화면과 챗봇 패널을 구현하고, SSE 이벤트를 읽어 진행 상태와 생성 토큰을 순서대로 표시
- API 연동 계층
요청·응답 타입과 API 클라이언트를 한곳에 정리해 인증·카탈로그·질의응답·요약·인제스트 호출 관리
- 관리자 화면·사이드바
작품·에피소드·자막을 등록하는 관리자 화면과 에피소드 탐색 사이드바 구현, 반응형 레이아웃 적용
사용 기술 & 이유 & 구현
FastAPI · Python
- 선택 이유
- API와 Python 기반 AI 처리 로직을 같은 환경에서 구성하기 위해 사용했습니다.
- 구현 내용
- 질의응답·요약·채팅 기록 API를 제공하고 질문 분기, 검색, 응답 검증의 책임을 분리했습니다. 질의응답은 StreamingResponse 기반 SSE 엔드포인트로도 제공했습니다.
PostgreSQL · pgvector · SQLAlchemy
- 선택 이유
- 작품·에피소드·자막처럼 관계가 명확하고 시점 조건으로 조회해야 하는 데이터를 관리하면서, 별도 벡터 DB 없이 유사도 정렬까지 같은 DB에서 처리하기 위해 사용했습니다.
- 구현 내용
- 자막 청크의 임베딩 컬럼을 vector 타입으로 바꾸고 ivfflat 코사인 인덱스를 추가해, 에피소드 ID와 시청 시각 조건에 맞는 청크를 거리순으로 가져오도록 했습니다. 인용 자막 ID는 실제 DB 데이터와 대조했습니다.
Redis
- 선택 이유
- 같은 에피소드의 자막 청크가 반복 조회되는 비용을 줄이기 위해 사용했습니다.
- 구현 내용
- 에피소드별 청크 TTL 캐시(기본 30분)와 선택 시 사전 적재를 구현하고, 자막을 새로 등록하면 캐시를 비운 뒤 다시 적재했습니다. 캐시가 없으면 DB를 조회했습니다.
RAG · OpenAI API
- 선택 이유
- 일반적인 모델 지식보다 현재 시점까지 확인 가능한 자막을 근거로 답하도록 구성하기 위해 적용했습니다.
- 구현 내용
- 작품 질문에 검색을 수행하고 실제 자막 근거를 조합해 응답을 생성했습니다. 반환 근거의 회차·시각을 다시 검증하고, 답변 생성은 스트리밍으로 받아 토큰 단위로 전달했습니다.
SSE (Server-Sent Events)
- 선택 이유
- 생성이 끝날 때까지 빈 화면을 보여주는 대신, 진행 상태와 답변을 도착하는 대로 보여주기 위해 사용했습니다.
- 구현 내용
- 워커 스레드가 파이프라인을 실행하면서 status·token·done·error 이벤트를 큐에 넣고, StreamingResponse가 text/event-stream으로 흘려보내도록 구성했습니다. 프록시 버퍼링을 막는 헤더도 함께 설정했습니다.
LangSmith
- 선택 이유
- 응답 품질이 떨어졌을 때 검색과 생성 중 어느 단계가 원인인지 재현해 확인하기 위해 사용했습니다.
- 구현 내용
- OpenAI 클라이언트를 wrap_openai로 감싸고 질의응답·요약 파이프라인에 traceable을 적용해 입력, 검색 결과, 생성 응답을 한 trace로 묶었습니다.
Docker Compose
- 선택 이유
- API와 관계형 DB의 로컬 실행 조건을 맞추기 위해 사용했습니다.
- 구현 내용
- API·PostgreSQL 서비스를 정의하고 DB 준비 후 마이그레이션과 API 서버가 실행되도록 구성했습니다.
문제 해결 사례
사례 1질문 분기·pgvector 정렬·캐싱·스트리밍으로 응답 지연 개선
1. 오류 분석
- 문제 현상
- 챗봇 응답에 약 4~5초가 걸려 3초대 응답이라는 성능 요구를 넘겼습니다. 같은 에피소드의 자막을 매 요청마다 다시 조회했고, 검색이 필요 없는 일상 질문에도 RAG가 실행될 수 있었습니다. 답변이 끝날 때까지 화면에 아무것도 보이지 않아 대화 흐름도 끊겼습니다.
- 원인
- 자막 청크를 모두 애플리케이션으로 가져와 점수를 매기는 구조라 조회와 정렬 비용이 매 요청에 들어갔고, 질문 유형과 상관없이 같은 경로를 탔습니다. 응답도 생성이 끝난 뒤 한 번에 내려줬습니다.
- 추가 확인 사항
- 캐시에서 가져온 자막도 현재 시청 시점 이후의 정보를 포함하지 않도록 검증해야 했습니다.
- 캐시가 없거나 비활성화된 경우에도 DB 조회로 요청을 처리해야 했습니다.
- pgvector 확장이 없는 환경에서도 같은 요청이 동작해야 했습니다.
2. 개선 과정
- 1.API 응답 흐름 분석
- 2.일반·작품 질문 분기
- 3.pgvector 거리순 정렬
- 4.자막 청크 Redis 캐싱
- 5.에피소드 선택 시 사전 적재
- 6.SSE 스트리밍 전환
- 7.시청 범위 재검증
- 8.응답 시간 비교
작품 질문에만 RAG를 수행하도록 분기하고, 자막 청크 유사도 정렬을 PostgreSQL pgvector 쿼리로 옮겨 현재 시점 이전 청크만 거리순으로 가져오게 했습니다. 에피소드별 자막 청크에는 TTL 캐시와 선택 시 사전 적재를 적용했고, 확장이 없는 환경에서는 캐시와 최근 자막 조회로 자동 대체되게 했습니다. 마지막으로 질의응답을 SSE로 바꿔 진행 상태와 생성 토큰을 먼저 보내되, 근거의 회차·시점 검증은 그대로 유지했습니다.
3. Before & After 비교
| 항목 | Before | After |
|---|---|---|
| 응답 시간 | 약 4~5초 | 약 2~3초 |
| 응답 시간 단축률 | 기준 | 약 40~50% |
| RAG 실행 | 불필요한 실행 가능 | 작품 질문에 실행 |
| 유사도 정렬 | 전체 청크를 가져와 애플리케이션에서 점수 계산 | pgvector 쿼리에서 거리순 조회 |
| 자막 조회 | 동일 데이터 반복 조회 | Redis 캐싱·사전 적재 |
| 응답 전달 | 생성 완료 후 일괄 전달 | SSE로 진행 상태·토큰 순차 전달 |
| 답변 근거 | 검색 결과 활용 | 실제 자막의 회차·시각 재검증 |
응답 시간과 단축률은 제공된 프로젝트 기록의 근사치입니다. p95 지연 시간, 캐시 적중률, DB 조회 시간, 첫 토큰 도착 시간은 별도로 측정하지 않았습니다.
해결 결과
응답 시간을 약 40~50% 줄여 3초대 요구를 맞추고, 첫 토큰이 먼저 도착해 체감 대기도 줄였습니다. 현재 시점까지의 자막을 근거로 설명하는 흐름은 그대로 유지했습니다.
프로젝트 성과 및 결과
- 응답 시간 약 4~5초 → 2~3초, 약 40~50% 단축
- 작품 질문에 한정한 RAG 실행, pgvector 유사도 정렬과 Redis 자막 청크 캐싱 구현
- SSE 스트리밍으로 진행 상태와 답변을 순차 전달해 첫 응답까지의 체감 대기 단축
- 회차·시청 시각 검증 및 실제 자막 기반 인용으로 스포일러 위험 감소
- LangSmith로 질의응답·요약 파이프라인의 검색·생성 단계 추적 구성
- 인증·요약·채팅 기록·인제스트를 포함한 시청 보조 백엔드 API 구성
프로젝트 회고
기술적 한계와 개선 방안
- 한계·아쉬운 점
- 전체 응답 시간만으로는 캐시, DB, 모델 호출이 각각 차지하는 비용을 구분하기 어렵습니다.
- 개선 방향
- API p95, Redis Cache Hit Ratio, DB Query Time을 수집하고 Prometheus·Grafana로 구간별 지연을 비교할 계획입니다.
- 한계·아쉬운 점
- 자막 청크 임베딩이 문자 기반의 경량 벡터라, 표현이 다른 비슷한 의미의 질문은 어휘 일치 점수에 의존합니다.
- 개선 방향
- OpenAI text-embedding-3-small 같은 다국어 임베딩 모델로 자막 청크를 다시 임베딩하고 pgvector 인덱스를 HNSW로 바꾼 뒤, 같은 질문 세트로 검색 정확도와 지연을 비교할 계획입니다.
- 한계·아쉬운 점
- 규칙 기반 질문 분기는 복합적인 문장을 오분류할 수 있습니다.
- 개선 방향
- 작품·일상 질문 평가셋을 만들고 규칙 기반 방식과 경량 분류 모델의 정확도·지연·비용을 비교할 계획입니다.
- 한계·아쉬운 점
- 현재 시점 이후의 자막을 제외하더라도 모델의 사전 지식에서 스포일러가 나오는 경우까지 완전히 보장할 수는 없습니다.
- 개선 방향
- 회차 경계·근거 부족·미래 내용 유도 질문을 별도 평가 시나리오로 관리하고 답변과 인용을 함께 검증할 계획입니다.