avatar
sanghyeon.dev.log

Notion API를 사용한 블로그 개발 이슈 정리

Notion API 사용기
Notion
Jul 31
·
7 min read

개요

Notion Database를 CMS로 활용하여 손쉽게 블로그를 만들고자 하였고, 블로그를 만들며 겪언던 이슈들을 정리하고자 한다.

사용한 라이브러리

notion-sdk-js는 data-fetching을 담당하는 라이브러리고, react-notion-x는 불러온 데이터를 노션과 유사한 화면으로 손쉽게 리액트에 그려주는 라이브러리이다.

react-notion-x에서 제공하는 <NotionRenderer /> 컴포넌트에 노션 페이지의 데이터가 담긴 recordMap 이라는 객체를 props로 내려주면 되는데, 이 recordMap을 가져오기 위해서는 공식 API가 아닌 notion-sdk-js에서 제공하는 방법이 아닌, react-notion-x 라이브러리 안에 담겨 있는 notion-client라는 unoffical API를 사용해야 한다.

따라서, 일반적인 경우에는 notion-sdk-js를 사용하지 않아도 노션 페이지 데이터를 가져와 화면에 뿌려줄 수 있다. 그럼에도 나는 두 라이브러리를 혼용해서 사용했는데, 이유는 아래에서 설명하겠다.

시작하기

우선 나는 화면 렌더링에 필요한 recordMap 을 제외하면, 모든 필요한 데이터는 official API (notion-sdk-js)를 통해 가져왔다. 예를 들어, 데이터베이스 내의 모든 태그 항목이라던가, Page의 unique Id, 최근 Page 등을 가져오는데 사용했다.

사실 그냥 데이터만 가져와서 화면에 보여줄 거라면 react-notion-xd의 readme만 잘 읽어도 구현할 수 있다. 내가 글을 쓰는 이유는 헤맸던 점들을 공유하기 위해서다.

💡 Good to Know:

  • notion-sdk-js는 서버사이드(node.js)에서만 동작한다.

  • database는 page의 묶음, page는 blocks의 묶음, block들이 페이지 내부에서 실제 보여지는 데이터들이다.

1. 타입 설정

Notion API를 사용하면 데이터베이스를 하나 정하고, 그 데이터베이스내에 항목들을 설정하여 데이터들을 가져올 텐데, 타입스크립트를 사용해서 내가 지정한 데이터베이스에 어떠한 이름의 항목들이 있고, 그 항목들은 무슨 속성을 가지는지 설정이 필요했다.

// database에 정해진 속성 이름들
type DatabaseProperties =
  | "title"
  | "post"
  | "create_date"
  | "author"
  | "tag";
​
// notion sdk에서 type 추출.....
export type PostResult = Extract<QueryDatabaseResponse["results"][number],{ properties: Record<string, unknown> }>;
​
type PropertyValueMap = DatabaseObjectResponse["properties"] & PageObjectResponse["properties"];
type PropertyValue = PropertyValueMap[string];
type PropertyValueType = PropertyValue["type"];
​
type ExtractedPropertyValue<T extends PropertyValueType> = Extract<
  PropertyValue,
  { type: T }
>;
// ...
​
export type RichText = ExtractedPropertyValue<"rich_text">;
export type Date = ExtractedPropertyValue<"date">;
export type Title = ExtractedPropertyValue<"title">;
export type MultiSelect = ExtractedPropertyValue<"multi_select">;
export type Files = ExtractedPropertyValue<"files">;
export type Id = ExtractedPropertyValue<"unique_id">;
​
// notion api custom response
export type BlogDatabaseObjectResponse = PostResult & {
  properties: {
    post: IDatabaseProperty.Title;
    create_date: IDatabaseProperty.Date;
    author: IDatabaseProperty.RichText;
    tag: IDatabaseProperty.MultiSelect;
    // ...
  };
};

위와 같이 타입 파일을 따로 만들어 관리했다.

2. Private Page 이미지 문제

until-1167

notion의 데이터베이스가 private page에 설정되어 있는 경우에, 이미지를 가져올 수 없는 문제가 있었다. 이유는 다음과 같다. private page의 이미지 url은 signed되어, 다른 도메인에서 접근시 인가 에러를 발생시킨다.

참조: https://github.com/NotionX/react-notion-x/issues/529

하지만! notion official API를 사용하면 외부에서 접근할 수 있는 URL을 반환해준다.

따라서 다음과 같이 함수를 작성하여 recordMap에 접근이 가능한 새로운 url image를 넣어 줬다.

/**
 * Block List로부터 file(image) sigend url을 가져온다.
 * 하위 블록에 이미지가 있을 경우를 고려해 재귀를 통해 전부 탐색한다.
 */
async function getSigendUrlFromBlocks(
  blockList: (PartialBlockObjectResponse | BlockObjectResponse)[],
  signedMap: Record<string, string>,
) {
  const promises = blockList.map(async block => {
    if ("image" in block && block.image.type === "file") {
      signedMap[block.id] = block.image.file.url;
    } else if ("has_children" in block && block.has_children) {
      // 자식의 blockList를 다시 가져와야함...
      const children = await this.getBlockList(block.id);
      // 재귀!
      await this.getSigendUrlFromBlocks(children.results, signedMap);
    }
  });
  await Promise.all(promises);
  return signedMap;
}

NotionRenderer 에서도 추가 설정을 해줘야 한다.

  <NotionRenderer
    recordMap={recordMap}
    // ...
    // image url을 notion api를 통해 가져온 url로 교체 해준다.
    mapImageUrl={(url, block) => {
      if (recordMap.signed_urls[block.id]) {
        return recordMap.signed_urls[block.id];
      }
      return url;
    }}
  />

이제 private page의 이미지도 잘 가져와진다!

3. 이미지 로딩 속도 문제

notion에 직접 업로드한 이미지를 불러오는 것은 굉장히 시간이 오래걸린다. 이유는 CDN을 제공하지 않기 때문이라고 한다.

아래는 똑같은 이미지 파일을 노션에 직접 업로드한 이미지를 가져올 때와, CDN을 제공하는 스토리지에서 가져왔을 때의 속도차이다.

until-1168

거의 20배 차이가 난다...

따라서 CDN을 제공하는 서버에 이미지를 업로드한 뒤, 그 링크를 임베딩하여 가져오는 것이 더 나은 성능을 제공하겠다.

4. 이미지 502 에러

배포(SSG) 후 일정 시간이 지나면 502 에러가 발생한다. 이는 노션에서 주기적으로 이미지 URL을 변경해서 그런 문제 같다.

until-1166

이 문제는 이미지를 노션에 업로드 하는 것이 아닌 다른 곳에 업로드한 후 가져오면 해결 된다..


- 컬렉션 아티클






안녕하세요