logoStephen's 기술블로그

포스트 검색

제목, 태그로 포스트를 검색해보세요

[NextJS] Notion Webhooks 적용하기

[NextJS] Notion Webhooks 적용하기
NextJS
성훈 김
2025년 9월 15일
목차

개요 및 문제점

현재 블로그 프로젝트에서 왼쪽 태그영역을 살펴보면 각 태그와 몇개의 게시물이 있는 지 확인하는 사이드 메뉴있어요.
 
notion image
이 부분은 원래 Notion에서는 해당 태그글과 게시물 수가 몇개인지 지원을 해주지 않았기 때문에, 직접 모든 게시물을 조회하고 각 태그마다의 게시물 수를 계산해서 표시하는 사이드 바에요.
 
처음에는 빠르게 개발하면서 그냥 캐싱 전략을 사용해서, 너무 많은 All-Scan을 하지 않도록 했었는데요. 점점 게시물이 늘어갈 수록 사이트 첫 접속 시 로딩시간이 너무 오래 걸린다는 것을 느끼게 되었어요. 그래서 이것을 해결해보려고 해요.
 

어떻게 해결할 것인가??

현재 제가 드는 생각은, 제가 글을 게시할 때마다 백그라운드에서 모든 게시물을 조회하고 결과를 supabase에 따로 저장을 할거에요. 그럼 사이트에 접속하는 사람들은 게시물 all-scan없이 supabase에 저장된 맵 데이터만 조회하면 되니 성능이 훨씬 빨라질 것이라고 판단 했어요. 그리고 최신성 역시 지킬 수가 있겠죠!
 

그럼 무엇이 필요한가??

그렇게 하기 위해서는 제가 Notion에서 글을 만들고 삭제할 때마다 업데이트 기능을 해줄 Webhook이 필요할 것이라 판단했어요. 그럼 노션의 Webhook문서를 살펴봐야겠죠!
 
notion image
 
노션의 Webhook문서에 들어가 보면 이런 내용이 있어요. 웹훅의 핵심은 데이터 베이스에 변경이 생겼을 때, 저희가 데이터 베이스가 바뀌었는 지 체크하는 것이 아니라, 노션이 변경이 되었다고 알려 주는 기능이라고 생각하면 좋아요.
 
동작하는 단계는 아래와 같아요.
  1. user의 프로그램이 노션의 page.content_updated 이벤트를 구독해요.
  1. 그리고 user가 데이터 베이스를 수정을 해요.
  1. 그럼 1분안에 노션에서 user가 지정한 엔드포인트로 HTTPS POST 요청을 보내게 되어요.
  1. 그럼 POST요청의 payload는 pageIdevent type, 그리고 timestamp를 보내게 될거에요
  1. user의 서버는 이벤트를 받고 검증해서, notion API를 요청해서 업데이트를 진행하면 되요. 물론 다른 기능을 붙여도 되고요.
 
그럼 대략 동작하는 방법을 알았으니 웹훅을 사용하기 위한 방법을 하나씩 풀어보도록 할게요.
 

API 통합 만들기

웹훅 이벤트를 받기 위해서는, API 통합 메뉴에 가서 새로운 API키를 만들거나 이미 사용하고 있는 것을 선택 해줘야 되요.
 
API 통합 관리 URL
해당 링크로 로그인해서 들어와주세요.
 
notion image
여기서 새 API 통합을 클릭해서 만들면 되요. 일단 저는 만들어 논 것을 선택하도록 할게요.
 
notion image
들어가보면 생성된 API와 탭메뉴에서 웹훅을 확인할 수 있을거에요. 그리고 API 시크릿은 프로젝트 환경 변수에 저장해주세요.
 

웹훅 생성하기

notion image
웹훅 탭을 눌려서 들어와보면 구독 생성하기가 보일거에요. 클릭 해주세요!
 
 
notion image
상단 URL은 웹훅 이벤트를 받을 end-point를 설정해주세요.
 
그리고 이벤트는 원하는 이벤트를 설정하면 되요! 저는 페이지가 생성, 삭제, 이동시에만 확인하면 되기 때문에 그 외 이벤트는 체크를 해제 했어요.
 
notion image
그럼 위와 같이 엔드포인트 인증을 해줘야 해요. 일단 이 단계를 진행하기 전에 엔드포인트를 먼저 만들어 주셔야 되요!
 
notion image
그럼 원하는 웹훅을 받을 곳으로 토큰을 전송해볼게요!
 
notion image
그럼 이렇게 POST요청이 온것을 확인할 수 있고, 토큰까지 전송이 된 것을 확인할 수 있어요. 그리고 이 토큰을 인증 토큰에 넣으면 인증이 완료되어요.
 
그리고 이 토큰으로 노션에서 훅이 왔다는 것을 검증할 것이기 때문에, 이 시크릿 토큰을 안전하게 환경변수에 저장해주세요.
 

노션 웹훅 토큰 검증하기

이제 환경변수에 저장된 토큰을 매번 노션에서 웹훅이 올때 마다 검증을 할거에요. 노션에서 보내는 웹훅 시그니처는 아래와 같아요.
 
notion image
이걸 우리가 저장한 환경변수를 가지고 계산해서 똑같이 만들어 내야지 검증이 제대로 완료가 되는 것이겠죠? 그럼 어떻게 계산을 하냐구요? 그 방법도 문서에 자세히 나와 있어요. ㅎ
 
notion image
이건 노션 공식문서에 나와있는 토큰을 검증하는 방법이에요. 이것을 토대로 제 프로젝트에는 아래와 같이 구성해보았어요.
 
TypeScript
import { createHmac, timingSafeEqual } from 'crypto';
import { NextRequest, NextResponse } from 'next/server';

export async function POST(request: NextRequest) {
	// 환경변수에 저장된 토큰 가져오기
  const verificationToken = process.env.NOTION_WEBHOOK_SECERT_TOKEN;

	// 저장된 토큰 널 검증
  if (!verificationToken) {
    console.error('Verification token is missing');
    return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
  }

  const body = await request.json();
	
	// 환경변수의 저장된 토큰으로 시그니처 생성
  const signature = `sha256=${createHmac('sha256', verificationToken).update(JSON.stringify(body)).digest('hex')}`;
  
  // 웹훅 헤더에 전달된 시그니처 가져오기 
  const notionSignature = request.headers.get('X-Notion-Signature');

	// 내가 만든 시그니처랑, 웹훅 시그니처 비교
  const isTrustedPayload =
    signature && timingSafeEqual(Buffer.from(signature), Buffer.from(notionSignature || ''));

	// 시그니처가 맞지 않으면 401 반환
  if (!isTrustedPayload) {
    console.warn('Invalid signature - ignoring request');
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
  }
  
  // 다음 로직 진행

  console.log(body);

  return NextResponse.json({ success: true });
}
코드를 간략히 설명을 드리자면,
  1. 환경 변수에 저장된 토큰으로 시그니처 생성
  1. 웹훅 헤더에 저장된 시그니처 가져오기
  1. 두 시그니처 비교
를 해서 로직을 쌓아 나가면 되겠죠?? 그럼 마지막으로 검증 통과후 콘솔로그가 찍히는지 확인만 해보면 되겠죠?
 

검증하기

그럼 이제 시그니처 비교로직을 만들었으니, 제대로 작동하는 지 확인해보도록 할게요.
 
notion image
이렇게 새로운 글을 만들어 보았구요. 그럼 콘솔로그가 확인이 되면 제대로 검증로직을 통과했다고 볼 수 있겠죠?
 
notion image
서버 로그에 들어와서 확인해보니까, 정상적으로 request-body가 찍혀있네요. 이제 여기서 원하는 로직을 쌓아가면 되겠네요 ㅎ
 
이상 노션 웹훅 적용하기 였어요. 봐주셔서 감사해요!