🔊 F-Lab 에서 3월 11일에 주최한 블로그 해커톤에 참여하여 작성한 글입니다 :)

Preview Mode란?

일반적으로 Headless CMS는 SSG(Static Site Generation)를 사용하는 것이 유리하다.
SSG는 빌드 타임에 데이터를 패칭하므로 콘텐츠별로 안정적인 View를 제공할 수 있기 때문이다.

그러나, SSG는 새로 작성한 글이나 글을 수정한 내용이 즉시 반영된 모습을 확인하기 어렵다.
빌드 타임 이후의 변경 사항은 페이지에 즉각적으로 반영되지 않기 때문이다.

Next.js는 SSG에서 사용자가 요청할 때마다 변경 사항을 미리 볼 수 있도록 Preview Mode 라는 기능을 제공한다.



참고: Headless CMS

CMS란 Content Management System의 약자로 콘텐츠를 관리하는 시스템을 의미한다.
콘텐츠의 대상은 블로그가 될 수도 있고, 공지사항, Q&A 등 다양하다.
CMS는 이러한 다양한 콘텐츠를 관리하고, 사용자에게 보여줄 목적으로 사용된다.

기존의 결합형 CMS는 백엔드와 프론트엔드가 긴밀하게 연결되어 웹 사이트를 구성한다.
콘텐츠를 발행하면 데이터베이스에 저장되고, 정해진 특정 템플릿에 따라서 웹 사이트에 배포된다.
콘텐츠에 따른 서식을 완전히 제어할 수는 있겠지만 다양한 환경의 애플리케이션에서 보여야 하는 측면에서는 유연성이 떨어질 수 있다.

반면에 Headless CMS는 콘텐츠 저장소인 백엔드와, 웹 사이트의 표현 부분인 프론트엔드를 분리한다.
백엔드는 콘텐츠를 생성하거나, 저장 및 관리하고 해당 콘텐츠는 주로 JSON 형태로 API를 통해 프론트엔드로 전달된다.
프론트엔드는 API를 통해 수신한 정보를 렌더링만 하면 된다.

즉, 백엔드에서 전달받은 콘텐츠(JSON)은 디자인이나 레이아웃 정보를 포함하지 않은, 순수한 콘텐츠이다.
해당 콘텐츠를 어떻게 보여줄지에 대해서는 프론트엔드에서 관리한다.

따라서, 데스크톱이나 모바일 디바이스 등 다양한 매체의 특성에 따라 유연하게 콘텐츠를 보여줄 수 있다. 이처럼 Headless CMS는 콘텐츠의 관리와 배포가 서로 독립적이라는 것에 의의가 있다.

(Headless CMS 관련 더 자세한 설명은 이 링크를 참고해주세요.)



Preview Mode API 실습

1. API Routes로 Preview Mode API 경로를 생성한다

setPreviewData 로 쿠키 설정

Preview Mode를 활성화하려면 API Routes의 핸들러의 response 객체가 가진 setPreviewData 메소드가 필요하다.
이때 setPreviewData 로 전달되는 인자는 객체여야 한다.

setPreviewData 메소드는 브라우저에 __prerender_bypass, __next_preview_data 라는 이름으로 쿠키를 셋팅하는데 해당 쿠키가 Preview Mode를 활성화한다.
Next.js를 통해 들어오는 어떤 요청이든 해당 쿠키를 가지고 있다면 Preview Mode로 인식하기 때문이다.

공식 문서에 나온 예제 코드를 따라해봤다.

// pages/api/preview.js

export default function handler(req, res) {
  res.setPreviewData({})
  res.end('Preview mode enabled')
}


클라이언트 사이드에서 위 주소의 API를 호출하면 __prerender_bypass, __next_preview_data 라는 이름으로 쿠키가 생성된 것을 확인할 수 있다.

image


또한, 인증된 사용자에게만 Preview Mode를 제공할 수도 있다.
아래와 같이 핸들러에서 토큰을 확인하는 인증 절차를 추가했다.

// pages/api/preview.js


export default function handler(req,res) {
    if (req.query.token !== 'hj' || !req.query.post) {
        return res.status(401).json({message: 'Invalid token'})
    }

    res.setPreviewData({})

    res.redirect(`/posts/${req.query.post}`)
}


파라미터로 ‘hj’라는 token을 전달하고, post에 올바른 값을 전달해야만 Preview Mode가 적용된 URL로 리다이렉트한다.

따라서, 위 API를 호출하는 사용자는 두가지 상황을 맞이한다.

  • token이나 post 값을 전달하지 않거나, 잘못된 값을 전달한 상황

token이나 post를 전달하지 않거나, 잘못된 값을 전달하는 상황


  • 올바른 token과 post 값을 전달해서 Preview Mode가 적용된 화면으로 리다이렉트된 상황
    (브라우저의 주소창에 http://localhost:3000/api/preview?token=hj&post=329 라고 입력했다.)

올바른 token과 post를 전달해서 해당 게시물로 정상적으로 리다이렉트된 상황


쿠키의 만료

기본적으로 Preview Mode API로 설정한 쿠키에는 만료 날짜가 설정되어 있지 않아서 브라우저를 닫으면 미리보기 세션이 종료된다.
만약 쿠키를 수동으로 지워서 미리 보기를 해제하고 싶다면 아래 코드처럼 res.clearPreviewData() 메소드를 사용할 수 있다.

// pages/api/preview-clear.js

export default function handler(req, res) {
  res.clearPreviewData()

  res.status(200).json({ message: 'cookies cleared' })
}

즉, http://localhost:3000/api/preview-clear 에 접속하여 위 API를 호출하면 __prerender_bypass, __next_preview_data 를 포함한 쿠키가 제거된다.


참고로, 쿠키 만료 시간과 쿠키가 적용되어야 하는 경로를 커스텀하게 설정할 수 있는데 자세한 내용은 Specify the Preview Mode duration 를 참고하면 된다.

나는 쿠키를 클리어하는 로직을 설정해주었기 때문에 쿠키 만료 시간이나 경로를 별도로 지정하지는 않았다.

이제 인증된 사용자만이 Preview Mode를 사용할 수 있는 API Routes 구성을 모두 마쳤다.
실제로 정적 페이지에서 Preview Mode가 활성화되는지 확인해보자!



2. getStaticProps 함수에서 Preview Mode를 확인해보자

res.setPreviewData 메소드를 사용하여 쿠키가 생성되면 getStaticProps 함수는 빌드 타임이 아닌, 리퀘스트 타임마다 호출된다.

getStaticProps 함수는 context라는 객체를 파라미터로 전달 받는데 Preview Mode가 활성화됐다면 context의 필드값이 다음과 같이 넘어온다.

  • context.preview: true,
  • context.previewData: res.setPreviewData 메소드에 넘겨준 인자 (우리는 위에서 빈 객체를 넘겨줬다.)

쿠키가 셋팅된 상태로 context 객체를 콘솔 로그에 찍어보았다.

// pages/post/[id].js

export async function getStaticProps(context) {
    console.log(context);
  const postData = await getPostData(context.params.id)
  return {
    props: {
      postData,
    },
  }
}


빌드 타임 뿐만 아니라, 빌드 타임 이후에도 getStaticProps 함수를 호출할 때마다 IDE의 터미널에 아래와 같이 context가 기록되었다.

{
  params: { id: '329' },
  preview: true,
  previewData: {},
  locales: undefined,
  locale: undefined,
  defaultLocale: undefined
}



SSG는 빌드 타임에 데이터를 패칭하므로 getStaticProps 함수 안에서 콘솔 로그를 기록하더라도 빌드 타임에만 찍히는게 맞다.
그런데 Preview Mode API에서 셋팅해준 쿠키 덕분에 사용자 요청시에 매번 getStaticProps 함수를 호출하게 됐다.


3. 빌드 타임 이후에 수정한 데이터가 정적 페이지에 즉각 반영되는지 확인해보자!

(최지민 님의 Next.js 완전 정복 : 확장성 높은 커머스 서비스 구축하기 강의를 참고해 작성한 코드입니다. MDX로 블로그 게시물을 발행하는 자세한 방법은 해당 강의를 참고해주세요!)

🙋🏻‍♀️ Next.js 프로젝트에서 마크다운 파일을 추가해서 블로그 게시물을 발행할 수 있도록 설정된 상태이다.

예를 들어, 아래와 같이 md 파일을 생성하고 내용을 작성한 후에 빌드 혹은 실행하면 웹 페이지에 게시물이 발행된다!

// posts/flab-blog-hackathon.md

---
title: 'F-LAB Blog Hackathon'
date: '2023-03-11'
---

오늘은 에프랩에서 주최한 블로그 해커톤에 참여했다.  
정말 재미있었다.  
오늘의 일기 끝-!

![귀여운 강쥐](/images/puppy.png)
이미지 출처: 문랩 스튜디오 @moonlab_studio

MDX로 게시물을 발행

이제 정말 빌드 타임 이후에 게시물을 수정하거나, 새 글을 추가하고 변경 사항을 바로 확인해보자!


빌드 타임 이후에 수정한 게시물을 발행 전에 미리 보기

$ yarn build -> $ yarn start 명령어를 순서대로 실행하여 빌드한 결과물을 띄웠다.
그리고 위에서 작성했던 md 파일을 아래와 같이 수정했다.

// posts/flab-blog-hackathon.md

---
title: '짱프랩 Blog Hackathon (제목도 바꿔볼게요)'
date: '2023-03-11'
---

오늘은 에프랩에서 주최한 블로그 해커톤에 참여했다.  
정말 재미있었다.  
오늘의 일기 끝-!

![귀여운 강쥐](/images/puppy.png)
이미지 출처: 문랩 스튜디오 @moonlab_studio

빌드 타임 이후에 수정한 내용을 발행하기 전 미리볼 수 있을까요?


그리고 Preview Mode API를 호출한다.
(주소창에 http://localhost:3000/api/preview?token=hj&post=flab-blog-hackathon 를 입력하여 API Routes에 접근한다.)

빌드 타임 이후에 데이터를 수정했는데도 즉각적으로 반영된 모습을 확인할 수 있었다!
빌드 타임 이후에 게시물 수정



그리고 다음 테스트를 위해서 쿠키를 제거했다.
쿠키 클리어



빌드 타임 이후에 생성한 게시물을 발행 전에 미리 보기

빌드 타임 이후에 아래와 같이 새로운 md 파일을 추가했다.

// posts/test.md

---
title: '새로운 게시물'
date: '2023-03-11'
---

빌드 타임 이후에 새로운 게시물을 추가하고 있어요!


블로그 리스트 페이지에서 아무리 새로 고침을 해도 새로 추가한 파일이 나타나지 않는다.
빌드 타임에 패칭한 데이터만을 보여주기 때문이다.


주소창에 http://localhost:3000/api/preview?token=hj&post=test 를 입력하여 Preview Mode API를 호출해보자!

빌드 타임 이후에 추가된 게시물 미리보기


이제는 블로그 리스트 페이지로 돌아가도 새로 추가된 게시물을 확인할 수 있다.
이때 쿠키를 클리어한 후에 블로그 리스트 페이지로 돌아가면 새로 추가된 게시물은 보이지 않는다.
빌드 타임에 패칭된 데이터만 리스트로 보여주기 때문이다!


📝 정리

Next.js의 Preview Mode API 통해 SSG 기반의 페이지에서도 빌드 타임 이후의 변경 사항을 즉각 반영하여 미리 볼 수 있었다.
이전에 Next.js의 Data Fetching과 API Routes를 공부했던 것이 Preview Mode를 제대로 이해하는 데에 도움이 되었다.

(아래 링크를 확인해보세용! ☺️)

Next.js의 Data Fetching
Next.js의 API Routes

또한, 블로그 해커톤을 통해서 주말에도 집중해서 공부하고, 공부한 내용을 정리할 수 있어서 참 좋았다! ✨😇👏


참고한 문서

Preview Mode
Headless CMS: What It Is, How It Works, and Why You Need It


귀여운걸 보면 기분이 좋아지니까
이미지 출처: 문랩 스튜디오 @moonlab_studio


오늘도 화이팅입니닷 !!

댓글남기기