개발자들은 PDF 뷰어를 지속적으로 과소평가합니다. 단순히 '파일을 박스에 넣는' 문제처럼 보이기 때문입니다. 바이트를 가져와서 컴포넌트에 URL을 지정하기만 하면 끝이라고 생각하죠. Vue.js는 대부분의 UI 작업을 이토록 간단하게 느껴지게 만들지만, 프로덕션 수준의 PDF 뷰어는 조용히 개발 스프린트 전체를 잡아먹는 기능 중 하나입니다. 저는 2주 동안 같은 뷰어를 네 가지 다른 방식으로 구축해 보았습니다. 매 시도마다 같은 결론에 도달했습니다. 월요일에 선택한 라이브러리가 두 달 뒤에 어떤 버그를 디버깅하게 될지를 결정한다는 사실입니다.
오래된 튜토리얼의 함정
대부분의 가이드는 여전히 Vue 2가 기본이었던 시절에 마지막으로 업데이트된 라이브러리를 추천합니다. 개발자는 README를 훑어보고 설치 명령어를 실행한 뒤, 어려운 부분은 끝났다고 가정합니다. 하지만 실제로는 어려운 부분은 이제 막 시작되었을 뿐입니다. 검색 기능이 필요할 것이고, 브라우저 탭이 멈추지 않게 하면서 400페이지짜리 정책 문서를 처리해야 할 것입니다. 모바일 사용자는 왜 핀치 투 줌(pinch-to-zoom)이 제대로 작동하지 않는지 물어올 것입니다. README는 이런 점들을 거의 경고하지 않습니다. 데모가 고작 5페이지짜리 논문의 첫 페이지만 보여주기 때문입니다.
네 가지 접근 방식
Vue를 위한 단 하나의 최고의 PDF 라이브러리는 없습니다. 오직 사용자가 실제로 무엇을 하려는지에 가장 적합한 라이브러리만 있을 뿐입니다.
PDF.js: DIY 방식
Mozilla의 PDF.js는 거의 모든 웹 기반 뷰어의 밑바탕이 되는 엔진입니다. 이를 Vue 3 앱에 가져다 쓰는 것은 단순히 컴포넌트를 설치하는 것이 아니라, 하나의 프로젝트를 채택하는 것을 의미합니다. getDocument로 문서를 가져오고, 각 페이지를 <canvas> 요소에 렌더링한 뒤, 해당 캔버스들을 템플릿에 연결해야 합니다. 첫날에는 생산적이라고 느끼겠지만, 셋째 날이 되면 Vite 번들링 및 CORS 헤더와 잘 맞물리도록 워커(worker) 스크립트를 설정하고 있을 것입니다.
브라우저 기본 스크롤은 10페이지 정도에서는 잘 작동합니다. 하지만 1,000페이지가 되면 버벅거리기 때문에 가상 스크롤(virtual scrolling)을 직접 구현해야 합니다. 그러다 텍스트를 선택할 수 없다는 사실을 깨닫고, 각 캔버스 위에 투명한 텍스트 div를 겹쳐 올리게 됩니다. 인쇄 결과가 흐릿해 보이면 DPI 설정과 미디어 쿼리를 붙잡고 씨름하게 됩니다. 모바일의 핀치 투 줌은 브라우저의 기본 제스처 핸들링과 충돌합니다. 문서 간 검색을 구현하려면 모든 페이지에서 비동기적으로 텍스트를 추출하고 인덱싱한 다음, 메인 스레드를 차단하지 않고 결과를 큐에 쌓는 UI를 구축해야 합니다. Cursor와 같은 AI 코딩 어시스턴트가 보일러플레이트 코드를 생성해 준다 해도, 아키텍처에 대한 책임은 여전히 당신에게 있습니다. 어려운 부분은 사라지지 않고 당신의 코드베이스로 옮겨올 뿐입니다. 이 방식은 요구 사항이 정말로 특수하거나, 몇 주 정도의 여유가 있고 기성 제품의 동작 방식을 피해야 할 강력한 이유가 있을 때만 의미가 있습니다.
vue-pdf-embed: 경량 방식
때로는 단순히 파일을 보여주기만 하면 될 때가 있습니다. vue-pdf-embed는 소스를 받아 페이지를 수직으로 쌓아서 렌더링하는 Vue 3 컴포넌트입니다. 설치와 통합에 몇 분이면 충분합니다. 생성된 인보이스나 컴플라이언스 보고서를 보여주는 내부 관리자 패널의 경우, 이 정도로도 충분한 경우가 많습니다. 컴포넌트가 캔버스 렌더링을 처리하므로 사용자는 스크롤만 하면 됩니다.
대가는 그 외의 모든 기능입니다. 툴바도, 문서 검색도, 썸네일 사이드바도 없으며, 스크롤 컨테이너를 훑어보는 것 외에는 페이지 이동 기능도 없습니다. 이해관계자가 "인보이스 번호를 검색할 수 있나요?"라고 묻는 순간, 2시간이면 끝날 통합 작업은 커스텀 빌드로 불어나게 됩니다. PDF가 짧고, 대상이 내부 사용자이며, 상호작용 모델이 순수하게 읽기 전용 스크롤인 경우에 이 방식을 선택하세요.
@tato30/vue-pdf: 제어 방식
이 라이브러리는 거대한 단일 컴포넌트 형태가 아닌 컴포저블(composable) 형태로 제공됩니다. setup 블록 내부에서 호출할 수 있는 usePDF를 노출합니다. 전체 스크롤 문서를 렌더링하는 대신, 반응형 ref를 통해 한 번에 한 페이지씩 관리합니다. 추가 작업처럼 들릴 수 있지만, 인터페이스에 정밀함이 요구될 때는 매우 자유롭습니다.
손해 사정사가 문서 한 페이지를 확인하고 '다음'을 클릭하면 시스템이 각 조회 이벤트를 기록하는 보험 청구 검토 도구를 상상해 보세요. 이런 경우 연속 스크롤 뷰어는 적절한 비유가 아닙니다. 현재 페이지 인덱스에 직접 연결된 페이지 단위의 댓글 기능이나 승인 버튼이 있는 제어 가능한 페이지네이터가 필요할 것입니다. usePDF가 페이지 수와 현재 페이지를 반응형 데이터로 제공하기 때문에, 이를 커스텀 네비게이션 바나 진행 표시기에 연결하는 것이 매우 자연스럽습니다. 여전히 캔버스 주변의 UI(chrome)를 직접 구축해야 하지만, 최하위 수준의 렌더링 보일러플레이트에서는 벗어날 수 있습니다. 전체 원고를 훑어보는 대신 사용자가 페이지를 하나씩 넘겨가며 확인하는 앱에 적합합니다.
Vue PDF Viewer: 풀 서비스 방식
뷰어 기능을 다시 만드는 것이 실제 제품 개발에 방해가 되는 시점이 옵니다. Vue PDF Viewer는 전체 툴바, 텍스트 검색, 주석, 모바일 반응형 디자인, 그리고 다양한 엣지 케이스에서 이미 검증된 가상 스크롤 기능을 갖춘 상용 컴포넌트입니다. 이제 여러분의 역할은 무언가를 발명하는 것이 아니라 설정하는 것이 됩니다. 디자인 시스템에 맞춰 테마를 조정하고, 필요한 기능을 활성화한 뒤, 앱을 차별화할 수 있는 실제 작업으로 넘어갈 수 있습니다.
초기 라이선스 비용이 발생하는 것은 사실이지만, 검색 인덱싱과 주석 레이어를 다시 만드는 데 드는 2주간의 엔지니어링 비용 또한 만만치 않습니다. 실제 마감 기한 내에 프로덕션 앱을 출시해야 하고, 사용자가 데스크톱 PDF 소프트웨어에 버금가는 경험을 기대한다면 이것이 바로 적합한 도구입니다.
설치의 진짜 비용
가장 큰 실수는 npm install 명령어를 전체 비용으로 생각하는 것입니다. 진짜 비용은 설치가 끝난 후 무엇을 구축하느냐에 달려 있습니다. 가벼운 라이브러리는 첫날에는 저렴하지만, 검색 바가 필요하다는 것을 깨닫게 되는 20일째에는 비용이 많이 듭니다. 직접 만드는(DIY) 방식은 첫날에는 무료지만, 60일째에도 여전히 모바일 터치 타겟과 인쇄 스타일시트를 수정하고 있다면 비용이 많이 듭니다. 상용 솔루션은 초기에 비용이 들지만, 실제 비즈니스 로직에 쏟을 수 있는 수 주간의 엔지니어링 시간을 아껴줍니다.
README가 짧고 친절하다고 해서 라이브러리를 선택하지 마세요. 프로젝트 요구 사항에 따라 선택해야 합니다. 썸네일 사이드바, 텍스트 검색, 클라이언트 측 주석 기능이 필요하다면 풀 서비스 솔루션을 고려해야 합니다. 내부 대시보드 내에서 영수증을 빠르게 미리 보기 하는 용도라면 가벼운 임베드 방식을 선택하는 것이 좋습니다.
핵심 요약
어떤 Vue PDF 라이브러리를 사용하기로 결정하기 전에, 사용자가 정확히 무엇을 해야 하는지 적어보세요. 단순히 짧은 문서를 스크롤하기만 하면 된다면, vue-pdf-embed가 정답입니다. 제어된 워크플로우에 따라 페이지를 넘겨야 한다면, @tato30/vue-pdf를 사용하세요. 비즈니스 핵심 앱 내에서 주석을 달고, 검색하고, 인쇄해야 한다면 상용 뷰어를 구매하세요. 만약 요구 사항이 정말 독특하지만 일정이 유연하다면, 몇 주간의 시간을 확보하여 PDF.js를 기반으로 직접 구축하세요. PDF 뷰어는 단순히 상자 안에 담긴 파일이 아닙니다. 그것은 완전한 문서 인터페이스이며, 어떤 빌딩 블록을 선택하느냐에 따라 이번 달에 출시할지 아니면 다음 분기에 출시할지가 결정될 것입니다.
