Gatsby에서 Notion CMS로 갈아탄 이유와 방법
author
postNo
status
thumbnail
description
category
tags
createdAt
updatedAt
Gatsby에서 Notion CMS로 옮겨가는 블로그 콘텐츠 파이프라인
Gatsby에서 Notion CMS로 옮겨가는 블로그 콘텐츠 파이프라인

한 줄 요약

Gatsby가 나빠서 바꾼 것은 아니다. Gatsby와 Markdown 조합은 개발자가 혼자 글을 쓰고 배포하기에는 여전히 단순하고 강력하다. 다만 블로그를 계속 운영하다 보니 병목은 렌더링 프레임워크가 아니라 글을 쓰고, 고치고, 이미지를 관리하고, 메타데이터를 맞추는 과정에 있었다.
그래서 콘텐츠의 원천을 Git 저장소의 Markdown 파일에서 Notion 데이터베이스로 옮겼다. 코드는 여전히 정적 사이트를 만들고, 글 작성과 운영은 Notion에서 처리한다. 역할을 분리한 셈이다.

기존 구조: Gatsby와 Markdown

기존 블로그는 Gatsby 기반이었다. Gatsby 프로젝트에서 글은 src/posts 아래 Markdown 파일로 관리되고, 각 파일의 frontmatter가 글의 메타데이터가 된다.
---
path: '/posts/1'
title: 'TypeORM Replication 설정과 MySQL PoolCluster'
description: 'TypeORM replication 설정 유무에 따라 MySQL 연결이 갈리는 이유를 정리합니다'
date: 2026-05-26
categories: [Database]
tags: [TypeORM, MySQL, NestJS]
thumb: 'mysql.jpg'
---
 
그리고 Gatsby에서 Markdown 파일이 정적 파일로 서빙되는 파이프라인은 대략 이렇다.
Markdown files
-> gatsby-source-filesystem
-> gatsby-transformer-remark
-> gatsby-node.js 
   createPages()
-> static HTML
이 구조의 장점은 분명했다. 모든 글이 파일이라 Git으로 변경 이력이 남고, 빌드 결과는 정적 파일이라 운영이 단순하다. 코드 블록, 이미지, RSS, 페이지네이션도 Gatsby 플러그인으로 해결할 수 있었다.
 
하지만 시간이 지나면서 불편한 지점도 선명해졌다.
  • 글 하나를 고치려면 Github 저장소를 열고 Markdown 파일을 찾아야 한다.
  • 본문을 작성할 때마다 제목, 목록, 코드 블록, 이미지 같은 Markdown 문법을 직접 신경 써야 한다.
  • 글을 수정한 뒤에는 git add, git commit, git push를 거쳐야 해서 단순한 문장 수정도 배포 작업처럼 느껴진다.
  • PC가 바뀌면 저장소 clone, 의존성 설치, branch 동기화, pull/push 상태를 다시 맞춰야 한다.
  • 이미지가 들어가면 static/thumb, src/posts/images 같은 경로와 실제 배포 URL을 계속 맞춰야 한다.
  • 제목, 설명, 날짜, 태그, 썸네일이 frontmatter에 흩어져 있어 목록 관리가 어렵다.
  • 글을 작성하는 일과 배포하는 일이 너무 강하게 묶여 있다.
  • 개발자가 아닌 사람이 초안을 보거나 수정하기 어렵다.
블로그가 개인 메모장에 가까울 때는 문제가 작다. 하지만 블로그의 글 수가 늘고 운영 단위가 커지면, 콘텐츠를 파일로만 관리하는 방식은 점점 무겁게 느껴졌다.
 

Notion CMS를 선택한 이유

Notion CMS로 옮긴 가장 큰 이유는 작성 경험과 운영 경험을 분리하기 위해서다.
Gatsby에서는 코드 저장소가 CMS 역할까지 했다. 글 작성, 이미지 관리, 메타데이터 입력, 배포 준비가 모두 Git 안에서 일어났다. 반대로 Notion을 CMS로 쓰면 글 작성자는 Notion DB에 row를 추가하고 본문을 작성하면 된다. 사이트는 그 DB를 읽어 정적 페이지를 만든다.
 
이번 전환에서 기대한 효과는 네 가지였다.
 
첫째, 글 작성 진입 장벽을 낮춘다. Notion은 이미 많은 사람이 익숙하게 쓰는 편집기다. 초안 작성, 이미지 붙여넣기, 문단 이동, 코멘트 같은 작업이 Markdown 파일보다 쉽다.
둘째, 글 목록을 데이터베이스로 관리한다. postNo, status, category, tags, description, createdAt, updatedAt 같은 값이 테이블 컬럼으로 보이면 누락과 중복을 찾기 쉽다.
셋째, 공개 상태를 명확하게 둔다. Notion DB의 status작성중발행 완료로 나누면 글 작성과 배포 판단을 분리할 수 있다.
넷째, 사이트 코드는 렌더링에 집중한다. 새로운 프로젝트는 Notion에서 데이터를 가져와서 본문을 렌더링하고, Next.js로 정적 페이지를 만든다. 콘텐츠를 어떻게 쓸지는 Notion이 맡고, 사이트는 어떻게 보여줄지만 책임진다. 책임이 분리되었다.
 

옮긴 방법

전환은 한 번에 모든 것을 새로 짜는 방식이 아니라, 기존 Gatsby의 데이터 모델을 Notion DB 속성으로 옮기는 방식으로 진행했다.
먼저 Gatsby frontmatter를 Notion DB 컬럼으로 대응시킨다.
path        -> postNo 또는 slug 전략
title       -> title
description -> description
date        -> createdAt / updatedAt
categories  -> category
tags        -> tags
thumb       -> thumbnail 또는 page cover
본문         -> Notion page content
 
그 다음 현재 프로젝트에 Notion 읽기 계층을 만들었다.
Notion DB
-> src/lib/notion/client.ts
-> src/lib/notion/getPosts.ts
-> src/lib/notion/mapPage.ts
-> src/pages/index.tsx
-> src/pages/posts/[slug].tsx
client.tsnotion-client로 Notion 페이지를 가져온다. 공식 Integration 토큰을 쓰는 방식이 아니라, 공개 게시된 Notion DB를 비공식 API로 읽는 방식이다.
const notion = new NotionAPI()

export const fetchPage = (pageId: string) =>
  notion.getPage(pageId, {
    throwOnCollectionErrors: true,
    collectionReducerLimit: 10000,
  })
 
getPosts.ts는 DB recordMap에서 collection schema와 row block id 목록을 뽑고, 각 row를 블로그에서 쓰기 좋은 형태로 바꾼다. 이때 status발행 완료인 글만 목록에 노출한다.
recordMap.collection -> schema
recordMap.collection_query -> row ids
row block -> TPost
 
mapPage.ts는 Notion의 속성 구조를 TPost로 변환한다. Notion 비공식 API 응답은 속성이 이름이 아니라 내부 property id로 keyed 되어 있기 때문에, 먼저 schema에서 status, category, tags, description, createdAt 같은 속성 id를 찾은 뒤 값을 읽는다.
 
본문은 별도로 Markdown으로 변환하지 않는다. 상세 페이지에서 해당 Notion page의 recordMap을 다시 가져오고, react-notion-xNotionRenderer에 그대로 넘긴다. 그래서 Notion의 제목, 문단, 코드 블록, 이미지, 콜아웃 같은 블록을 비교적 자연스럽게 렌더링할 수 있다.
 

Notion CMS에서 글을 가져오는 방법

프로젝트에서 글을 가져오는 흐름은 아주 단순하다.
  1. Notion DB를 웹에 게시한다.
  1. NOTION_DATABASE_ID에 DB page id를 넣는다.
  1. 빌드 시 fetchPage(databaseId)로 DB recordMap을 가져온다.
  1. collection schema와 row id 목록을 추출한다.
  1. 각 row의 속성을 TPost로 변환한다.
  1. status발행 완료인 글만 목록과 상세 경로로 만든다.
  1. 상세 페이지에서는 row page id로 Notion page를 다시 가져와 NotionRenderer로 렌더링한다.
 
운영자가 새 글을 추가할 때는 다음 값만 맞추면 된다.
postNo: 140
title: Gatsby에서 Notion CMS로 갈아탄 이유와 방법
description: 목록과 SEO에 노출될 한 줄 설명
category: Development, Migration
tags: blog, Migration, Web, deploy
createdAt / updatedAt: 발행일
status: 작성중 또는 발행 완료
cover: 외부 이미지 URL
작성중 상태에서는 초안으로 두고, 공개할 때 발행 완료로 바꾸면 된다. 사이트 빌드는 이 값을 기준으로 글 노출 여부를 결정한다.
 

전환하면서 얻은 것

가장 큰 변화는 글 작성이 코드 변경에서 분리된 것이다. 이제 글을 하나 고치기 위해 매번 Markdown 파일을 찾고 frontmatter를 손볼 필요가 없다. 단순한 문장 수정 때문에 Git commit과 push를 반복하지 않아도 되고, 어떤 PC에서든 Notion만 열면 같은 초안과 글 목록을 이어서 다룰 수 있다. Notion DB에서 제목, 설명, 날짜, 태그, 공개 상태를 한눈에 보고 수정할 수 있다.
또한 사이트 코드는 더 명확해졌다. Gatsby 시절에는 파일 시스템, Markdown 파서, 이미지 플러그인, 페이지 생성 로직이 한 파이프라인 안에 있었다. 지금은 Notion 읽기 계층, 데이터 매핑 계층, 화면 렌더링 계층이 나뉘어 있다.
물론 Notion CMS도 만능은 아니다. 비공식 API를 쓰면 Notion 구조 변경에 민감할 수 있고, 내부 attachment 이미지는 조심해야 한다. 그래서 빌드 실패를 빨리 발견할 수 있게 테스트를 두고, 이미지는 가능한 외부 URL 기준으로 관리해야 한다.
 

마무리

Gatsby에서 Notion CMS로 옮긴 이유는 프레임워크를 바꾸고 싶어서가 아니라 운영 병목을 줄이고 싶어서였다.
글은 Notion에서 더 쉽게 쓰고, 사이트는 Next.js가 정적으로 더 안정적으로 만든다.
 
정리하면 방향은 이렇다.
Gatsby + Markdown: 개발자 중심의 파일 기반 블로그
Notion CMS + Next.js: 편집기 중심의 데이터 기반 블로그
글을 쓰는 사람에게는 Notion DB가 CMS가 되고, 사이트를 만드는 코드에게는 Notion DB가 읽기 전용 콘텐츠 API가 된다. 이 정도의 역할 분리가 이번 전환의 핵심이다.