Notion API로 블로그 만들기 (with npm) - 1

Notion API로 블로그 만들기 (with npm) - 1

현재 여러분이 보고 있는 내 블로그의 포스팅은 아래 사진처럼 노션 Table(DB)에 저장되어 있다.
notion image
내 블로그는 NextJSreact-notion-x 라이브러리 조합으로 작동한다. 서버사이드에서 노션 페이지(포스팅)의 정보를 가져와 react-notion-x 의 렌더러를 사용해 포스팅을 보여주는 것이다.
현재 아주 잘 작동하고 있다. 다만 앞으로도 잘 작동할 것인지는 의문이 있는데 react-notion-x 의 유틸 쪽에서 노션의 비공식 API를 쓰고 있기 때문이다.
예를 들면, 노션에서 페이지 내보내기 기능이 있는데 노션의 공식 API에서는 내보내기 API가 없는 것이다. 노션 앱에서는 당연히 API를 통해 내보내기 기능을 제공할 텐데 외부 개발자들이 사용할 수 있도록 내놓은 노션 공식 API에는 해당 API 기능이 빠져있는 것이다.
이처럼 노션 공식 API만을 사용해서는 기능 구현이 번거롭거나 제한적인 부분들이 있다. 그래서 그런지 대부분의 노션을 활용한 라이브러리는 노션 비공식 API를 활용하는 것 같다.
노션 비공식 API 모음
하지만 비공식 API이기 때문에 언제든 하위 호환성이 깨질 수 있다. 그리고 해당 라이브러리를 쓰면서 아래와 같은 몇몇 문제에 부딪혔다.

라이브러리 만드는 목적

위와 같은 이유로 결국 나의 목적은 내 블로그에 쓸 노션 관련 라이브러리를 직접 만들어 보는 것이다. 라이브러리를 만들며 달성하고 싶은 세부 목표는 아래와 같다.
  1. 최대한 노션 공식 API를 사용하여 현재 기능 + a 를 제공하는 라이브러리를 만든다.
  1. 프론트엔드 개발자로서 직접 라이브러리를 만들어 오픈소스에 기여해보는 경험을 얻는다.

Steps

직접 구현해 볼 대상 선정

react-notion-x
NotionXUpdated Mar 7, 2025
내 블로그는 위 레포에서 notion-client, notion-utils, react-notion-x 라이브러리를 쓰고 있는데 우선, notion-client, notion-utils 의 기능을 직접 만들어보자! react-notion-x 는 노션 컴포넌트 렌더러인데 코드 양이 방대해서 제일 나중에 작업한다.

기술 선정

라이브러리를 만드는데 필요한 기술을 정해보자.
  • Package Management: pnpm
  • Repo Management : turborepo
  • Bundling: tsup
  • Testing : vitest
  • Linting : ESLint + Prettier (Biome도 고려해보자)
  • Versioning : Changeset
현재 인기가 많은 모노레포 툴은 turborepo라고 생각해서 선정했다. 연장선으로 turborepo의 특정 버전 이상에서는 yarn berry의 PnP를 지원하지 않기 때문에 pnpm를 선택했다. 나머지 도구도 현시점에서 많이 사용되고 레퍼런스가 많아 선정했다.
각 도구(Tool)는 자세히 보면 동일한 목적을 가진 도구에서 특징, 장점은 조금씩 차이가 있지만 큰 관점에서 목적에 맞게 비슷한 기능을 제공한다. 어떤 도구에서 아주 뛰어난 특징, 장점은 시간이 지나며 다른 도구에서도 사용되고 그렇지 못한 도구는 인기를 잃는다.
각 도구(예를 들면 Testing)의 목적과 세부 도구(vitest, jest가 예시)들의 특징과 장점을 빠르게 학습하고 적용하는 게 오늘날 개발자에게 중요한 능력같다. (물론 JS, TS, React, Next 등 코어 한 라이브러리는 내부까지 깊게 파보는 게 너무 중요하다!)
Changeset에 대한 설명과 적용은 아래 포스팅에서 이미 다루었다.
요즘 많이 사용하는 tsup에 대해 간단히 알아보고 기술 선정의 이유는 여기서 마무리한다. (하나 하나 다 쓰면 글이 너무 길어진다..ㅠ)

JavaScript 모듈 시스템의 발전 과정

번들링에 대해 알아보려면 우선 JavaScript 모듈 시스템의 발전 과정을 알아봐야 한다. 간단히 알아보자!

초기 JavaScript

초기에는 물론 JavaScript의 모듈 시스템이 없었다. 그래서 전역 스코프에서 모든 코드가 실행되어 변수명 충돌 등의 문제가 발생했다.

CommonJS (2009)

Node.js 가 서버사이드 JavaScript 개발을 위해 CommonJS를 채택했다. 주요 특징은 아래와 같다.
  • require() 와 module.exports 를 사용한 동기적 모듈 로딩
  • 서버 환경에 최적화된 설계
  • 파일 시스템 접근이 빠른 서버 환경에 적합
  • 동기적으로 모듈을 로드
이와 비슷한 시기에 AMD 라는 모듈 시스템도 나왔다. RequireJS 를 통해 구현되었는데 경쟁에서 밀려 오늘날에 거의 쓰이지 않는다.

ES Modules (ES2015)

JavaScript 언어 자체에 내장된 공식 모듈 시스템이다.
  • import 와 export 문법으로 더 간결한 코드 작성 가능
  • 정적 분석이 가능해 트리 쉐이킹과 최적화에 유리
  • 브라우저와 Node.js 모두에서 사용 가능한 표준
  • 비동기적으로 모듈을 로드하며, top-level await을 지원

둘 간의 호환성

  • ESM에서는 CommonJS 모듈을 import 할 수 있다.
  • CommonJS에서는 ESM 모듈을 직접 require 할 수 없으며, 동적 import() 를 사용해야 한다.
현재는 ESM이 JavaScript 의 표준 모듈 시스템이라고 할 수 있다. 하지만, CommonJS도 여전히 Node.js 생태계에서 널리 사용되고 있으며, 레거시 코드와 호환성을 위해 지원한다.
현재 여러분의 코드를 ESM, CommonJS로 변환해주는 번들링 도구 중 많이 사용하는 것은 esbuild, rollup 이 있다.
간단하게 두 개의 특징을 보자.

Rollup

  • format 옵션을 통해 ESM과 CommonJS 출력을 모두 지원
  • 하나의 소스코드로 두 가지 포맷의 번들을 동시에 생성

ESBuild

  • Go 언어로 작성되어 매우 빠른 속도
  • 병렬 처리를 통해 모든 CPU 코어를 최대한 활용
  • 증분 컴파일을 지원하여 변경된 부분만 다시 빌드
  • ESM에서 CommonJS로의 변환이나 그 반대 경우에 일부 제한
Vite와 같은 도구들은 개발 환경에서는 빠른 esbuild 를 사용하고, 프로덕션 빌드에는 안정적인 rollup 을 사용하는 전략을 채택하고 있는데, ESM ↔ CommonJS로 변환 시에 일부 제한이 있어서 그런 전략을 선택했나?? 하는 생각이 든다.
그런데 나는 rollup , esbuild 둘 중 하나를 쓰지 않고 tsup 를 사용했다.

tsup이 뭔데?

tsup 은 2020년에 처음 출시되었으며, TypeScript 라이브러리를 위한 번들러이다. rollup 를 간단히 사용해 본 적이 있는데 수많은 플러그인을 설치해야 하고 스크립트를 짜야할 것도 생각보다 많았다. tsup 은 이러한 문제를 해결하는 기존 번들러들의 복잡한 설정 없이 빠르게 번들링 할 수 있는 도구라고 할 수 있다.
esbuild 의 성능과 SWC(Speedy Web Compiler)의 장점을 결합했다고 한다.
주요 특징
  • Zero-Config 지향
    • 별도의 복잡한 설정 없이도 사용 가능
  • 성능
    • esbuild 를 기반으로 하여 매우 빠른 빌드 속도를 제공
    • 병렬 처리를 통해 모든 CPU 코어를 최대한 활용
내 라이브러리에 사용한 tsup.config.ts
위와 같이 설정하고 pnpm build 커맨드를 입력하면 아래와 같이 빌드가 되었음을 확인할 수 있다.
notion image
별다른 설정 없이 config에 몇 개의 값만 추가해서 빌드를 정상적으로 하였다. 그런데 한 가지 의문점이 들었다. esbuild 에서는 ESM ↔ CommonJS로 변환 시에 일부 제한이 있다고 했는데 어떻게 tsup 은 그 문제를 풀었을까? tsup 레포 issue와 구글링을 해보았는데 명확한 정답은 찾지 못했다. 그래서 아래와 같이 tsup 개발자에게 질문을 남겼다..!

마무리

라이브러리에 유틸 코드와 테스트 코드 작성하는 것까지 작성하려고 했는데 생각보다 글이 길어져서 다음 편으로 넘겨야겠다!

Reference