ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [NestJS] EC2에서 Puppetter를 활용한 PDF 파일 다운로드
    공부/Nest 2025. 10. 25. 15:09

    PDF 파일을 다운 받을 수 있도록 구현해야 하는 업무가 있었습니다.

    구현하면서 겪었던 내용을 공유하려고 글을 작성하였습니다.

     

    혹시라도 부정확한 정보를 전달드릴 수 있습니다. 다만 틀린 부분이 있으면 댓글을 남겨주세요. 


    Puppeteer란?

    Puppeteer는 Google Chrome 팀에서 개발한 Node.js 라이브러리로, 웹 페이지를 렌더링 하고 PDF로 변환하는 작업에 매우 적합합니다.

     

    1. 로컬에서 Puppetter 라이브러리 설치

    npm i puppeteer

     

    2. 코드 구현

    // service.ts
    
    
    // PDF에 출력되는 page를 HTML로 구현하였습니다.
    const htmlContent = template.generateHtmlTemplate();
    
    const browser = await puppeteer.launch({
      headless: true, 
      args: [
        '--no-sandbox', 
        '--disable-setuid-sandbox',
        '--single-process', // 단일 프로세스 모드로 실행하여 리소스 사용 최적화
      ],
      timeout: 30000, // 브라우저 실행 타임아웃 설정
    });
    
    try {
      const date = new Date();
      const defaultFileName = `PDF 생성 테스트_${date}.pdf`;
      const encodedFileName = encodeURIComponent(defaultFileName); // 한글 인코딩으로 PDF 파일명 지정
    
      const page = await browser.newPage();
      await page.setContent(htmlContent, {
        waitUntil: 'networkidle0', // 이미지나 외부에 있는 리소스가 모두 가져온 후 PDF를 생성합니다.
      });
      
      const pdfBuffer = await page.pdf({
        format: 'A4',
        printBackground: true,  // CSS 배경색과 이미지를 PDF에 포함
        preferCSSPageSize: true,	// CSS의 @page 규칙을 우선 적용
        displayHeaderFooter: false,  // 기본 헤더, 푸터 비활성화
        margin: {
          top: '20mm',
          right: '20mm',
          bottom: '20mm',
          left: '20mm',
        },
        scale: 1,
      });
    
      return {
        fileName: encodedFileName,
        pdfBuffer: Buffer.from(pdfBuffer),
        contentType: 'application/pdf',
      };
    } finally {
      await browser.close();   // 메모리 누수를 방지하기 위한 브라우저 인스턴스 종료
    }

     

    // controller.ts
    
    @Get('download')
    async downloadPdf(@Res() res: Response) {
      const { fileName, pdfBuffer, contentType } = await this.pdfService.generatePdf();
      
      res.set({
        'Content-Type': contentType,
        'Content-Disposition': `attachment; filename*=UTF-8''${fileName}`,
        'Content-Length': pdfBuffer.length,
      });
      
      res.end(pdfBuffer);
    }

     

    실제 구현한 코드의 일부분을 가져와서 수정하였습니다. 

     

    postman에서 보이는 예시입니다. 우측에 있는 ... 를 누르시면 save response to file을 누르시면 파일을 다운 받으실 수 있습니다.

     

     

    3. EC2에서 사용하기

    local에는 chrome이 설치된 환경이라서 문제가 없지만 EC2 인스턴스는 동일한 환경이 아니기 때문에 테스트 하시면 서버에러가 발생하게 됩니다.

    EC2에서는 추가적으로 설치해줘야 하는 부분들이 존재합니다.

    # Chromium 설치(ubuntu)
    sudo apt-get update
    sudo apt-get install -y chromium-browser
    
    # 필수 라이브러리 설치 필요
    sudo apt-get update
    sudo apt-get install -y \
        ca-certificates \
        fonts-liberation \
        libappindicator3-1 \
        libasound2 \
        libatk-bridge2.0-0 \
        libatk1.0-0 \
        libc6 \
        libcairo2 \
        libcups2 \
        libdbus-1-3 \
        libexpat1 \
        libfontconfig1 \
        libgbm1 \
        libgcc1 \
        libglib2.0-0 \
        libgtk-3-0 \
        libnspr4 \
        libnss3 \
        libpango-1.0-0 \
        libpangocairo-1.0-0 \
        libstdc++6 \
        libx11-6 \
        libx11-xcb1 \
        libxcb1 \
        libxcomposite1 \
        libxcursor1 \
        libxdamage1 \
        libxext6 \
        libxfixes3 \
        libxi6 \
        libxrandr2 \
        libxrender1 \
        libxss1 \
        libxtst6 \
        lsb-release \
        wget \
        xdg-utils
    
    # 한글 폰트 깨짐 설정
    sudo apt-get install -y \
        fonts-nanum \
        fonts-nanum-coding \
        fonts-nanum-extra \
        fonts-noto-cjk \
        fonts-noto-color-emoji \
        fonts-dejavu-core \
        fontconfig

     

    총 3개를 설치하지만 한글 폰트 깨짐이 없으시다면 폰트 설정은 무시하셔도 됩니다.

     

    저의 경우 한글 폰트가 깨져서 폰트 설정도 함께 해주었습니다.

    이를 통해서 PDF 파일을 다운 받을 수 있도록 설정하였습니다.

     

    실제 결과물은 회사정보를 포함하고 있어서 보여드리지 못하는 점 죄송합니다.

     

     

     

    Ref.

    https://velog.io/@wlduq0150/Node.js-Aws-Ec2Ubuntu%EC%97%90-puppeteer-%EC%84%B8%ED%8C%85-%EB%B0%8F-Xvfb%EB%A5%BC-%ED%86%B5%ED%95%B4-headless-False-%EC%8B%A4%ED%96%89

    https://www.npmjs.com/package/puppeteer

     

     

     

     

     

    댓글

Designed by Tistory.