개선사항
PDF.js를 이용한 자체 개발 이유
배경 및 필요성
네이버 파이낸셜에서는 네이버 페이 약관부터 마이데이터 약관, 대출 약관, 보험 약관 등 다양한 종류의 약관을 관리하고 있습니다. 이러한 다양한 약관 페이지들을 효율적으로 관리하고 사용자에게 제공하기 위해 기존의 백엔드(BE)에서 프론트엔드(FE)로 약관 페이지가 이관되었습니다. 이 과정에서 PDF를 렌더링하기 위해 react-pdf를 도입하게 되었습니다.
기술적 도전과 해결 방안
react-pdf를 도입할 당시, IE(Internet Explorer)를 지원하는 것이 필수적인 스펙이었습니다. 그러나 문제는 최신 버전의 react-pdf(v5)가 IE를 지원하지 않았고, 구버전인 react-pdf(v4)만이 IE를 지원한다는 점이었습니다. 이에 따라, react-pdf v4와 v5를 동시에 설치하고, 사용자 에이전트(User Agent)에 따라 분기 처리하는 방식으로 문제를 해결해야 했습니다.
또한, react-pdf는 메모리 누수(memory leak) 문제가 있었기 때문에 서버 사이드 렌더링(SSR)을 지원하는 Next.js를 사용할 때, 동적 임포트(dynamic import) 방식으로 사용해야 했습니다. 이러한 여러 문제들로 인해 react-pdf를 그대로 사용하는 데는 한계가 있었습니다.
기존 react-pdf
와 @naverpay/react-pdf
의 차이점
react-pdf
장점
- 문서와 페이지 분리:
Document
와Page
로 나뉘어 있어 다양한 props를 통해 커스텀 개발이 가능함. - 유연한 커스터마이징: 다양한 props를 통해 개발자가 자유롭게 커스터마이징할 수 있음.
단점
- IE 지원 부족: IE를 지원하지 않음.
- 메모리 누수 문제: SSR 시점에서 메모리 누수 발생. (nextjs 사용 시, dynamic import 를 해야합니다.)
- 한글 파싱 문제: 한글 텍스트를 복사하기 위해
TextLayer
를 활성화하면, 폰트에 따라 한 글자씩span
태그로 렌더링되는 이슈 발생. 이에 따라 렌더링해야 하는 노드 수가 기하급수적으로 증가함.
@naverpay/react-pdf
장점
- 간편한 사용: 별다른 작업 없이 컴포넌트 하나로 PDF를 띄울 수 있음.
- 무한 스크롤 지원: 초기 렌더링 속도가 빠름.
- 렌더링 최적화: PDF 렌더링 시, requestAnimationFrame을 사용하여, 렌더링 최적화.
- 한글 파싱 최적화: 텍스트 복사를 위해 과도한
span
태그가 생성되지 않음. - 기본 스타일 적용: 기본적인 스타일이 적용되어 있음.
- SSR 지원: 별다른 설정 없이 SSR 지원 (SSR 시 미렌더링).
- IE 지원: IE를 지원함.
- 커스텀 핸들러 동작: PDF 내부 텍스트 클릭 시 커스텀 핸들러가 동작함.
단점
- 자유도 감소: 개발자의 자유도가 떨어짐.
- 제한된 커스터마이징 옵션: 커스터마이징할 수 있는 옵션이 적음.
성능 비교 및 결과
다음은 기존 react-pdf와 @naverpay/react-pdf의 성능 비교 결과입니다:
PDF 종류 | 로딩 시간 | JS Heap 메모리 | DOM Node 개수 |
---|---|---|---|
네이버 페이 약관 (7 페이지) | 약 500ms ⬇️ (50% 감소) | 15MB ⬇️ (50% 감소) | 7355개 ⬇️ (92% 감소) |
동부화재 보험약관 pdf (500 페이지) | 약 19,000ms ⬇️ (약 89% 감소) | 447MB ⬇️ (약 68% 감소) | 약 48,000 ⬇️ (약 97% 감소) |
🖼️ 네이버 페이 약관 PDF Chrome Performance 결과 확인하기
네이버 페이 약관 pdf 결과
react pdf
chrome performance check 결과
- 전체 로딩 시간 : 992ms
- 전체 node 수 : 7913개
한글 rendering
- text를 모두 분리해서 rendering 하며 위치가 정확하게 맞지 않음
@naverpay/react-pdf
chrome performance check 결과
-
lazyLoading 옵션이 true 일 경우 (= 화면에 노출되는 페이지만 rendering 하는 경우)
- 전체 로딩 시간 : 414ms
- 전체 node 수 : 288개
-
lazyLoading 옵션이 false 일 경우 (= 전체 페이지를 rendering 하는 경우)
- 전체 로딩 시간 : 500ms
- 전체 node 수 : 558개