개요
현재 저는 Notion API 연동 블로그를 개발 중 입니다. Notion 에 개발 관련 글을 작성하고 정리를 해두었는데, 다른 유명 블로그 플랫폼에 복사 붙여넣기로 옮기기를 시도했습니다만, 문제가 좀 있었습니다.
- 내가 Notion에 작성한 그대로의 표현이 되지 않는다.
- Notion은 대체로 마크다운을 따르지만, Notion만의 특수한 기호가 추가되어있어 (블록
>
등) 대부분의 마크다운 뷰어에서의 표현이 Notion 뷰어에서의 표현과 다릅니다.
- 매번 Notion 에 글을 쓰고 다른 블로그에 복사 붙여넣기 하는 것은 여러모로 불편하다.
- 일일이 복붙하고 깨진 글을 정리해주어야하며, Notion에 작성한 글을 수정하면, 블로그 글에도 수정해주어야한다.
이런 이유로 저는 Notion 에서 제공하는 API 를 사용해 제가 작성하던 방식에 맞는 방식으로 Notion에 작성하던 TIL 중 공개하고 싶은 포스트만 따로 공개할 수 있는 블로그를 개발하고자 했습니다.
저는 다음과 같이 Notion 데이터베이스에 TIL 을 작성하고, 상태를 두어 공개 상태인 포스트만 블로그에서 가져가길 원했습니다. Notion API 는 무료!로 많은 기능을 제공하기 때문에, 이를 실제로 제 Next.js 프로젝트에 가져다 사용하고, 구현하는데 긴 시간이 걸리지는 않았습니다. 하지만, API docs 와 실제 구현이 조금 다른 부분(특히 타입!!)도 있어서 이를 조율하는데 애먹었던 것 같습니다.
Notion API 를 사용가능하게 만들기
일단, Notion API 를 사용하기 위해서는 두가지 절차가 필요합니다.
- Notion API Key 를 받아야합니다.
- 연동하고 싶은 개인 Notion 페이지를 공개해야합니다.
일단 API Key 를 받는 것은 간단합니다.
해당 북마크에 들어가서
새 API 통합 을 통해
API 통합 이름을 지정 후 (로고는 없어도 됩니다.) 제출하면 끝
그리고 만들어진 API 통합을 클릭하면 다음과 같이 시크릿이 나오는데, 이 시크릿을 사용하면 됩니다.
그리고, 새 API 통합을 만드셨다면, 내가 API 를 통해 부르거나 업데이트할 페이지를 공개해 주시고, 다음과 같이 API 통합과 연결해주시면 됩니다.
공개한 페이지의 우측 상단에 옵션 버튼 클릭 나오는 드롭다운 최하단을 보면, 연결 항목이 옵션이 보입니다. 여기서 내가 새로 만든 API 통합 이름을 선택해 연결하면 됩니다.
이러면 이제 Notion 에서 제공하는 API 를 통해 공개한 페이지 및 그 하위 페이지들을 (공개 하셨다면) 가져오거나, 수정, 삭제 할 수 있습니다.
Next.js 프로젝트에 적용
Notion API 를 Next.js 에서 사용하기 위해서는 제공하는 프론트엔드 라이브러리를 사용해야 합니다. Notion 에서는 많은 라이브러리를 제공하는데요. 저는 다음과 같이 세가지 라이브러리를 사용했습니다.
"dependencies": { "@notionhq/client": "^2.2.15", "next": "14.2.2", "notion-client": "^6.16.0" "react-notion-x": "^6.16.0", //...others },
@notionhq/client
- v1 API 를 호출할 때 사용하는 클라이언트 라이브러리 입니다. (구체적인 엔드포인트는
api.notion.com/v1
) - 꾸준히 관리되고있습니다. (24년 4월까지 변경사항을 올라온 것을 확인했습니다.)
- 데이터베이스의 데이터들을 filter, sort 해서 가져올 때 사용했습니다.
- docs 주소
react-notion-x
¬ion-client
- v3 API 를 호출할 때 사용하는 클라이언트 라이브러리 입니다. (구체적인 엔드포인트는
www.notion.so/api/v3
) - react 관련 프로젝트에서 손쉽게 사용할 수 있도록 만든 라이브러리 입니다.
- 관리가 원활히 되고있지 않습니다. (최근 커밋 8개월 전)
- Notion 의 마크다운을 그대로 표현할 수 있는 컴포넌트를 사용하기 위해 사용했습니다.
- docs 주소
react-notion-x
NotionX • Updated Oct 16, 2024
Notion 은 API 버전이 v3 까지 나와있습니다. v1 은 docs 에 사용법이 아주 잘 나와있어서 편하게 사용이 가능합니다. v3 은
react-notion-x
github 의 readme 가 docs 역할을 하는데, 그래서 그런지 사용하기가 쉽지 않았습니다. (가져올 때 데이터들을 filter 할 때, 자기네들만의 query string 이 있는데, 이 문법을 어떻게 사용하는지 나와있는 곳을 찾지 못 했었습니다.) 그래서 처음에는 react-notion-x
와 notion-client
만 가지고 해결하려고 시도하다가, 방법을 찾지 못 해서 @notionhq/client
을 추가로 사용하였습니다. docs 를 잘 나와 있어서 잘 읽고 사용해도 되지만, 이렇게 넘어가기엔 사용시 유의사항이 있기에 사용법을 작성해보겠습니다.
@notionhq/client
내부 서비스 사용하기@notionhq/client
서비스 세팅Client
클래스를 사용해야합니다. auth 는 필수로 들어가야합니다.- 환경변수인
NOTION_KEY
는 아까 API 통합에서 복사해놓은 시크릿 키 입니다. - 환경변수인
NOTION_DATABASE_ID
는 데이터베이스의 ID 입니다. getPosts
는 제가 지정한 데이터베이스에서 원하는 row 를 쿼리해오는 서비스입니다.- 쿼리는 데이터베이스의 상태 컬럼이 공개인 것을 날짜 최신순으로 가져오도록 합니다.
- 이 서비스는 v3 버전에서도 비슷한게 있지만, 쿼리가 이 API 처럼 객체로 받는 것이 아니라
string
형태로 받습니다. 근데 어떻게 사용해야하는지 docs 에 자세히 나오지 않아요. 그래서 만약 복잡한 쿼리를 해야한다면, v1 버전을 사용하시는 것을 추천드립니다. getDatabaseTags
는 제가 지정한 데이터베이스에서 특정한 태그들의 종류를 가져오는 서비스 입니다.- 구현된 라이브러리의 type 과 실제 response 가 다른 상황입니다. 직접 console.log 로 찍어보면서 값을 찾았습니다.
import { Client } from "@notionhq/client"; const notionToken = process.env.NOTION_KEY; const notionDatabaseId = process.env.NOTION_DATABASE_ID; const notion = new Client({ auth: notionToken, }); export async function getPosts() { const response = await notion.databases.query({ database_id: notionDatabaseId as string, filter: { property: "상태", status: { equals: "공개", }, }, sorts: [ { property: "날짜", direction: "descending", }, ], }); return response.results as DatabaseObjectResponse[]; } export async function getDatabaseTags() { const response = await notion.databases.retrieve({ database_id: notionDatabaseId as string, }); const tags = (response as any).properties["Tags"].multi_select.options; return tags as Tag[]; }
다음과 같이 데이터베이스를 전체 페이지로 열고 들어가면 나오는 링크의 뒤에 복잡한 코드가 데이터베이스의 ID 입니다.
notion-client
내부 서비스 사용하기react-notion-x
¬ion-client
서비스 세팅NotionAPI
클래스를 사용해야합니다. 페이지 정보만 가져오는 경우에는 반드시activeUser
,authToken
이 필요한 것은 아닙니다.- 환경변수
NOTION_USER_ID
,NOTION_TOKEN_V2
는 Notion 페이지에서 개발자도구 → Application 탭 → cookies 에서www.notion.so
쿠키를 확인하고,
import { DatabaseObjectResponse } from "@notionhq/client/build/src/api-endpoints"; import { NotionAPI } from "notion-client"; const notionUser = process.env.NOTION_USER_ID; const notionTokenv2 = process.env.NOTION_TOKEN_V2; const notionApi = new NotionAPI({ activeUser: notionUser, authToken: notionTokenv2, }); export async function getPage(id: string) { const response = await notionApi.getPage(id); return response; }
두 값을 확인해 복사 붙여넣기 하시면 됩니다.
2.
getPage
서비스는 특정 페이지의 ExpandedRecordMap
객체를 return 해줍니다. 이 서비스의 response 를 통해서 NotionRenderer
라는 컴포넌트에 해당 객체를 넘기기만 하면 화면이 보여집니다.react-notion-x
를 통해 노션 마크다운을 제대로 렌더링 하기
"use client"; import Image from "next/image"; import { NotionRenderer } from "react-notion-x"; import { Code } from "react-notion-x/build/third-party/code"; import { Collection } from "react-notion-x/build/third-party/collection"; import { ExtendedRecordMap } from "notion-types"; import "react-notion-x/src/styles.css"; const ClientNotionRenderer = ({ recordMap, }: { recordMap: ExtendedRecordMap; }) => { return ( <NotionRenderer recordMap={recordMap} components={{ nextImage: Image, Code: Code, Collection: Collection, }} fullPage={true} darkMode={false} disableHeader /> ); }; export default ClientNotionRenderer;
components
속성에 필요한 컴포넌트를 추가해 주어야합니다. 표현하고자 하는 페이지가 Code 블록을 사용한다면, Code 컴포넌트를 추가해야하고, Collection (데이터베이스나 이런것들) 을 사용한다면 Collection 컴포넌트를 추가해야합니다. 해당 페이지를 불러올 때, 경고 콘솔이 뜨기 때문에 확인하기 용이합니다.NotionRenderer
컴포넌트는 내부적으로 Web API 인 intersection-observer api 를 사용하고 있어, 서버에서 렌더링이 불가능 합니다. (서버 컴포넌트로 렌더하려고 하면 에러남) 그래서 클라이언트 컴포넌트로 만들고, 컴포넌트 트리 최하위 노드로 빼서 사용하고 있습니다.import ClientNotionRenderer from "@/components/posts/ClientNotionRenderer"; import { getPage } from "@/services/notion"; type PostDetailPageProps = { params: { postId: string }; }; export default async function PostDetailPage({ params }: PostDetailPageProps) { const pageRecordMap = await getPage(params.postId); return <ClientNotionRenderer recordMap={pageRecordMap} />; }
해당 기능을 통해 완성한 블로그 페이지 입니다. 제가 공개 태그를 걸어놓은 포스트만 보이게 됩니다.
마무리
Next.js 14 버전 기준으로 Notion API 연동해서 제가 원하는 기능을 블로그 프로젝트에 적용해 보았습니다. docs 가 v1 api 들은 정말 구체적이고 잘 나와있어서, 복잡한 백엔드 기능들은
@notionhq/client
를 통해 사용하고, 전체 페이지를 노션 스타일에 맞게 보여주고 싶을 때만, react-notion-x
, notion-client
를 사용하는게 좋을 것 같습니다.