뒤로
Cailyn Yong
Cailyn Yong ·

추석동안 oopy.io 결제하기 싫어서 직접 만든 노션 연동 웹사이트

Screenshot 2023-10-02 at 11.32.43 PM.png우피는 웹사이트 빌더중에 단언컨대 최고입니다. 하지만 최대한 돈을 아껴쓰고 싶은 저에게 oopy 도메인을 사서 결제까지 하는건 사치라고 생각했습니다.

추석 한 주 내내 10X AI University 런칭을 위해 웹사이트를 빌딩했는데, 각 커리큘럼 챕터 별로 방대한 양의 컨텐츠 내용들을 직접 Next js와 Tailwind 프론트엔드 개발을 해서 올릴 걸 생각하니 머릿속이 하얘지더라구요.

어떻게 하면 oopy를 사용하지 않고 노션에 올린 내용을 바로 내 웹사이트 도메인에 올릴 수 있을까?

oopy에 웹사이트 연동을 하기 위해서는 무조건 Notion 사이트를 공개로 돌려야 하는데 저 같은 경우에는 유료 회원들만 내용을 접하게 하고 싶었기 때문에 공개로 돌릴 수 없었습니다.

그래서 인터넷을 뒤지다가 몇일동안 할 일을 하루만에 단축시켜준 개발 package를 발견했습니다.

바로 이겁니다: https://github.com/kerwanp/notion-render

노션 데이터베이스에 작성하는 모든 컨텐츠들을 바로 노션 스타일 형식처럼 렌더링해서 Next js 앱에 보여지게 하는 툴입니다.

이 글에서 직접 활용하는 것까지 개발 과정을 공유해드리겠습니다!

1. 노션 데이터베이스 만들기

먼저 노션에 이런식으로 테이블을 하나 만들어주세요.

여기서 중요하고 필요한 column 값들은 Status와 Slug 입니다.

Screenshot 2023-10-02 at 11.46.43 PM.png그 다음 이 데이터베이스의 ID를 찾아줍니다. ID를 찾기 위해서는 상단의 오른쪽 코너에 있는 ... 점 세개 버튼을 누르고 Copy Link를 눌러주세요.

Screenshot 2023-10-02 at 11.48.51 PM.pngnotion.so/5k314fze586d45639eild48951afba89?v=4ca2463b53614a5691bd

그럼 이런식으로 링크가 보여지는데 여기서 데이터베이스의 ID는 www.notion.so/ 다음부터 ? 물음표 전까지의 값입니다. 즉 이 예시에서 ID는 "5k314fze586d45639eild48951afba89" 입니다.

2. 노션 개발자 토큰 얻기

https://www.notion.so/my-integrations 이 링크로 간다음에 새로운 프로젝트를 만들어주세요.

Screenshot 2023-10-02 at 11.57.20 PM.png그 다음 Secret key를 복사해서 저장합니다.

Screenshot 2023-10-02 at 11.58.00 PM.png환경 설정에서는 데이터베이스의 컨텐츠들을 읽어올 수 있게 허용을 해줍니다.

Screenshot 2023-10-02 at 11.59.13 PM.png

3. 프로젝트 셋업하기

이미 기존의 Next JS 앱이 있다면 터미널에 다음의 패키지들을 다운받아주세요.

$ yarn add @notionhq/client @notion-render/client @notion-render/hljs-plugin @notion-render/bookmark-plugin
# Or
$ npm install @notionhq/client @notion-render/client @notion-render/hljs-plugin @notion-render/bookmark-plugin

그 다음에 로컬 .env 파일에 다음 값들을 저장해주세요. 여기서 NOTION_TOKEN은 위에서 저장한 notion secret key이고, NOTION_DATABSE_ID는 데이터베이스 ID 입니다.

NOTION_TOKEN="secret_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
NOTION_DATABASE_ID="xxxxxxxxxxxxxxxxxxxxxxx"

4. 노션 데이터베이스 불러오기

그 다음 Next JS 앱 환경에서 lib 폴더에 notion.ts라는 파일을 만들어줍니다.

import "server-only";

import { Client } from "@notionhq/client";
import React from "react";
import {
  BlockObjectResponse,
  PageObjectResponse,
} from "@notionhq/client/build/src/api-endpoints";

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

export const fetchPages = React.cache(() => {
  return notion.databases.query({
    database_id: process.env.NOTION_DATABASE_ID!,
    filter: {
      property: "Status",
      select: {
        equals: "Published",
      },
    },
  });
});

export const fetchPageBySlug = React.cache((slug: string) => {
  return notion.databases
    .query({
      database_id: process.env.NOTION_DATABASE_ID!,
      filter: {
        property: "Slug",
        rich_text: {
          equals: slug,
        },
      },
    })
    .then((res) => res.results[0] as PageObjectResponse | undefined);
});

export const fetchPageBlocks = React.cache((pageId: string) => {
  return notion.blocks.children
    .list({ block_id: pageId })
    .then((res) => res.results as BlockObjectResponse[]);
});
  1. fetchPages() 함수는 노션 데이터베이스 ID를 사용해서 데이터베이스를 불러온 다음, 데이터베이스의 column 값들 중에서 Status가 "Published"로 돼있는 컨텐츠들만 불러온다는 뜻

  2. fetchPageBySlug() 함수는 마찬가지로 데이터베이스를 불러오지만 데이터베이스의 column 값 중에서 Slug에 있는 텍스트를 바탕으로 내용을 불러온다는 뜻

5. page.tsx에서 데이터베이스 컨텐츠 렌더링하기

그 다음 app/blog/[slug]/page.tsx와 같은 파일 내에서 아래의 코드를 사용합니다.

여기서 const post = await fetchPageBySlug("week2-employee") 줄은 맨 위에 제 데이터베이스 사진에서 Slug column 값들 중에 "week2-employee"로 돼있는 페이지 컨텐츠를 불러온다는 뜻입니다.

import { fetchPageBlocks, fetchPageBySlug, notion } from "@/lib/notion";
import { NotionRenderer } from "@notion-render/client";
import { notFound } from "next/navigation";
import { Client } from "@notionhq/client"
import "@notion-render/client/sass/theme.scss"

export default async function Page() {
  const post = await fetchPageBySlug("week2-employee");
  if (!post) notFound();

  const blocks = await fetchPageBlocks(post.id);

  const client = new Client({ auth: process.env.NOTION_TOKEN })
  const renderer = new NotionRenderer({ client })

  const html = await renderer.render(...blocks);

  return <div className="notion-render" dangerouslySetInnerHTML={{ __html: html }}></div>;
}

그리고 className = "notion-render"를 통해 노션 스타일링 형식에 맞게 페이지 컨텐츠를 불러오도록 합니다.

그럼 노션에 작성한 컨텐츠 스타일링 그대로 Next JS 웹사이트에 컨텐츠가 뜹니다.

Code block부터 이미지, Header, inline code, bullet list 등이 다 렌더링됩니다. (렌더링 되지 않는 블록들도 있습니다! 깃헙 페이지에서 확인하세요.)

Screenshot 2023-10-03 at 12.15.32 AM.png이런식으로 노션에서 작성한 스타일링 대로 그대로 뜹니다!!

마무리..

혹시 하면서 안되는 부분 있으면 댓글 달아주세요! 저도 겨우겨우 우여곡절에 성공한거라 모든 방법들은 다 해봤던 것 같네요.

29

댓글

로그인 후 댓글을 남길 수 있습니다.

이상선
이상선

대단해요~~~

Cailyn Yong
Cailyn Yong

감사합니다!!

칸맨파
칸맨파

대박~ 능력자시네여

장영운
장영운

Next JS 앱을 사용하신건 이용자 권한 때문인가요?

Cailyn Yong
Cailyn Yong

처음에 next auth 쓸려고 next js 사용했는데 supabase에서도 지원을 해줘서 굳이 next를 쓸 필요는 없는 것 같아요! 근데 프론트엔드는 계속 이걸 써왔어서 익숙해서 썼습니다 :)

Adam
Adam

공유 감사합니다!