Draft Mode
Pages 문서 및 Data Fetching 문서에서 getStaticProps
와 getStaticPaths
를 사용하여 빌드 타임에 페이지를 사전 렌더링(정적 생성)하는 방법에 대해 이야기했습니다.
정적 생성은 페이지가 헤드리스 CMS에서 데이터를 가져올 때 유용합니다. 하지만 헤드리스 CMS에서 초안을 작성하고 페이지에서 바로 보는 것을 원할 때는 이상적이지 않습니다. 이러한 경우 Next.js가 이 페이지들을 빌드 타임이 아닌 요청 타임에 렌더링하고 게시된 컨텐츠 대신 초안 컨텐츠를 가져오도록 하고 싶을 것입니다. 이와 같이 Next.js가 특정 경우에 정적 생성을 우회하기를 원할 수 있습니다.
Next.js에는 이러한 문제를 해결하는 Draft Mode라는 기능을 제공합니다. 사용 방법은 다음과 같습니다.
Step 1: Create and access the API route
Next.js API Routes를 잘 모르는 경우 API Routes 문서을 먼저 참조하십시오.
먼저 초안 API route를 만듭니다. 이름은 자유롭게 설정할 수 있습니다 - 예: pages/api/draft.js
이 API route에서 응답 객체에 setDraftMode
를 호출해야 합니다.
export default function handler(req, res) {
// ...
res.setDraftMode({ enable: true })
// ...
}
이렇게 하면 draft 모드를 활성화하는 쿠키가 설정됩니다. 이러한 쿠키를 포함한 후속 요청은 Draft Mode를 트리거하여 정적으로 생성된 페이지의 동작을 변경합니다(이후에 더 설명함).
브라우저에서 직접 액세스하여 수동으로 테스트할 수 있도록 다음과 같은 API route를 만들고 액세스해보세요:
// 브라우저에서 수동으로 테스트하기 위한 간단한 예제입니다.
export default function handler(req, res) {
res.setDraftMode({ enable: true })
res.end('Draft mode is enabled')
}
브라우저의 개발자 도구를 열고 /api/draft
를 확인하면 __prerender_bypass
라는 이름의 쿠키가 포함된 Set-Cookie
응답 헤더를 확인할 수 있습니다.
Securely accessing it from your Headless CMS
실제로는 헤드리스 CMS에서 이 라우트 핸들러를 보안 방식으로 호출하고 싶을 것입니다. 사용 중인 헤드리스 CMS에 따라 구체적인 단계는 다를 수 있지만, 다음과 같은 일반적인 단계를 따를 수 있습니다.
이 단계는 사용 중인 헤드리스 CMS가 사용자 정의 초안 URL 설정을 지원한다고 가정합니다. 지원하지 않는 경우에도 이 방법을 사용하여 초안 URL을 보호할 수 있지만, 초안 URL을 수동으로 구성하고 접근해야 합니다.
먼저, 원하는 토큰 생성기를 사용하여 시크릿 토큰 문자열을 생성해야 합니다. 이 시크릿은 Next.js 앱과 헤드리스 CMS만 알고 있어야 합니다. 이 시크릿은 CMS에 액세스할 수 없는 사람들이 초안 URL에 접근하는 것을 방지합니다.
두 번째, 헤드리스 CMS가 사용자 정의 초안 URL 설정을 지원하는 경우, 다음을 초안 URL로 지정합니다. 이는 초안 API route가 pages/api/draft.js
에 위치한다고 가정합니다.
https://<your-site>/api/draft?secret=<token>&slug=<path>
<your-site>
는 배포 도메인이어야 합니다.<token>
은 생성한 시크릿 토큰으로 교체해야 합니다.<path>
는 보려고 하는 페이지의 경로여야 합니다./posts/foo
를 보고자 한다면&slug=/posts/foo
를 사용해야 합니다.
사용중인 헤드리스 CMS가 변수 삽입을 지원하는 경우 <path>
를 CMS 데이터에 따라 동적으로 설정할 수 있습니다: &slug=/posts/{entry.fields.slug}
마지막으로, 초안 API route에서:
- 시크릿이 일치하고
slug
매개변수가 존재하는지 확인합니다(존재하지 않으면 요청이 실패해야 합니다). res.setDraftMode
를 호출합니다.- 그런 다음 브라우저를
slug
로 지정된 경로로 리디렉션합니다(다음 예제에서는 307 redirect (opens in a new tab)을 사용합니다).
export default async (req, res) => {
// 시크릿과 다음 매개변수를 확인합니다.
// 이 시크릿은 이 API route와 CMS만 알고 있어야 합니다.
if (req.query.secret !== 'MY_SECRET_TOKEN' || !req.query.slug) {
return res.status(401).json({ message: 'Invalid token' })
}
// 제공된 `slug`가 존재하는지 확인하기 위해 헤드리스 CMS를 가져옵니다.
// getPostBySlug는 헤드리스 CMS에 필요한 fetching 로직을 구현합니다.
const post = await getPostBySlug(req.query.slug)
// slug가 존재하지 않으면 Draft Mode가 활성화되지 않도록 합니다.
if (!post) {
return res.status(401).json({ message: 'Invalid slug' })
}
// 쿠키를 설정하여 Draft Mode를 활성화합니다.
res.setDraftMode({ enable: true })
// 가져온 게시물의 경로로 리디렉션합니다.
// req.query.slug로 리디렉션하지 않는 이유는 open redirect 취약점이 발생할 수 있기 때문입니다.
res.redirect(post.slug)
}
성공하면 브라우저는 설정된 draft mode 쿠키와 함께 보고자 하는 경로로 리디렉션됩니다.
Step 2: Update getStaticProps
다음 단계는 getStaticProps
를 업데이트하여 draft mode를 지원하는 것입니다.
res.setDraftMode
를 통해 쿠키가 설정된 페이지를 요청하면 getStaticProps
가 빌드 타임이 아닌 요청 타임에 호출됩니다.
또한, context.draftMode
가 true
인 context
객체와 함께 호출됩니다.
export async function getStaticProps(context) {
if (context.draftMode) {
// 동적 데이터
}
}
우리는 초안 API route에서 res.setDraftMode
를 사용했으므로 context.draftMode
는 true
가 됩니다.
getStaticPaths
를 사용하는 경우, context.params
도 사용할 수 있습니다.
Fetch draft data
getStaticProps
를 업데이트하여 context.draftMode
에 따라 다른 데이터를 가져올 수 있습니다.
예를 들어, 헤드리스 CMS가 초안 게시물에 대해 다른 API 엔드포인트를 가지고 있는 경우, 아래와 같이 API 엔드포인트 URL을 수정할 수 있습니다:
export async function getStaticProps(context) {
const url = context.draftMode
? 'https://draft.example.com'
: 'https://production.example.com'
const res = await fetch(url)
// ...
}
이제 초안 API route(시크릿
및 slug
포함)를 헤드리스 CMS 또는 수동으로 접근하면 초안 컨텐츠를 볼 수 있어야 합니다. 초안을 게시하지 않고 업데이트하더라도 초안을 볼 수 있어야 합니다.
헤드리스 CMS에 이를 초안 URL로 설정하거나 수동으로 접근하면 초안을 볼 수 있습니다.
https://<your-site>/api/draft?secret=<token>&slug=<path>
More Details
Clear the Draft Mode cookie
기본적으로 초안 모드 세션은 브라우저를 닫으면 종료됩니다.
초안 모드 쿠키를 수동으로 지우려면 setDraftMode({ enable: false })
를 호출하는 API route를 만듭니다:
export default function handler(req, res) {
res.setDraftMode({ enable: false })
}
그런 다음 /api/disable-draft
에 요청을 보내 API Route를 호출합니다. next/link
를 사용하여 이 라우트를 호출하는 경우, 쿠키가 prefech 중에 실수로 삭제되지 않도록 prefetch={false}
를 전달해야 합니다.
Works with getServerSideProps
Draft mode는 getServerSideProps
와 함께 작동하며, context
객체에 draftMode
키로 사용할 수 있습니다.
참고: Draft Mode를 사용할 때
Cache-Control
헤더를 설정하면 우회할 수 없으므로 설정하지 않아야 합니다. 대신, ISR을 사용하는 것을 추천합니다.
Works with API Routes
API Routes는 요청 객체에서 draftMode
에 접근할 수 있습니다. 예를 들어:
export default function myApiRoute(req, res) {
if (req.draftMode) {
// 초안 데이터를 가져옵니다.
}
}
Unique per next build
next build
를 실행할 때마다 새로운 우회 쿠키 값이 생성됩니다.
이를 통해서 우회 쿠키를 추측할 수 없도록 합니다.
참고: 로컬에서 HTTP를 통해 Draft Mode를 테스트하려면 브라우저에서 서드 파티 쿠키와 로컬 스토리지 액세스를 허용해야 합니다.