Notion API 이미지 오류 해결하기

지금 운영하고 있는 블로그 게시글은 직접 구현한 방식이 아닌 notion API를 통해서 notion에서 게시글을 작성하면 notion.ts API를 통해 데이터를 로드하는 방식이였다.

처음엔 직접 구현을 생각했었는데 에디터도 직접 구현해야하고, 이미지 처리도 s3나 clareflare에 올려주거나 아니면 프론트에서 /public 에 게시글을 작성해야하는 번거로움이 있었다.

그래서 최종 notion API를 통해 블로그 게시글 기능을 만들게 되었다.

운영하다보니 한가지 문제가 있었는데 가끔씩 내 블로그에서 게시글을 보다보면 이미지가 깨지는 문제가 있었다.

너무 편하게 쓰고 있었는데 왜 가끔씩만 깨지는 문제가 있었을까.. 하고 찾아보니 notion API가 반환하는 이미지 구조에 문제가 있었다.

https://prod-files-secure.s3.us-west-2.amazonaws.com/...?X-Amz-Expires=3600

AWS S3의 Presigned URL이다. X-Amz-Expires=3600이 보이는가? 1시간 후 만료된다.

Next.js의 unstable_cache로 HTML을 캐싱하고 있었는데, 캐싱된 HTML 안에 이 만료되는 URL이 그대로 들어있으니 이미지 로드가 3600에 한번씩 만료되니 시간이 지나면 이미지가 깨질 수밖에 없었던 구조 였던 것 이다.

S3나 claudeflare 와 같은 곳에 저장을 할까 하다가 해결이 되었는데 해결 방법은 은근 단순했다.

(notion에서 유료 버전을 사용하면 문제가 발생하지 않는다고 한다)

해결 방법은 간단했다.

Notion은 이미지 프록시 서비스를 제공한다.

notion 게시글을 게시를 하게 되면 위와 같은 사이트로 게시글이 게재된다.

노션으로부터 게시글을 올리고, 이미지 처리는

https://www.notion.so/image/{인코딩된 원본 URL}?table=block&id={블록ID}&cache=v2

이 URL로 요청하면 Notion 서버가 원본 이미지를 대신 가져와서 응답한다.

Notion 서버는 S3에 대한 접근 권한이 있으므로 URL 만료와 무관하게 동작한다.

notion-to-md 라이브러리에 커스텀 변환기를 추가해서 이미지 블록을 변환할 때 이 프록시

URL을 사용하도록 한다.

아래는 notion 을 호출하는 로직에 담길 내용이다.

n2m.setCustomTransformer('image', async (block) => {

const { id, image } = block as NotionImageBlock;

const url = image.type === 'external' ? image.external?.url : image.file?.url;

if (!url) return '';

const caption = image.caption?.[0]?.plain_text ?? '';

return ![${caption}](<https://www.notion.so/image/${encodeURIComponent(url)}?table=b>   lock&id=${id}&cache=v2);

});

결과

  • 변경 전: S3 presigned URL → 1시간 후 만료(주기 갱신)
  • 변경 후: Notion 프록시 URL → 영구적

캐싱 기간과 상관없이 이미지가 정상적으로 표시된다.

의견 남기기

이 글에 대한 의견이나 질문이 있으시다면 언제든 연락주세요!