페이지 렌더링
Notion 페이지를 React 앱에 렌더링하는 방법을 알아봅니다.
실제 동작 데모
tip
위 데모는 실제 동작하는 컴포넌트입니다. 전체 Storybook에서 더 많은 옵션을 테스트해보세요.
언제 쓰나요?
- 블로그 글을 Notion에서 작성하고, 내 웹사이트에 보여주고 싶을 때
- 문서/매뉴얼을 Notion으로 관리하고, 별도 페이지에 보여주고 싶을 때
- Notion 페이지를 "새 창"이 아니라 내 앱 안에서 보여주고 싶을 때
방법 1: 전체 페이지 렌더링
가장 기본적인 방법입니다. Notion 페이지 하나를 통째로 React 컴포넌트로 렌더링합니다.
완성 코드 (복사해서 바로 사용)
// app/notion-page/page.tsx
import { NotionThemeProvider, NotionPage } from '@gendive/react-notion-renderer';
export default function NotionDemoPage() {
return (
<NotionThemeProvider>
<NotionPage
pageId="2baa76221f408089b1a9cfce9c133621"
token={process.env.NOTION_TOKEN!}
/>
</NotionThemeProvider>
);
}
환경변수 설정
.env.local 파일에 Notion API 토큰을 넣어주세요:
NOTION_TOKEN=ntn_xxxxx...
결과
Notion 페이지의 모든 블록(제목, 문단, 코드, 이미지, callout 등)이 React 컴포넌트로 렌더링됩니다.
방법 2: 데이터를 먼저 가져온 후 렌더링
더 세밀하게 제어하고 싶다면, 데이터를 먼저 가져오고 별도로 렌더링할 수 있습니다.
왜 이렇게 하나요?
- 데이터를 캐싱하거나 가공하고 싶을 때
- 페이지 제목만 따로 쓰고 싶을 때
- 로딩 상태를 직접 관리하고 싶을 때
코드
// app/notion-page/page.tsx
import {
fetchNotionPage,
NotionThemeProvider,
NotionPageRenderer,
} from '@gendive/react-notion-renderer';
export default async function Page() {
// 1. 데이터 가져오기
const page = await fetchNotionPage(
'2baa76221f408089b1a9cfce9c133621', // 페이지 ID
process.env.NOTION_TOKEN!, // API 토큰
);
// 2. 렌더링
return (
<NotionThemeProvider>
<h1>{page.title}</h1> {/* 페이지 제목 따로 사용 가능 */}
<NotionPageRenderer page={page} />
</NotionThemeProvider>
);
}
fetchNotionPage 반환값
{
id: string; // 페이지 ID
title: string; // 페이지 제목
blocks: NotionBlock[]; // 블록 배열
}
방법 3: 모달로 페이지 보여주기
데이터베이스에서 카드를 클릭했을 때, 모달(팝업)로 페이지 내용을 보여주고 싶다면 NotionPageModal을 사용합니다.
동작 방식
- 사용자가 카드 클릭
- 모달이 열리면서 해당 페이지 데이터를 가져옴
- 모달 안에 페이지 내용 표시
- 모달 닫기 버튼 또는 바깥 클릭으로 닫기
단계별 설정
1단계: API Route 만들기
브라우저에서는 Notion API를 직접 호출할 수 없습니다.
Next.js API Route를 통해 서버에서 가져오도록 합니다.
// app/api/notion/page/[pageId]/route.ts
export { GET } from '@gendive/react-notion-renderer/api';
이 한 줄이면 /api/notion/page/[pageId] 엔드포인트가 만들어집니다.
2단계: 환경변수 확인
# .env.local
NOTION_TOKEN=ntn_xxxxx...
3단계: 모달 컴포넌트 사용
"use client";
import { useState } from 'react';
import { NotionPageModal } from '@gendive/react-notion-renderer';
import { fetchNotionPageClient } from '@gendive/react-notion-renderer/client';
export function PageModalDemo() {
const [selectedPageId, setSelectedPageId] = useState<string | null>(null);
return (
<div>
{/* 클릭하면 모달 열기 */}
<button onClick={() => setSelectedPageId('2baa76221f408089b1a9cfce9c133621')}>
페이지 보기
</button>
{/* 모달 */}
<NotionPageModal
pageId={selectedPageId}
onClose={() => setSelectedPageId(null)}
fetchPage={fetchNotionPageClient}
/>
</div>
);
}
NotionPageModal Props
| Prop | 타입 | 설명 |
|---|---|---|
pageId | string | null | 표시할 페이지 ID. null이면 모달이 닫힘 |
onClose | () => void | 모달을 닫을 때 호출되는 함수 |
fetchPage | Function | 페이지 데이터를 가져오는 함수 |
테마 설정
라이트/다크 모드
<NotionThemeProvider theme="dark">
<NotionPage pageId="..." token="..." />
</NotionThemeProvider>
| 값 | 설명 |
|---|---|
"light" | 밝은 테마 |
"dark" | 어두운 테마 |
| 생략 | 시스템 설정 따름 |
지원하는 블록 타입
이 라이브러리가 렌더링할 수 있는 Notion 블록들입니다:
텍스트/구조
| 블록 | 설명 |
|---|---|
paragraph | 일반 문단 |
heading_1, heading_2, heading_3 | 제목 |
quote | 인용문 |
callout | 콜아웃 (아이콘 + 배경색) |
toggle | 토글 (접기/펼치기) |
divider | 구분선 |
리스트
| 블록 | 설명 |
|---|---|
bulleted_list_item | 글머리 기호 목록 |
numbered_list_item | 번호 목록 |
to_do | 체크박스 |
미디어
| 블록 | 설명 |
|---|---|
image | 이미지 |
video | 비디오 |
embed | 외부 콘텐츠 임베드 (iframe) |
bookmark | 북마크 링크 |
코드/표
| 블록 | 설명 |
|---|---|
code | 코드 블록 (복사 버튼 포함) |
table | 표 |
자세한 미디어 블록 사용법은 Embed & Media 문서를 참고하세요.
자주 묻는 질문
Q. 페이지 ID는 어디서 찾나요?
Notion 페이지 URL에서 찾을 수 있습니다:
https://www.notion.so/My-Page-Title-2baa76221f408089b1a9cfce9c133621
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
이 부분이 페이지 ID
Q. "Could not find page" 에러가 나요
Integration이 해당 페이지에 연결되어 있는지 확인하세요:
- Notion에서 페이지 열기
...→ Connections → Integration 선택
Q. 이미지가 안 보여요
Notion 이미지는 1시간 후 만료됩니다. 프로덕션에서는 이미지를 별도 스토리지에 저장하는 것을 권장합니다.