[Next] PDF 뷰어 구현하기 ( API 라우팅 vs embed 태그 )

2023. 1. 31. 20:46Study/Next

    개요

    아늑 프로젝트에서 대학원 입시요강을 PDF 뷰어로 웹상에서 볼 수 있도록 구현해야 했다. 처음에는 Next.js에서 API 동적 라우팅을 통해 해당하는 PDF를 불러오도록 했다. 여기서 발생한 문제는 일반대학원, 교육대학원으로 2개의 pdf를 불러와야 하는 경우가 있다는 점이었다. 실제 배포해서 사용할 수 있는 서비스를 구현하고 있다 보니 로딩 속도가 가장 주된 관심사였다. 그래서 머릿 속에서만 고민하지 말고 실제로 비교해보고자 했다! 결과적으로는 embed 태그로 바로 불러왔다.

     

    lighthouse로 성능 비교

    비교 조건

    • 동일한 pdf 파일을 불러오도록 함.
    • 하나의 파일만 불러오도록 함.

     

    embed 태그를 사용해서 pdf 파일을 불러왔을 때

     

    API 라우팅을 통해 pdf 파일을 불러왔을 때

     

    전체적인 성능은 비슷했지만 embed의 Speed Index가 0.5초 정도 더 빠른 것을 볼 수 있었다. 대학원 입시요강 페이지는 탭을 선택했을 때 해당하는 대학별 입시요강 pdf를 보여주는 것이 목적이기 때문에, 주 콘텐츠인 pdf 파일이 빨리 보이는 게 중요하다고 생각해서 embed로 PDF Viewer를 구현했다.

     

    후기

    • Next.js에서 동적 API 라우팅 생성하는 작업을 처음 해보다 보니 삭제하기 아쉽긴 했다. 하지만 사용하지 않을 모듈은 삭제해서 번들파일을 최대한 축소시키기 위해 싹 삭제했다. 대신 블로그에 기록하기!
    • 우려했던 부분이 실제 성능 차이를 보이고, 더 좋은 선택을 할 수 있어서 좋았다. 서비스 최적화에 대해서 더 알아보고자 한다.
    • 중복되는 코드가 몇몇 보여서 리팩토링이 필요해보인다. 우선 구현해야 하는 범위까지 빠르게 마감해놓고 코드를 싹 리팩토링해야겠다.

     

    페이지 실제 모습

     

    API 라우팅 코드

    // pages/api/pdf0.ts
    import { NextApiRequest, NextApiResponse } from 'next';
    import fs from 'fs';
    
    export default async (req: NextApiRequest, res: NextApiResponse) => {
      try {
        const file = fs.readFileSync('public/pdf/서울대(일,교).pdf');
        res.setHeader('Content-Type', 'application/pdf');
        res.setHeader('Content-Disposition', 'inline; filename="sample.pdf"');
        return res.status(200).send(file);
      } catch (error: any) {
        return res.status(400).json({ message: error.message });
      }
    };
    
    // pages/graduate/guide.tsx
      const PDFViewer = (props: any) => {
        return (
          <iframe
            src={`/api/pdf${props.props}`}
            title="pdf-viewer"
            width="100%"
            height="768px"
          />
        );
      };

     

    탭 + embed 코드

    // pages/graduate/guide.tsx
      interface Tab {
        id?: string;
        label?: string;
        content?: React.ReactNode;
      }
    
      interface Props {
        tabs: Tab[];
      }
    
      const Tabs: React.FC<Props> = ({ tabs }) => {
        const [activeTab, setActiveTab] = useState(tabs[0].id);
    
        return (
          <>
            {/* 탭 나열 */}
            <ul className="flexBox flex-shrink-0 border-gray-200">
              {tabs.map((tab) => (
                <li
                  key={tab.id}
                  // 선택된 탭이면 배경색 바뀌도록
                  className={`m-4 w-56 h-12 p-4 flexBox text-base text-center cursor-pointer rounded-2xl border ${
                    activeTab === tab.id ? 'bg-shadowColor' : ''
                  }`}
                  onClick={() => setActiveTab(tab.id)}
                >
                  {tab.label}
                </li>
              ))}
            </ul>
            <div className="p-6">
              {/* 현재 선택된 탭의 content 삽입 */}
              {tabs.find((tab) => tab.id === activeTab)?.content}
            </div>
          </>
        );
      };
              <Tabs
                tabs={[
                  {
                    id: '0',
                    label: '서울대학교 모집요강',
                    content: (
                      <>
                        <h1 className="text-xl mb-2.5">일반대학원, 교육대학원</h1>
                        <embed
                          className="w-full  h-[768px]"
                          src="/pdf/서울대(일,교).pdf"
                          type="application/pdf"
                        />
                      </>
                    ),
                  },
    .
    .
    .
                ]}
              />

     

    프로젝트에 대한 자세한 코드는 제 깃허브에서 확인하실 수 있습니다!

    https://github.com/dorrion/Psychology-Brochure

     

    GitHub - dorrion/Psychology-Brochure: 심리학과 학생들에게 희망 진로에 맞는 커리큘럼을 추천하기 위한

    심리학과 학생들에게 희망 진로에 맞는 커리큘럼을 추천하기 위한 홈페이지입니다. Contribute to dorrion/Psychology-Brochure development by creating an account on GitHub.

    github.com

     

    Reference

    맨 위로
    // //