Notion CMS 구축 시 발생하는 'databases.query' 함수 누락 이슈와 V5 아키텍처 대응 전략
1. 서론: 왜 Notion을 CMS로 선택했는가?
Next.js 14(App Router) 환경에서 개인 포트폴리오 및 실험실(Lab) 페이지를 구축하며, 콘텐츠 관리 시스템(CMS)으로 Notion을 채택했습니다. Notion은 직관적인 UI를 제공하며, 별도의 백엔드 어드민 페이지를 구축할 필요 없이 API를 통해 데이터를 불러올 수 있다는 장점이 있기 때문입니다.
하지만, 시중에 나와 있는 표준 문서(Official Docs)와 강의를 그대로 적용하는 과정에서 예상치 못한 치명적인 런타임 에러에 직면했습니다. 이 글은 특정 Notion API 클라이언트 환경(Strict Mode/Enterprise V5)에서 발생하는 이슈와 그 해결 과정을 상세히 기술합니다.
2. 문제 상황 (The Problem)
2.1. 표준 구현 방식
일반적인 notionhq/client 라이브러리 사용법은 다음과 같습니다.
// 일반적인 접근 방식 (표준 문서)
const response = await notion.databases.query({
database_id: process.env.NOTION_DATABASE_ID,
filter: { ... },
sorts: [ ... ],
});2.2. 발생한 에러
위 코드를 실행했을 때, 다음과 같은 TypeError가 발생하며 서버가 중단되었습니다.
2.3. 1차 분석
처음에는 단순한 환경 변수(Environment Variable) 누락이나 API Key 권한 문제로 의심했으나, 디버깅 결과는 달랐습니다.
이는 제가 사용 중인 Notion Integration 환경이 표준 퍼블릭 API가 아닌, 엄격한 타입(Strict Notion Client) 혹은 데이터 소스(Data Source) 기반의 V5 아키텍처를 따르고 있음을 시사했습니다.
3. 원인 분석 (Root Cause Analysis)
심층 디버깅 결과, 해당 환경에서는 데이터베이스에 접근하는 방식이 근본적으로 달랐습니다.
즉, **"데이터베이스에 쿼리를 날려라"**라는 명령어(databases.query)가 성립하지 않으며, **"데이터베이스에서 데이터 소스 ID를 찾고 → 그 소스에 쿼리를 날려라"**라는 2단계 프로세스가 필요했습니다.
4. 해결 솔루션 (The Solution)
문제를 해결하기 위해 두 단계의 비동기 로직을 수행하는 **Wrapper Function(헬퍼 함수)**을 구현했습니다.
4.1. 프로세스 설계
4.2. TypeScript 구현 코드
이 로직을 모든 페이지(블로그, 프로젝트, 실험실)에서 재사용할 수 있도록 queryV5Database라는 유틸리티 함수로 추상화했습니다.
import { Client } from '@notionhq/client';
// 1. Strict Mode 클라이언트 타입 재정의 (표준 SDK에 없는 메서드 선언)
interface StrictNotionClient {
databases: {
retrieve: (args: { database_id: string }) => Promise<{
id: string;
data_sources?: { id: string }[]
}>;
};
dataSources: {
query: (args: {
data_source_id: string;
filter?: object;
sorts?: object;
}) => Promise<any>; // CustomQueryResponse
};
}
const notion = new Client({ auth: process.env.NOTION_API_KEY });
const strictNotion = notion as unknown as StrictNotionClient;
/**
* V5 호환성 해결을 위한 쿼리 래퍼 함수
* databases.query 대신 retrieve -> dataSources.query 패턴을 사용
*/
async function queryV5Database(databaseId: string, sorts?: object[], filter?: object) {
// Step 1: DB 정보 조회 및 Data Source ID 추출
const db = await strictNotion.databases.retrieve({ database_id: databaseId });
const dataSourceId = db.data_sources?.[0]?.id;
if (!dataSourceId) {
throw new Error(`[Fatal] Data Source ID not found for DB: ${databaseId}`);
}
// Step 2: 실제 데이터 쿼리 실행
return await strictNotion.dataSources.query({
data_source_id: dataSourceId,
sorts,
filter,
});
}5. 적용 결과 (Result)
이 헬퍼 함수를 적용한 후, 기존에 실패하던 모든 데이터 페칭 로직이 정상화되었습니다.
Before (Error)
TypeError: strictNotion.databases.query is not a functionAfter (Success)
[Next.js] Compiling /lab/bdo-crafting ...
[Notion] Successfully retrieved 128 items from Data Source.6. 결론 및 인사이트
Notion API는 매우 강력하지만, 모든 환경이 동일하지 않습니다. 특히 튜토리얼이나 공식 문서만 의존하다 보면, 이와 같은 환경 특이적인(Environment-specific) 아키텍처 이슈에서 며칠을 허비할 수 있습니다.
핵심 요약:
이 글이 저와 같은 에러(strictNotion...)로 고통받는 개발자들에게 명확한 이정표가 되기를 바랍니다.