[Next] PDF 뷰어 구현하기 ( API 라우팅 vs embed 태그 )
2023. 1. 31. 20:46ㆍStudy/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