정적 사이트 생성기(SSG)인 Astro로 구축된 웹사이트에 노션(Notion)을 Headless CMS로 연결하고, Cloudflare Pages로 배포하는 파이프라인을 구축했습니다.
초기 아키텍처 설계부터 연동 과정에서 발생한 실제 트러블슈팅(게시글 미노출, Deploy Hook 메소드 에러, 로컬 포스트 덮어쓰기 복구, 유튜브 iframe 대괄호 파싱 이슈)까지의 상세 과정을 기록합니다.
1. 아키텍처 및 파이프라인 개요
Astro의 정적 빌드 성능과 Cloudflare Pages의 글로벌 엣지 CDN 환경을 유지하면서, 콘텐츠 작성은 생산성이 높은 노션에서 수행할 수 있도록 파이프라인을 설계했습니다.
[노션 데이터베이스] (글 작성 및 Published 플래그 활성화)
│
▼ (브라우저 북마클릿 Deploy Hook 호출)
[Cloudflare Pages 빌드 파이프라인]
│ - @notionhq/client (메타데이터 및 DB 쿼리)
│ - notion-to-md & marked (마크다운 및 시맨틱 HTML 변환)
│ - Astro SSG 정적 HTML 페이지 빌드
▼
[Cloudflare Edge CDN] 글로벌 배포 완료
2. 연동 및 구축 프로세스
1) 노션 API 및 데이터베이스 스키마 설계
- 노션 개발자 연결(Connection): Notion 개발자 포털에서 새 내부 연결(Internal Connection)을 생성하고 API 시크릿 토큰(
NOTION_TOKEN)을 발급받았습니다.
MKTGLab 노션 토큰 발급 - 데이터베이스 속성(Property) 정의:
Title(Title): 글 제목Slug(Text): 라우팅 URL 경로Published(Checkbox): 배포 필터링 플래그Date(Date): 발행일Category(Select): 카테고리 (modern-web)Tags(Multi-select): 태그 목록Author,Article Preview,Keywords,Cover Image,Image Alt,Image Caption
- 권한 연결: 데이터베이스 상단 설정에서 생성한 Connection을 연결하고 URL에서 32자리
NOTION_DATABASE_ID를 추출했습니다.
2) Astro 프로젝트 설정 및 데이터 패칭 구현
- 라이브러리 구성:
@notionhq/client,notion-to-md,marked설치. - 노션 유틸리티(
src/lib/notion.ts):Published === true조건으로 필터링하고 날짜 기준 내림차순 정렬하여 Astro 컴포넌트에 넘겨주는 인터페이스를 구현했습니다. - SSG 동적 라우팅:
src/pages/blog/index.astro(목록) 및src/pages/blog/[slug].astro(상세)에서getStaticPaths()를 적용했습니다.
3. 실무 트러블슈팅 및 해결 과정
이슈 1. 노션 글 작성 및 Published 체크 후에도 "발행된 게시글이 없습니다" 노출
- 현상: 노션 데이터베이스에 첫 글을 작성하고 Published 체크박스를 활성화했음에도 웹 화면에 빈 목록이 출력됨.
- 원인:
.env에 새로 추가한NOTION_TOKEN과NOTION_DATABASE_ID가 이미 띄워져 있던 Astro 개발 서버에 즉시 반영되지 않음. - 해결: 개발 서버 프로세스를 완전히 종료(
Ctrl + C) 후 재시작(npm run dev)하여 환경 변수를 정상 로드함.
이슈 2. Cloudflare Deploy Hook 호출 시 405 Method Not Allowed (code: 1001)
- 현상: Cloudflare Pages 대시보드에서 생성한 Deploy Hook URL을 브라우저 주소창에 직접 입력하자
{ "code": 1001, "error": "method_not_allowed" }JSON 에러 반환. - 원인: Cloudflare의 Deploy Hook은
POST요청을 수신해야 하지만, 일반 브라우저 주소창 입력은GET방식으로 전송됨. - 해결: 브라우저 북마크에 일반 URL 대신 자바스크립트
fetch로POST를 전송하는 원클릭 북마클릿(Bookmarklet)을 등록하여 해결함.
javascript:(function(){
fetch('YOUR_CLOUDFLARE_DEPLOY_HOOK_URL', { method: 'POST' })
.then(r => {
if(r.ok) { alert('✅ Cloudflare 배포가 시작되었습니다! (약 1분 소요)'); }
else { alert('❌ 배포 실패: ' + r.status); }
})
.catch(e => alert('오류: ' + e));
})();
이슈 3. 노션 연동 후 기존 로컬 마크다운 포스트 소실 현상
- 현상: 노션 연동 코드를 반영한 직후, 기존
src/content/blog/내 로컬 마크다운 파일 기반 이전 글들이 사이트 목록에서 제외됨. - 원인: 블로그 목록 및 상세 페이지 템플릿이 노션 API 데이터만 단독으로 바라보도록 덮어씌워짐.
- 해결:
Astro Content Collection(getCollection('blog'))데이터와 노션API(getPublishedPosts())데이터를 병합(Merge)하고, 날짜순으로 재정렬하여 로컬 마크다운 포스트와 노션 포스트가 한 화면에 함께 나오도록 템플릿 로직을 복구함.
이슈 4. 본문 유튜브 iframe 임베딩 시 404 Not Found 발생
- 현상: 노션 본문에 삽입한 유튜브
<iframe>코드가 화면에서 영상 대신404: Not found (Path: /blog/[https://www.youtube.com/embed/...])에러 박스로 렌더링됨. - 원인:
notion-to-md변환 과정에서 HTML raw 태그 내부의 URL을 일반 마크다운 하이퍼링크 문법([url])으로 오인하여 주소 앞뒤에 대괄호([ ])가 붙었고, Astro가 이를 내부 상대 경로(/blog/...)로 파싱함. - 해결: 마크다운 파서 전달 전 단계에서
src="[http...]"패턴을 정규식으로 감지하여 대괄호를 제거하는 치환 로직(replace(/src="\[(https?:\/\/[^\]]+)\]"/g, 'src="$1"'))을 적용하여 정상 렌더링을 구현함.
4. 최종 운영 워크플로우
- 콘텐츠 작성: 노션 데이터베이스에서 글 작성, 태그/카테고리 지정, 본문(시맨틱 HTML 테이블, 유튜브 임베드 포함) 입력 후
Published체크. - 원클릭 배포: 브라우저 북마크 바의 🚀 블로그 배포 클릭.
- 결과 확인: Cloudflare Pages가 노션 최신 데이터를 빌드하여 1분 이내에 글로벌 엣지 CDN에 배포 완료.