[개인] 기술 블로그 with Notion
역할 개인 프로젝트
요약 Notion을 CMS로 쓰는 블로그를 직접 제작, 운영 중.(2024~) API 제약(호출 제한, 이미지 만료)을 캐시 설계로 해결하고, Next.js 16 마이그레이션까지 지속적으로 개선 중.
프로젝트 개요
Notion 데이터베이스를 CMS로 사용하는 개인 기술 블로그입니다. 글은 Notion에 쓰고, 사이트는 Notion API를 렌더링합니다.
2024.02 — 최초 제작·배포
2024.12 — 다크 모드
2025.11 — Next.js 16 마이그레이션, FSD 재구조화, PR시 Gemini Code Assist 도입
최초 구현 화면





1. Notion API 구조 분석과 재귀 렌더링
Notion 페이지에 있는 구성 요소의 기본 단위는 Block이고, 구조상 Block은 기본적으로 다형성을 가진다고 생각해 Polymorphic Component로 처리할 수 있다고 생각했습니다.
{
"object": "block",
"id": "c02fc1d3-db8b-45c5-a222-27595b15aea7",
"has_more": "...",
"next_cursor": "...", //다음 블록 id
"has_children": true,
...,
//블록의 메타 데이터
"type": "heading_2",
"heading_2": { //type의 value가 key로 오는 모습 (다형성)
...,
//블록의 실질적인 데이터
"children": [...] //block[],
//깊이 2단계(children의 children)부터는 따로 요청해야 합니다.
//모든 블록을 얻으려면 BFS의 형태로 Block API를 재귀호출해야 합니다.
}
}2. API 제약을 캐시 설계로 해결
Notion API는 초당 평균 3회 호출 제한이 있고, 위 구조 때문에 페이지 하나에도 다수의 재귀 호출이 필요합니다.
CSR/SSR로는 블로그가 커질수록 호출량을 감당할 수 없다고 판단해 ISR을 채택했습니다.
빌드 시 Fn Timeout이 발생했습니다. 중복 호출을 없애기 위해 Promise 기반 인메모리 캐시를 직접 구현했습니다. (배포 시 전체 호출 143 → 102회, 글 리스트 API 40 → 6회)
Notion 이미지 URL에 만료 시간이 포함되어 ISR 페이지에서 이미지가 깨지는 문제는, 이미지 컴포넌트의
onError에서 해당 블록만 재요청해 리로드하도록 해결했습니다.
3. Next.js 16 마이그레이션
뒤집힌 캐시 전략, Next.js 16의 Cache Component – SYdev.seungyoon-yu.comCache Components로 알아보는 Next.js 16의 새로운 캐시 전략Next.js 16의 Cache Components('use cache')가 기존의 라우트 단위 ISR을 대체하면서, 직접 구현했던 Promise 캐시를 프레임워크 캐싱으로 정리했습니다. 마이그레이션에 앞서 변경된 캐시 전략의 설계 의도를 분석했고, 이 과정에서 프로젝트 전체를 FSD(Feature-Sliced Design)로 재구조화해 기능 단위 응집도를 높였습니다.
4. 다크 모드 전환 최적화
CSS
data attribute로 테마를 한 번에 전환, Vanilla Extract와 함께 사용해 인라인 스타일 없이 DOM 재계산을 최소화최초 방문 시 시스템 설정을 따르고, 수동 변경 시 저장된 설정 우선. 시스템 설정 변경에도 실시간 반응
SSR 페이지의 테마 초기화에는
useLayoutEffect가 적절하지만 hydration error를 유발해useIsomorphicLayoutEffect로 해결
운영
2024년부터 꾸준히 프로젝트의 회고 및 트러블슈팅 기록을 축적하고 있습니다.