# next.js에서 pdf viewer 사용하기

웹 또는 웹앱에서 로컬 또는 서버에서 가져온 Pdf를 보여주는 예제입니다. 이 글에서는 이런 것들을 다룹니다.

  1. pdf viewer 예제
  2. 화면의 width에 pdf가 꽉차게 보이기
  3. 스크롤 내림에 따라 다음 pdf 보이기
  4. pdf 복사 방지

# 프로젝트 세팅

  • 먼저 예시에 필요한 디펜던시를 설치합니다.
yarn add react-pdf
// typescript 쓴다면
yarn add -D @types/react-pdf

# pdf viewer 예제

import { useState } from "react";
import { Document, Page, pdfjs } from "react-pdf";

// workerSrc 정의 하지 않으면 pdf 보여지지 않습니다.
pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;

const Index = () => {
  const [numPages, setNumPages] = useState(0);
  const [pageNumber, setPageNumber] = useState(1);

  function onDocumentLoadSuccess({ numPages }: { numPages: number }) {
    setNumPages(numPages);
    setPageNumber(1);
  }

  return (
    <>
      <Document
        file="/static/doc/test.pdf" // 여기는 가지고 계신 pdf 주소
        onLoadSuccess={onDocumentLoadSuccess}
      >
        {/* height, width는 number 타입으로 vh, %는 먹지 않습니다. */}
        <Page height={600} pageNumber={pageNumber} />
      </Document>
      Page {pageNumber} of {numPages}
      {pageNumber > 1 && (
        <button onClick={() => setPageNumber(prev => prev + -1)}>
          이전페이지
        </button>
      )}
      {pageNumber < numPages && <button onClick={() => setPageNumber(prev => prev + +1)}>다음페이지</button>}
    </>
  );
};

export default Index;

# 스크롤 내림에 따라 다음 pdf 보이기

  • 위에서 말씀드린대로 width, height는 number값으로 %가 들어가지 않아 width를 화면에 꽉차게 할라면 현재 보는 화면의 width값을 px로 알아야 합니다. window innerWidth를 가져오는 간단한 훅을 만듭니다.
// ./useWindowSize.tsx
import { useState, useEffect } from "react";

const useWindowSize = () => {
  const [windowSize, setWindowSize] = useState({
    width: 0,
    height: 0
  });

  useEffect(() => {
    if (typeof window !== "undefined") {
      const handleResize = () => {
        setWindowSize({
          width: window.innerWidth,
          height: window.innerHeight
        });
      };

      window.addEventListener("resize", handleResize);
      handleResize();

      return () => window.removeEventListener("resize", handleResize);
    }
  }, []);
  return windowSize;
};

export default useWindowSize;

만든 훅을 가져와 width를 넣어줍니다.

import { useState } from "react";
import { Document, Page, pdfjs } from "react-pdf";
import useWindowSize from "./useWindowSize";

pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;

const Index = () => {
  const windowSize = useWindowSize();
  const [numPages, setNumPages] = useState(0);
  const [pageNumber, setPageNumber] = useState(1);

  function onDocumentLoadSuccess({ numPages }: { numPages: number }) {
    setNumPages(numPages);
    setPageNumber(1);
  }

  return (
    <>
      <Document
        file="/static/doc/test.pdf"
        onLoadSuccess={onDocumentLoadSuccess}
      >
        {/* width, height값을 훅에서 가져온 값으로 넣어줍니다 */}
        <Page
          width={windowSize.width}
          height={windowSize.height}
          pageNumber={pageNumber}
        />
      </Document>
      Page {pageNumber} of {numPages}
      {pageNumber > 1 && (
        <button onClick={() => setPageNumber(prev => prev + -1)}>
          이전페이지
        </button>
      )}
      {pageNumber < numPages && <button onClick={() => setPageNumber(prev => prev + +1)}>다음페이지</button>}
    </>
  );
};

export default Index;

# 스크롤 내림에 따라 다음 pdf 보이기

  • 지금은 하단의 다음 버튼을 클릭해야 다음장이 보이고 있습니다.
  • 이번에는 스크롤 내림으로 다음 pdf가 보이도록 합니다.
  • 간단하게 전체 pdf를 가져와서 밑으로 보여줍니다.
<Document file="/static/doc/test.pdf" onLoadSuccess={onDocumentLoadSuccess}>
  {Array.from(new Array(numPages), (_, index) => (
    <Page
      width={windowSize.width}
      height={windowSize.height}
      key={index}
      pageNumber={index + 1}
      renderAnnotationLayer={false}
    />
  ))}
</Document>

# pdf 복사 방지

  • 지금과 같은 경우 pdf 위에서 마우스 우클릭을 하면 '다른 이름으로 저장'을 통해 pdf를 image로 저장할 수 있습니다. 외부 유출이 되면 안되는 pdf의 경우 우클릭을 막아야 할 상황이 있을 수 있습니다. (디바이스 자체 캡쳐기능을 사용하면 막을 방법이 없습니다..)
  • html에서는 마우스 오른쪽을 눌렀을 때 나오는 메뉴를 contextmenu라고 합니다. 그래서 우리는 html의 onContextMenu를 이용하여 우클릭 함수가 실행되지 않도록 합니다.
  • Event.preventDefault() (opens new window) 메소드를 이용합니다. 이 메소드는 해당 이벤트 (이 예시에서는 onContextMenu)의 기본 동작이 실행되지 않도록 하는 기능입니다.
<div onContextMenu={e => e.preventDefault()}>
  <Document file="/static/doc/test.pdf" onLoadSuccess={onDocumentLoadSuccess}>
    {Array.from(new Array(numPages), (_, index) => (
      <Page
        width={windowSize.width}
        height={windowSize.height}
        key={index}
        pageNumber={index + 1}
        renderAnnotationLayer={false}
      />
    ))}
  </Document>
</div>

위처럼 간단하게 pdf를 보여주는 기능을 만들 수 있습니다. pdf를 보여주는 것은 react-pdf (opens new window)를 사용하면 되고 보여지는 화면을 pdf로 변환 하고 싶을 때는 React-pdf (opens new window)를 사용하면 간편하게 개발 할 수 있습니다!!

#react #next #TS
노경환
이 글이 도움이 되셨다면! 깃헙 스타 부탁드립니다 😊😄
최근변경일: 3/25/2024, 12:16:11 PM