loading ...
노션 API 연동 초기 세팅
1. 통합 API 토큰 발급
- 설정 페이지 접속
- API 관리 페이지 접속
- 새 API 통합
- 설정
- API 키 확인 + 기능 설정
2. API 연동을 위한 페이지/데이터 베이스 사용 권한 지정
아래와 같이 연결을 위한 페이지/DB를 지정하고 사용 권한을 주어야합니다.
- 연결이 필요한 페이지에 접속하여 오른쪽 상단 … 을 눌러 원하는 API와 연결합니다
노션 API 활용하기
우선, 노션 API는 모든 요청에서 HTTP Authorization 헤더를 사용하여 인증과 권한 부여를 처리합니다.
저희는 위에서 생성한 통함 API 시크릿 키(통합 토큰)을 활용하면 됩니다!
1. API 호출 방법
API 호출 방법은 크게 두 가지가 있습니다.
1) 직접 HTTP 요청
터미널이나 일반적인 HTTP 클라이언트에서 호출할 때 사용하는 표준 방식입니다.
curl 'https://api.notion.com/v1/users' \
-H 'Authorization: Bearer '"$NOTION_ACCESS_TOKEN"'' \
-H "Notion-Version: 2022-06-28"
- 필수 헤더 1: Authorization: Bearer {토큰}
- 필수 헤더 2: Notion-Version: 2022-06-28 (사용할 API 버전 명시)
2) Notion SDK for JS
new Client 초기화 시 토큰을 한 번만 전달하면 요청에서 자동으로 사용됩니다.
const { Client } = require('@notionhq/client');
const client = new Client({ auth: process.env.NOTION_ACCESS_TOKEN });
사실 저는 1번 방식을 성공하지 못했고 2번만 성공했습니다. 하하 2번에 대해 초점을 둬서 해보도록 하죠
우선 노션 API 를 다루기 전에 노션에서 사용하는 데이터 구조를 간단하게 이해하고자 합니다
크게 블록, 페이지, 데이터베이스, 데이터 소스의 개념과 차이점 정도를 간단하게 알면 좋을 것 같습니다.
2. 노션에서의 데이터 구조 이해
1) 블록 (Block)
블록은 노션의 가장 최소 단위이자 모든 콘텐츠의 기본 구성 요소입니다.
노션 페이지 내에서 보는 텍스트 한 줄, 이미지 하나, 제목 하나가 모두 개별적인 블록입니다.
따라서 노션 모든 페이지의 콘텐츠는 블록 리스트로 구성되며, 블록은 자식 블록을 갖는 계층 구조로 이루어집니다.
- 주요 type
- paragraph: 일반 텍스트 문단
- heading_1, heading_2, heading_3: 다양한 크기의 제목
- image, video, file: 미디어 파일
- to_do: 체크리스트 아이템
- code: 프로그래밍 코드 블록
- 실제 코드 예시
2) 페이지 (Page)
페이지는 블록들을 담는 컨테이너이자, 데이터베이스의 개별 아이템입니다.
페이지는 고유한 ID와 속성(Properties), 그리고 본문(블록 리스트)을 가지며, 독립적으로 존재하거나 데이터베이스의 하위 요소로 존재할 수 있습니다.
- 주요 속성:
- title: 페이지의 제목
- icon, cover: 페이지를 꾸미는 아이콘과 커버 이미지
- parent: 페이지가 속한 부모 요소(다른 페이지, 데이터베이스, 또는 워크스페이스) 정보
- 주로 페이지의 메타데이터(작성일, 카테고리 등)를 조회할 때 사용합니다.
3) 데이터베이스 (Database)
데이터베이스는 여러 페이지를 구조화하여 관리하는 페이지들의 집합입니다.
데이터베이스는 각 페이지(아이템)들이 가져야 할 스키마(속성 정의)를 결정합니다.
- 구성 요소:
- properties: 데이터베이스 내 모든 페이지가 공유하는 컬럼 정의
- title: 데이터베이스의 이름
4) 데이터 소스 (Data Source)
데이터 소스는 노션 외부의 데이터를 노션과 연결하기 위한 기술적인 통로
Notion API에서 데이터베이스(Database)와 페이지(Page)를 추상화하여, 구조와 내용물(row)을 쉽게 가져오고 조작할 수 있게 해주는 단위
5) 결론
| 구분 | 역할 | API 요청 대상 |
| 블록 | 내용의 최소 단위 | block_id (본문 내용 요청) |
| 페이지 | 정보의 최소 단위 | page_id (특정 글 정보 요청) |
| 데이터베이스 | 정보의 관리 단위 | database_id (글 목록 요청) |
| 데이터 소스 | 외부 데이터 연결 | data_source_id (동기화 설정) |
3. API 호출 코드로 이해하기
API 호출 전 저희가 URL 을 통해 접근 가능한 id는 database 의 id 입니다.
저희가 DB의 내용을 직접 접근하기 위해서는 dataSources 의 id 가 필요하기 때문에 SDK 코드를 통해 databaseId → dataSourcesId 과정을 거쳐야합니다!
1) URL로 DB ID 가져오기
DB의 ID는 URL의 아래 부분을 가져오면 됩니다.
이 과정에서 주의해야할 건 인라인 DB와 페이지 DB의 ID가 각각 다르다는 것입니다
- 인라인 DB
인라인 DB는 아래와 같이 전체 페이지를 누른 후에 열리는 페이지에서 URL을 확인해야합니다.
2) API 호출하기
우선, 아래 DB를 기반으로 호출을 해봤습니다
우선은 단순 터미널에서 Node.js로 스크립트를 직접 실행하여 Notion API를 호출했습니다.
npm install @notionhq/client
import { Client } from "@notionhq/client";
import dotenv from "dotenv";
dotenv.config();
const notion = new Client({
프론트엔드와의 연동을 생각했을 때는 express 서버용 페이지를 별도로 생성하여 우리 페이지 내에서 사용될 API 를 따로 생성해주는 형태로 진행했습니다.
import express from "express";
import { Client } from "@notionhq/client";
import cors from "cors";
import dotenv from "dotenv";
dotenv.config
3) SDK 코드를 통해 databaseId → dataSourcesId 가져오기
db_id를 가져왔다면, 아래 코드를 통해 database 의 정보를 가져옵니다
특정 DB에 속하는 datasource들은 배열 형태로 들어오기에
배열을 통해 어떤 이름의 DB가 어떤 sourceId를 가지고 있는지 확인하면 됩니다
const db = await notion.databases.retrieve({
database_id: databaseId,
});
{
object: 'database',
id: '2ceb725c-9d72-80bb-8606-e28c9b7d14ed',
title: [
{
type: 'text',
text: [Object],
annotations: [Object],
plain_text
4) 함수 종류 알아보기
notion.dataSources.retrieve()
- 데이터소스의 메타 정보 조회
- 실제 데이터(row)는 안 나옴
- 스키마(컬럼), 제목, inline 여부 등을 확인하는 용도
const response = await notion.dataSources.retrieve({
data_source_id: DATA_SOURCE_ID,
});
{
"object": "data_source",
"id": "2c6b725c-9d72-8087-aa0e-000bc35fff7f",
"cover": null,
"icon": null,
"created_time": "2025-12-11T14:50:00.000Z",
"created_by": {
"object":
| 필드 | 의미 |
| id | Data Source ID (API 호출에 사용) |
| title | 데이터소스 이름 |
| is_inline | false → 페이지 기반 DB / true → 인라인 DB |
| properties | 컬럼 정의 (title, people 등) |
| parent.database_id | 실제 Notion Database ID |
| database_parent.page_id | 이 DB가 속한 페이지 |
notion.dataSources.query()
이 데이터소스 안에 들어있는 실제 데이터(Page)를 가져올 때 = DB 조회
- DB의 row = page 목록 조회
- 필터, 정렬 가능
const response = await notion.dataSources.query({
data_source_id: DATA_SOURCE_ID,
});
{
"object": "list",
"results": [
{
"object": "page",
"id": "2c6b725c-9d72-8017-a9c1-ee22d48419f5",
"created_time": "2025-12-11T14:57:00.000Z",
"last_edited_time": "2025-12-11T14:57:00.000Z",
| 필드 | 의미 |
| results[] | DB에 들어있는 각 row (= Page) |
| object: "page" | 데이터 한 줄은 항상 Page |
| properties | 컬럼 값들 |
| parent.data_source_id | 이 페이지가 속한 Data Source |
| parent.database_id | 원본 Database ID |
{
object: 'list',
results: [
{
object: 'block',
id: '2ceb725c-9d72-804c-a8af-e20dc72d1c2e',
parent: [Object],
created_time: '2025-12-19T23:25:00.000Z',
last_edited_time:
페이지 자체의 데이터들 조회 (커버이미지, 속성 등등)
const blocks = await notion.pages.retrieve({ page_id: id });
{
object: 'page',
id: '2ceb725c-9d72-80a6-9f28-fb7353e6ba27',
created_time: '2025-12-19T16:02:00.000Z',
last_edited_time: '2025-12-19T23:26:00.000Z',
created_by: { object: 'user', id: '74a382ea-ce4a-40e2-a533-e54bf92430eb' },
last_edited_by
REACT 로 한다면 ..
react 로 진행하게 된다면 아래 라이브러리를 통해 CURL 호출하는 것처럼 호출한다고 합니다.
아마 저는 CORS 에러 등으로 인해 현재 성공하지 못한 것 같아요 ..
노션 API 참고 문서
페이지 DB페이지 DB는 눌렀을 때 뜨는 URL에서 바로 확인할 수 있습니다
auth
:
process
.
env
.
NOTION_KEY
}
)
;
const res = await notion.dataSources.query({
data_source_id: process.env.NOTION_DATABASE_ID,
(
)
;
const notion = new Client({ auth: process.env.NOTION_KEY });
const db = await notion.databases.retrieve({
const DATA_SOURCE_ID = "2ceb725c-9d72-80f7-9cb0-000b57658d78";
app.get("/api/posts", async (req, res) => {
const response = await notion.dataSources.query({
data_source_id: DATA_SOURCE_ID,
const posts = response.results.map((page) => ({
title: page.properties["이름"]?.title[0]?.plain_text || "제목 없음",
res.status(500).json({ error: error.message });
:
'프론트 스터디'
,
plain_text: '한곳에서 문서를 작성하고 협업하세요.',
parent: { type: 'page_id', page_id: '21cb725c-9d72-809e-9bbc-d13f298d72e7' },
created_time: '2025-12-19T14:28:25.695+00:00',
last_edited_time: '2025-12-19T15:55:36.714+00:00',
"user"
,
"id": "74a382ea-ce4a-40e2-a533-e54bf92430eb"
"id": "74a382ea-ce4a-40e2-a533-e54bf92430eb"
"last_edited_time": "2025-12-11T15:24:00.000Z",
"plain_text": "DBSAMPLE",
"database_id": "2c6b725c-9d72-8049-a681-c5c028a427ca"
"page_id": "33cc0dd8-c691-4c25-9f0c-946b32019183"
"url": "https://www.notion.so/2c6b725c9d728049a681c5c028a427ca",
"request_id": "ec1174af-7c22-40c4-9622-c69be1f50a19"
"id": "74a382ea-ce4a-40e2-a533-e54bf92430eb"
"id": "74a382ea-ce4a-40e2-a533-e54bf92430eb"
"type": "data_source_id",
"data_source_id": "2c6b725c-9d72-8087-aa0e-000bc35fff7f",
"database_id": "2c6b725c-9d72-8049-a681-c5c028a427ca"
"id": "74a382ea-ce4a-40e2-a533-e54bf92430eb",
"avatar_url": "https://lh3.googleusercontent.com/a/AATXAJwDM1rlFgNa_rf0dCSujSTB2n8sCT0PK17nTJox=s100",
"email": "sy1110@soongsil.ac.kr"
"url": "https://www.notion.so/2c6b725c9d728017a9c1ee22d48419f5",
"id": "2c6b725c-9d72-8030-a83d-e60056f632a7",
"created_time": "2025-12-11T14:53:00.000Z",
"last_edited_time": "2025-12-11T14:53:00.000Z",
"id": "74a382ea-ce4a-40e2-a533-e54bf92430eb"
"id": "74a382ea-ce4a-40e2-a533-e54bf92430eb"
"type": "data_source_id",
"data_source_id": "2c6b725c-9d72-8087-aa0e-000bc35fff7f",
"database_id": "2c6b725c-9d72-8049-a681-c5c028a427ca"
"url": "https://www.notion.so/2c6b725c9d728030a83de60056f632a7",
"id": "2c6b725c-9d72-80de-842b-e781c619bbcf",
"created_time": "2025-12-11T15:24:00.000Z",
"last_edited_time": "2025-12-11T15:24:00.000Z",
"id": "74a382ea-ce4a-40e2-a533-e54bf92430eb"
"id": "74a382ea-ce4a-40e2-a533-e54bf92430eb"
"type": "data_source_id",
"data_source_id": "2c6b725c-9d72-8087-aa0e-000bc35fff7f",
"database_id": "2c6b725c-9d72-8049-a681-c5c028a427ca"
"url": "https://www.notion.so/new-2c6b725c9d7280de842be781c619bbcf",
"type": "page_or_data_source",
"page_or_data_source": {},
"request_id": "34d45852-6bbb-46cc-b0c8-b701d93423b2"
'2025-12-19T23:25:00.000Z'
,
last_edited_by: [Object],
id: '2ceb725c-9d72-8083-8a52-d705811c98d1',
created_time: '2025-12-19T23:25:00.000Z',
last_edited_time: '2025-12-19T23:26:00.000Z',
last_edited_by: [Object],
id: '2ceb725c-9d72-80cb-9e7c-d718f78772af',
created_time: '2025-12-19T23:26:00.000Z',
last_edited_time: '2025-12-19T23:26:00.000Z',
last_edited_by: [Object],
id: '2ceb725c-9d72-8039-8fb7-e7389b411729',
created_time: '2025-12-19T23:26:00.000Z',
last_edited_time: '2025-12-19T23:26:00.000Z',
last_edited_by: [Object],
id: '2ceb725c-9d72-8044-b1e4-f0a562bf879a',
created_time: '2025-12-19T23:26:00.000Z',
last_edited_time: '2025-12-19T23:26:00.000Z',
last_edited_by: [Object],
id: '2ceb725c-9d72-8050-9320-d85fc870efe1',
created_time: '2025-12-19T23:26:00.000Z',
last_edited_time: '2025-12-19T23:26:00.000Z',
last_edited_by: [Object],
id: '2ceb725c-9d72-80aa-91ac-ca06de64f1bf',
created_time: '2025-12-19T23:26:00.000Z',
last_edited_time: '2025-12-19T23:26:00.000Z',
last_edited_by: [Object],
request_id: '1a28c663-0869-4fe1-aed9-5c707a82e4a1'
:
{
object
:
'user'
,
id
:
'74a382ea-ce4a-40e2-a533-e54bf92430eb'
}
,
data_source_id: '2ceb725c-9d72-80f7-9cb0-000b57658d78',
database_id: '2ceb725c-9d72-8011-9aa4-e98475841653'
properties: { '이름': { id: 'title', type: 'title', title: [Array] } },
url: 'https://www.notion.so/2ceb725c9d7280a69f28fb7353e6ba27',
public_url: 'https://enshrined-indigo-728.notion.site/2ceb725c9d7280a69f28fb7353e6ba27',
request_id: '35ed2be9-9213-4bc0-8ff7-45539d11ba5b'