새로고침해도 추출 결과 유지되도록 개선
목차
이번 커밋은 pdf2api 서비스의 핵심 경험을 세 가지 방향으로 개선한 결과다. 추출된 데이터를 새로고침해도 유지하도록 했고, 분석 중인 상태를 모달로 시각화했으며, 유료 사용자들에게 고급 모델을 제공하기 시작했다. 따로 보면 각각 작은 개선 같지만, 함께 묶으면 사용자 신뢰와 서비스 가치를 크게 높이는 변화다.
배경: 세 가지가 왜 함께 필요했는가
상태 유지의 신뢰 문제
가장 먼저 생각해야 할 건 "새로고침 후에도 추출 결과 유지"다. 사용자가 큰 PDF 파일을 업로드하고 몇 분을 기다려 추출을 완료했는데, 실수로 F5를 누르거나 탭을 닫았다가 다시 들어올 때 모든 결과가 날아간다면 어떨까? 이건 단순한 불편함을 넘어 신뢰 문제가 된다. "이 서비스에서 작업해도 안전한가?" 하는 의문이 생긴다. 특히 처리 시간이 길수록 사용자는 "혹시 또 잃으면 어떻게 하지?" 라는 불안감 속에 작업한다. 이런 심리적 장벽을 없애려면 추출 결과를 메모리가 아닌 어딘가 영구적인 곳에 보관해야 한다.
진행 상태 시각화의 UX
"analyzing modal"은 길어질 수 있는 작업 중에 "지금 뭐 하고 있는 중이야?" 를 명확히 보여주려는 의도다. PDF 추출은 텍스트를 추출한 후 Anthropic API를 호출해 구조화된 데이터로 변환하는데, 이 과정이 얼마나 걸릴지는 파일 크기, 복잡도, 모델 성능에 따라 달라진다. 응답을 기다리는 동안 사용자에게 어떤 피드백도 없으면? "혹시 응답이 없는 건가?" "실패한 건가?" 하고 브라우저를 닫거나 여러 번 클릭할 것이다. 모달은 이런 상황에서 명확한 상태 신호를 주고, 불필요한 재시도나 중복 요청을 방지한다.
유료 모델 차등화의 수익화
"Opus for paid"는 기능보다 비즈니스 결정이다. 유료 구독자에게는 더 강력한 모델을 제공해서 업그레이드의 실질적 가치를 전달하려는 의도다. Anthropic의 모델 라인업 중 Opus는 복잡한 추론과 정확도에서 앞선다. 무료 사용자는 더 빠르고 저렴한 모델(예: Sonnet)로 충분할 수 있지만, 프리미엄 사용자는 추출 정확도와 품질의 향상을 체감할 수 있게 차등화하면, 업그레이드 결정을 쉽게 만들 수 있다.
작업 내용: 세 레이어의 변경
클라이언트 상태 관리 (home-client.tsx)
추출 결과를 React 상태에만 두면 새로고침에 날아간다. 따라서 로컬스토리지나 세션 스토리지와 동기화하는 로직이 필요했을 것이다. 컴포넌트가 마운트될 때 저장된 데이터를 불러오고, 새 추출이 완료되면 즉시 저장하는 식이다. 동시에 analyzing 상태를 명시적으로 관리해서 모달을 show/hide 할 수 있도록 했다. API 호출 시작 → analyzing=true → 모달 표시, 완료/실패 → analyzing=false → 모달 닫기 흐름이 자연스럽게 된다.
API 레이어의 모델 선택 (anthropic.ts)
사용자의 구독 여부나 권한을 확인해서 호출할 모델을 결정하는 로직이 추가됐다:
// 간단한 예
const model = userIsPaid ? 'claude-3-5-opus' : 'claude-3-5-sonnet';
물론 실제로는 더 복잡할 수 있다 (A/B 테스트, 점진적 롤아웃, 오류 폴백 등). 중요한 건 이 로직을 한 곳에 집중시켜서, 나중에 모델을 바꾸거나 조건을 수정할 때 코드 탐색의 부담을 줄인다는 것이다.
다국어 메시지 (i18n.ts)
모달과 상태 메시지들이 여러 언어로 지원되도록 정리했다. "분석 중입니다", "완료되었습니다", "오류가 발생했습니다" 같은 텍스트가 번역되어 있어야 글로벌 사용자들도 무슨 상태인지 이해할 수 있다. 다국어 지원은 작지만 깊은 디테일이다. 처음부터 i18n을 고려하지 않으면 나중에 누더기가 되기 쉽다.
회고: 한 커밋, 세 기능의 함의
이 세 가지가 한 커밋에 묶인 이유는 논리적 연관성일 것이다. 상태를 유지하려면 상태 관리 구조를 먼저 정리해야 하고, 그 과정에서 analyzing 상태도 명시적으로 처리하게 되며, 모델 선택도 같은 API 호출 레이어에서 처리하는 게 자연스럽다. 결국 하나의 큰 리팩토링 안에서 세 개의 기능이 함께 완성된 셈이다.
하지만 여러 기능을 함께 배포할 때는 주의가 필요하다. 로컬스토리지 동작, 모달 타이밍, 모델에 따른 응답 시간 차이 등이 예상과 다르게 상호작용할 수 있기 때문이다. 특히 persistence와 관련해서는 세션 스토리지 vs 로컬스토리지의 트레이드오프를 고려해야 한다. 세션 스토리지는 더 간단하고 보안 위험이 낮지만 (탭 닫으면 사라짐), 사용자가 하루 종일 작업하는 경우를 지원하지 못한다. 로컬스토리지는 장기 유지가 가능하지만 용량 제한과 평문 저장의 보안 문제를 고려해야 한다. 어느 것을 선택했든, 그 판단 이유를 코드나 설계 문서에 남겨두면 나중에 팀원들이 이해하기 쉽다.
마지막으로, 유료/무료 모델 차등화는 처음부터 로깅(어떤 사용자가 언제 어떤 모델을 썼는지)을 남겨둬야 한다. 나중에 Opus 업그레이드가 실제로 사용자 만족도나 전환율을 높였는지, 비용 대비 가치가 있는지 데이터로 검증할 때 이 기록이 필수다.
🛒 이 글과 어울리는 추천 상품
*위 링크는 쿠팡파트너스 활동의 일환이며, 일정액의 수수료를 제공받을 수 있습니다.
댓글 0
첫 댓글 달아줘.