0ju-log
💻 Frontend

[DevOps] 지금 이 블로그 자동 배포 구축하기

💡 시작하기

맨날 노션에 쓰고 버셀 들어가서 Redeploy 눌러서 갱신하는 자체가 너어어어어어어어어무 귀찮았다

그래서 노션에 글을 쓰면 굳이굳이 버셀을 들어가지 않아도 알아서 자동으로 재배포가 되는 CD를 구축하기로 했다

이제 회사에서 CICD도 구축해봤으니 함 해봐야지!

🤖 지피띠니가 알려준 전체 구조

Notion (글 작성)
   ↓
status = Public 변경
   ↓
GitHub Actions (주기적 체크)
   ↓
"새로 Public 된 글 있음?" 판단
   ↓
Vercel Deploy Hook 호출
   ↓
Next.js 재빌드
   ↓
Notion 글 반영

즉, Polling을 통해 기존 데이터와 갱신된 데이터의 diff를 비교하고 diff가 생겼을 시 자동 재배포하도록 구축하면 된다는 뜻인 것 같다(라고 일단은 이해했다)

📌 구축 시작! @ㅅ@

1️⃣ Notion API에 들어가서 노션 블로그 데이터베이스와 연동을 하자

  1. ↓ 아래 사이트에 들어간다

  2. API의 이름을 정하여 자신만의 Notion API를 만든다

  3. 사용 권한 탭을 눌러 연동할 블로그의 데이터베이스를 추가해준다

  4. 데이터베이스 페이지로 들어와 우측 상단 ・・・ → Connections을 확인한다

    그럼 요롷게 뾰로롱하고 생긴다능

2️⃣ Environment Secrets을 세팅해주자

  1. 블로그를 배포한 Vercel에 들어간다

    • Settings → Git으로 들어가면 Deploy Hooks가 있다
    • name과 branch를 작성해준 후, Create Hook 을 클릭해주면 아래 사진과 같이 Deploy Hook이 완성된다! 이걸 Copy해서 Github Secret에 넣을 예정이다

  2. 내 노션 블로그 Github를 들어간다

    • Settings → Secrets and Variables → Actions 로 들어가면 Environment SecretsRepository Secrets가 있는데 반드시 Repository Secrets에 세팅을 해줘야 한다!!! (처음에 Environment에 넣어놓고 5번 정도 CD Failed 나와서 애꿎은 지피띠니탓 함..)

    • 세 가지의 secret을 넣어줘야 한다

      NOTION_TOKENNotion API에 있던 프라이빗 API 통합 시크릿
      NOTION_DATABASE_IDhttps://qwerqwer.notion.site/{이부분에해당하는ID}?v=qwertyuiopasdfghjklzxcvbnm&source=copy_link
      VERCEL_DEPLOY_HOOKvercel에서 copy한 deploy hook

3️⃣ 코드를 작성하자

.notionsync/published.json

이 코드에는 NotionAPI를 통해 불러온 블로그의 글의 ID가 작성될 예정이다

{
  "pageIds": []
}

scripts/check-notion.js

이 코드는 Notion 데이터베이스에서 Public 상태인 페이지를 가져와 이전에 배포한 것과 비교하고, 새 글이 있으면 Vercel 배포를 트리거하고, 현재 상태를 캐시에 저장하는 로직이다

if (!process.env.GITHUB_ACTIONS) {
  require("dotenv").config()
}
const fs = require("fs")
const CACHE_PATH = ".notionsync/published.json"

async function run() {
  const prev = new Set(JSON.parse(fs.readFileSync(CACHE_PATH, "utf8")).pageIds)

  const res = await fetch(
    `https://api.notion.com/v1/databases/${process.env.NOTION_DATABASE_ID}/query`,
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.NOTION_TOKEN}`,
        "Notion-Version": "2022-06-28",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        filter: {
          property: "status",
          select: { equals: "Public" },
        },
      }),
    }
  )

  console.log("DATABASE_ID:", process.env.NOTION_DATABASE_ID)

  const data = await res.json()
  console.dir(data, { depth: null })

  if (!Array.isArray(data.results)) {
    console.log("No valid results from Notion API. Skip deploy.")
    process.exit(0)
  }

  const current = data.results.map((page) => page.id)

  const hasNew = current.some((id) => !prev.has(id))

  if (!hasNew) {
    console.log("No new public posts. Skip deploy.")
    process.exit(0)
  }

  console.log("✨ New public posts detected")
  await fetch(process.env.VERCEL_DEPLOY_HOOK, { method: "POST" })
  fs.writeFileSync(CACHE_PATH, JSON.stringify({ pageIds: current }, null, 2))
}

run()

.github/workflow

이 코드는 10분마다 또는 수동 실행 시 Notion에서 새 공개 글 확인 후, 새 글이 있으면 Vercel 배포를 트리거하고, 배포 후 캐시 파일을 GitHub에 커밋 및 업데이트하는 워크플로우다

name: Notion Auto Deploy

on:
  schedule:
    - cron: "*/10 * * * *"
  workflow_dispatch:

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 18

      - name: Install dependencies
        run: yarn install --frozen-lockfile

      - name: Check Notion status
        run: node scripts/check-notion.js
        env:
          NOTION_TOKEN: ${{ secrets.NOTION_TOKEN }}
          NOTION_DATABASE_ID: ${{ secrets.NOTION_DATABASE_ID }}
          VERCEL_DEPLOY_HOOK: ${{ secrets.VERCEL_DEPLOY_HOOK }}

      - name: Update cache
        run: |
          if git diff --quiet .notionsync/published.json; then
            echo "No cache changes, skip commit"
            exit 0
          fi

          git config user.name "깃허브이름"
          git config user.email "깃허브메일"
          git add .notionsync/published.json
          git commit -m "chore: update notion publish cache [skip ci]"
          git push

4️⃣ 결과 확인

  • 새로 작성한 글이 없을 때

  • 새로운 글을 작성했을 때

대성공 야호~!

버셀 deployment에도 잘 뜬다 ㅎㅅㅎ

🤔 이후 계획

  1. 현재 10분 단위로 스케줄링하여 배포하는 방식을 사용하고 있는데 아래 이미지 보면 거의 30분에 한 번씩 워크플로우가 돌아간다

    이건 Github의 상황에 따라 바뀌는 거라 어쩔 수 없다고는 하지만 조금 더 찾아보고 정한 기준의 시간에 따라 돌아갈 수 있도록 수정해볼 예정이다

  2. 현재는 status 칼럼의 값이 Public으로 바뀌면 github에서 diff를 인식하여 배포가 되는 방식으로 구현했는데, 추후 Public인 글 중 수정된 칼럼이 있다면 그것도 인지하여 배포되도록 수정해볼 예정이다