본문으로 건너뛰기

개선사항

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

장점

  • 문서와 페이지 분리: DocumentPage로 나뉘어 있어 다양한 props를 통해 커스텀 개발이 가능함.
  • 유연한 커스터마이징: 다양한 props를 통해 개발자가 자유롭게 커스터마이징할 수 있음.

단점

  • IE 지원 부족: IE를 지원하지 않음.
  • 메모리 누수 문제: SSR 시점에서 메모리 누수 발생. (nextjs 사용 시, dynamic import 를 해야합니다.)
  • 한글 파싱 문제: 한글 텍스트를 복사하기 위해 TextLayer를 활성화하면, 폰트에 따라 한 글자씩 span 태그로 렌더링되는 이슈 발생. 이에 따라 렌더링해야 하는 노드 수가 기하급수적으로 증가함.

장점

  • 간편한 사용: 별다른 작업 없이 컴포넌트 하나로 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 결과

image
  • 전체 로딩 시간 : 992ms
  • 전체 node 수 : 7913개

한글 rendering

image
  • text를 모두 분리해서 rendering 하며 위치가 정확하게 맞지 않음

chrome performance check 결과

  1. lazyLoading 옵션이 true 일 경우 (= 화면에 노출되는 페이지만 rendering 하는 경우)

    image
    • 전체 로딩 시간 : 414ms
    • 전체 node 수 : 288개
  2. lazyLoading 옵션이 false 일 경우 (= 전체 페이지를 rendering 하는 경우)

    image
    • 전체 로딩 시간 : 500ms
    • 전체 node 수 : 558개

한글 rendering

image
  • 복사를 위한 한글 text layer 최적화
🖼️ 동부화재 보험약관 PDF Chrome Performance 결과 확인하기

동부화재 보험약관 pdf 결과

react pdf

chrome performance check 결과

스크린샷 2024-06-20 오후 4 36 06
  • 전체 로딩 시간 : 21,344ms
  • 전체 node 수 : 49,420개
image
  • 전체 로딩 시간 : 2,139ms
  • 전체 node 수 : 1,208개

Chrome 성능 확인 결과, 기존 react-pdf보다 @naverpay/react-pdf가 JS Heap 메모리 사용량과 HTML 노드 개수를 크게 줄인 것을 확인할 수 있었습니다. @naverpay/react-pdf는 기본적으로 화면에 노출되는 페이지만 렌더링하는 레이지 로딩(lazy loading)이 적용되어 있고, pdf 렌더링 시, requestAnimationFrame을 이용하여 렌더링을 최적화했습니다. 이를 통해 렌더링 속도와 메모리 사용률이 눈에 띄게 향상되었습니다.

특히 한글 렌더링에 최적화되어 있어 렌더링되는 DOM의 개수가 현저히 줄었으며, 이에 따라 로딩 및 렌더링 속도와 JS Heap 메모리 사용률도 크게 감소했습니다.

구체적인 성능 개선 사례

네이버 페이 약관(10페이지)의 경우, @naverpay/react-pdf를 사용한 후 로딩 시간은 약 500ms로 50% 감소하였고, JS Heap 메모리는 15MB로 50% 감소하였습니다. 또한 DOM 노드의 개수는 7355개로 92% 감소하였습니다.

동부 화재 보험 약관(500페이지)의 경우, 로딩 시간은 약 19,000ms로 약 89% 감소하였고, JS Heap 메모리는 447MB로 약 68% 감소하였습니다. DOM 노드의 개수는 약 48,000개로 약 97% 감소하였습니다.

이러한 성능 개선은 사용자 경험을 크게 향상시켰으며, 더 나아가 서버 자원의 효율적인 사용과 응답 속도의 향상을 가져왔습니다.

사용자 에이전트 기반 분기 처리

react-pdf v4와 v5를 동시에 설치하고, 사용자 에이전트에 따른 분기 처리를 통해 IE 사용자를 지원하였습니다. 이로 인해 기존 시스템에서 발생하던 IE 지원 문제를 해결할 수 있었습니다.

메모리 누수 문제 해결

메모리 누수 문제를 해결하기 위해 Next.js와 동적 임포트(dynamic import)를 사용하여 서버 사이드 렌더링(SSR)을 구현하였습니다. 이를 통해 메모리 누수 문제를 최소화하고, 안정적인 PDF 렌더링을 제공할 수 있었습니다.

결론

네이버 파이낸셜에서는 다양한 약관 페이지를 효율적으로 관리하고 사용자에게 제공하기 위해 PDF.js를 기반으로 자체 개발한 @naverpay/react-pdf를 도입하였습니다. 이 컴포넌트는 기존 react-pdf의 단점을 보완하고, 한글 파싱 및 IE 지원 등 네이버 파이낸셜의 요구사항에 최적화된 솔루션을 제공합니다. 이를 통해 성능 향상과 더불어 개발 생산성도 크게 개선되었습니다.

@naverpay/react-pdf는 초기 로딩 시간, 메모리 사용량, DOM 노드 수 등의 성능 지표에서 기존 react-pdf에 비해 뛰어난 성능을 보여주었으며, 특히 한글 텍스트 처리와 IE 지원에 있어서 큰 강점을 가지고 있습니다. 이러한 성능 개선은 사용자 경험을 향상시키고, 더 나아가 서버 자원의 효율적인 사용과 응답 속도의 향상을 가져왔습니다.

이를 통해 네이버 파이낸셜은 사용자에게 더 나은 서비스를 제공하고, 개발팀은 더 효율적인 개발 환경을 구축할 수 있게 되었습니다. @naverpay/react-pdf의 도입은 네이버 파이낸셜의 기술적 도전과 해결 방안의 좋은 사례가 될 것입니다.