Notion API를 이용한 포스팅 관리

작성일: 2023-06-27

이번에는 Notion API를 사용해서 Notion에서 작성한 글을 Markdown 파일로 생성하는 과정을 살펴보겠습니다.

작업은 다음 순서로 진행합니다.

  1. Markdown 파일 분석
  2. Notion 테이블 구성
  3. Notion API 발급
  4. 스크립트 작성

Markdown 파일 분석

제가 사용하는 Jekyll Theme인 just-the-docs에 대한 마크다운 파일 포맷은 아래와 같습니다.

---
layout: 레이아웃 (default, minimal)
title: 글 제목
nav_order: 글 정렬순서
has_children: 자식 존재 여부
grand_parent: 최상위 메뉴명
parent: 상위 메뉴명
---

Markdown 문법

Layout 메뉴를 살펴보면, Layout → A minimal layout page → Default layout child page 순서로 계층구조를 이루는데, 파일의 실제 폴더 구조와 마크다운 형태로는 다음과 같이 구성되는 것을 확인했습니다.

layout 폴더(Layout 메뉴) 하위에 layout에 대한 Markdown 파일이 자식들과 같은 레벨에 존재하는 것을 확인했습니다.

최상위, 상위, 자식에 대한 Markdown 파일을 아래와 같습니다.

---
title: Layout
layout: default
nav_order: 4.5
has_children: true -> 메뉴로 만들기 위해서는 true 설정 필요
---

# Layout
---
title: A minimal layout page
layout: minimal
parent: Layout
has_children: true
---

# A minimal layout page
---
title: Default layout child page
layout: default
parent: A minimal layout page -> 상위 메뉴 지정
grand_parent: Layout -> 최상위 메뉴 지정
---

This is a child page that uses the same minimal layout as its parent page.

Notion 테이블 구성

위에서 분석한 Markdown 구조를 바탕으로 Notion 테이블을 아래와 같이 구성했습니다.

 최상위폴더상위폴더관리번호순번제목메인공개배포작성일수정일
타입stringstringstringnumberstringbooleanbooleanbooleandatedate
매핑grand_parentparent자체nav_ordertitlehas_children자체자체자체자체

실제 Notion에서 구성한 테이블은 다음과 같습니다.

Notion API 발급

Notion API 사용을 위해 Notion 페이지에서 키를 생성한 후 안전한 곳에 보관합니다.

다음으로 Notion 데이터베이스 페이지에서 생성한 API를 연결합니다.

이후 데이터베이스에 대한 정보를 확인해야 하는데, 데이터베이스 옵션에서 보기 링크 복사 후 붙여넣기를 하면 데이터베이스 아이디를 확인할 수 있는데 이것도 안전한 곳에 보관합니다.

https://www.notion.so/<database_id>?v=<long_hash>

스크립트 작성

다음은 Notion 테이블의 글을 가져와 Markdown 파일로 변환하는 JavaScript 코드입니다.

Notion 테이블의 내용을 쉽게 Markdown 파일로 만들어주는 notion-to-md 라이브러리를 사용했습니다.

npm install -D @notionhq/client moment notion-to-md

_scripts/notion-import.js

const { Client } = require("@notionhq/client")
const { NotionToMarkdown } = require("notion-to-md")
const moment = require("moment")
const moment_timezone = require("moment-timezone")
const path = require("path")
const fs = require("fs")

const notion = new Client({
  auth: process.env.NOTION_TOKEN,
})


// passing notion client to the option
const n2m = new NotionToMarkdown({ notionClient: notion });

(async () => {
  // ensure directory exists
  const root = `docs`

  const databaseId = process.env.DATABASE_ID
  const response = await notion.databases.query({
    database_id: databaseId,
    filter: {
      "and": [
        {
          property: "공개",
          checkbox: {
            equals: true,
          },
        },
        {
          property: "배포",
          checkbox: {
            equals: true,
          },
        },
      ],
    },
  })
  for (const r of response.results) {
    const id = r.id

    // 최상위폴더
    let upUpFolder = ""
    let pUpUpFolder = r.properties?.["최상위폴더"]?.["rich_text"]
    if (pUpUpFolder) {
      upUpFolder = pUpUpFolder[0]?.["plain_text"]
    }

    // 상위폴더
    let upFolder = ""
    let pUpFolder = r.properties?.["상위폴더"]?.["rich_text"]
    if (pUpFolder) {
      upFolder = pUpFolder[0]?.["plain_text"]
    }

    // 순번
    let navOrder = r.properties?.["순번"]?.["number"] || ""

    // 제목
    let title = id
    let pTitle = r.properties?.["제목"]?.["title"]
    if (pTitle?.length > 0) {
      title = pTitle[0]?.["plain_text"]
    }

    // 메인
    let hasChild = r.properties?.["메인"]?.["checkbox"] || false

    // 작성일
    let date = moment(r.created_time).tz("Asia/Seoul").format("YYYY-MM-DD HH:mm")

    let header = `---
layout: default
title: ${title}
has_children: ${hasChild}
last_modified_date: ${date}`

    if (navOrder) {
      header += `
nav_order: ${navOrder}`
    }

    if (hasChild) {
      if (upFolder) {
        header += `
parent: ${upUpFolder}`
      }
    } else {
      header += `
grand_parent: ${upUpFolder}`
      if (upFolder) {
        header += `
parent: ${upFolder}`
      }
    }
    header += `
---`

    const folderPath = upFolder ? `${root}/${upUpFolder}/${upFolder}` : `${root}/${upUpFolder}`
    fs.mkdirSync(folderPath, { recursive: true })

    const mdBlocks = await n2m.pageToMarkdown(id)
    let body = n2m.toMarkdownString(mdBlocks)["parent"]


    //writing to file
    const fTitle = `${title}.md`
    fs.writeFile(path.join(folderPath, fTitle), header + body, (err) => {
      if (err) {
        console.log(err)
      }
    })
  }
})()

로컬환경에서 환경변수 NOTION_TOKEN, DATABASE_ID 를 등록하고 실행시키면 docs 폴더에 마크다운 파일이 생성된 것을 확인할 수 있습니다.

  • await notion.databases.query 코드에서 보면 공개배포 칼럼 모두 체크가 되어있는 글만 배포를 진행하도록 했습니다. (관리 편의성을 위해)

Reference

Jekyll-기반-Github-Pages와-Notion-Page-연동