Notion → GitHub 자동 배포 파이프라인 구축기
1. 배경
블로그에 글을 올리려면 매번 마크다운을 직접 작성하고 커밋해야 했습니다. 노션에서 이미 글을 쓰고 있었기 때문에 이 작업이 중복이었습니다. 노션의 Status를 Published로 바꾸는 것만으로 블로그에 자동 배포되는 파이프라인이 필요했습니다.
2. 전체 흐름
3. Notion 데이터베이스 구조
| 속성 | 타입 | 설명 |
|---|---|---|
title | title | 포스트 제목 |
status | status | draft / published / synced |
post | multi_select | 카테고리 (폴더명으로 사용) |
날짜 | date | 발행일 |
description | rich_text | 포스트 설명 (SEO, 목록에 표시) |
status를 published로 변경하는 순간 파이프라인이 시작됩니다.
4. 핵심 구현
4.1 sync_notion.py
notion-client 라이브러리 대신 requests로 Notion REST API를 직접 호출합니다. 라이브러리 버전 이슈를 피하기 위한 선택입니다.
def fetch_published_pages() -> list[dict]:
body = {
'filter': {
'property': 'status',
'status': {'equals': 'published'},
},
}
data = notion_post(f'databases/{DATABASE_ID}/query', body)
return data.get('results', [])블록을 마크다운으로 변환할 때 Python 3.10의 match 문으로 블록 타입별로 처리합니다.
match bt:
case 'heading_1': return f'\n# {rich_to_md(rich)}'
case 'heading_2': return f'\n## {rich_to_md(rich)}'
case 'code':
lang = data.get('language', '')
return f'\n```{lang}\n{get_plain_text(rich)}\n```'
case 'image':
local_path = download_image(url, image_dir)
rel = os.path.relpath(local_path, PUBLIC_DIR)
return f'\n'4.2 이미지 로컬화
노션 이미지 URL은 만료 기간이 있습니다. 동기화 시점에 이미지를 직접 다운로드하여 public/{category}/assets/{page_id}/ 에 저장합니다.
public/
book/
assets/
b648951665fa4c22b6890a96c771390d/ ← page_id
a48e201ce382.png
마크다운에서는 /book/assets/.../image.png 경로로 참조합니다. Next.js는 public/ 폴더를 정적 파일로 서빙하므로 별도 설정 없이 동작합니다.
4.3 Notion 상태 업데이트 시점
중요한 설계 결정: git push 성공 후에만 노션 상태를 synced로 업데이트합니다.
- name: Commit and push changes
id: push
run: |
git add posts/ public/
git commit -m "chore: sync notion posts"
git push
echo "pushed=true" >> $GITHUB_OUTPUT
- name: Update Notion status to synced
if: steps.push.outputs.pushed == 'true'
run: python scripts/sync_notion.pypush가 실패하면 노션 상태가 변경되지 않아 다음 실행에서 재처리됩니다.
4.4 CD 연동
sync-notion.yml이 완료되면 cd.yaml이 자동으로 트리거됩니다.
on:
workflow_run:
workflows: ['Sync Notion Posts']
types: [completed]sync가 성공했을 때만 CD가 실행되도록 조건을 추가했습니다.
if: ${{ github.event_name == 'workflow_dispatch' ||
github.event.workflow_run.conclusion == 'success' }}5. 생성되는 마크다운 형식
---
title: '[HTTP] 1. 인터넷과 네트워크'
published: true
date: 2026.03.06
description: 'IP 프로토콜의 한계와 TCP, UDP의 차이...'
---
## 1. 인터넷 통신
...title은 YAML 특수문자([, : 등)를 포함할 수 있으므로 항상 따옴표로 감쌉니다.
6. Dry Run 모드
배포 없이 변환 결과만 확인하고 싶을 때는 GitHub Actions에서 수동 실행 시 Dry run 옵션을 활성화합니다.
- 마크다운 변환 및 이미지 다운로드는 실행
git push없음- 노션 상태 변경 없음
7. 마치며
이제 노션에서 글을 완성하고 status를 published로 바꾸면 30분 이내에 블로그에 반영됩니다. 이미지 만료 문제도 해결됐고, 마크다운을 직접 관리할 필요도 없어졌습니다.