Notion api로 블로그 만들기 image 기간 제한 없이 사용하는 방법
안녕하세요. 오랜만에 글을 쓰네요. 오늘은 이 블로그를 만들면서 하나의 문제였던 image 기간 때문에 블로그 이미지가 사라지는 문제를 해결한 경험을 공유하겠습니다.
원본 코드는 이곳에 있습니다.
Notion Api 응답은 어떻게 올까?
우선 저는 @notionhq/client 이 라이브러리를 사용하여 만들었습니다. 이제 React의 useEffectEvent는 뭘까 ? 글을 기준으로 응답을 살펴보겠습니다.
notion.databases.query 응답
{ "tags": { "id": "%3Ch%60t", "type": "multi_select", "multi_select": [] }, "id": { "id": "DegF", "type": "number", "number": 4 }, "description": { "id": "M%5CZ%5B", "type": "rich_text", "rich_text": [ { "type": "text", "text": { "content": "useEffectEvent의 필요성, 사용 방법, 폴리필까지 한번에 알아보자", "link": null }, "annotations": { "bold": false, "italic": false, "strikethrough": false, "underline": false, "code": false, "color": "default" }, "plain_text": "useEffectEvent의 필요성, 사용 방법, 폴리필까지 한번에 알아보자", "href": null } ] }, "publish": { "id": "M_%3E%60", "type": "checkbox", "checkbox": true }, "thumbnail": { "id": "pWE%60", "type": "files", "files": [ { "name": "thumbnail-maker_202537.jpg", "type": "file", "file": { "url": "https://prod-files-secure.s3.us-west-2.amazonaws.com/0cde9f09-34e9-4761-9262-2c7efcbe4b59/993873e6-77db-4795-9890-686743b5c04d/thumbnail-maker_202537.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIAZI2LB466Z775I5YQ%2F20250603%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20250603T050127Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEDUaCXVzLXdlc3QtMiJGMEQCIBlp2maakipYfpUM9GC%2BImqQsuXTr1lLnUAbdq9S6sqaAiA8BT8cgpIrpjiuSX9cT%2B%2B3MOGBqwl%2B0FOkiIidQbGAtiqIBAj%2B%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8BEAAaDDYzNzQyMzE4MzgwNSIMC50wq%2F4J29FabnCwKtwDKr5pSHoVb01pkAHFpjMKVAKm2wK4qFsUNGRXmOWAelkeqW8mmoFopZyKBe1iYZzQ8tTDyWA%2Fh6VPBfOnS5t9y05BYR1DhjTV3KmhylVNnFZwVa14A5lgGzPuwtHj0O3SYA4fmJPwPmLr06BzjnCzzel9GTY3bzf%2BMPDX9Wehmfs6Z1YzF%2B3FFR7NyJj4scAr6mTG3xoQEdjZ6HMKvTFsNCmBK40KAZ3PnUeHgC8HQN%2FyCf1Ukpwoq4wPhAvOo7DKJOJwabfNH4wsjmCmaY%2FBo4KfWJd6mg%2BxbdzSbAhtXGR%2BGCVHN6QqE0ECcoaP6Bwqr%2B0MZ2oBFSoms%2FTP0jCSFTF4xBl%2BK2ua22tDrczfzAx4rzHvNDWw0XOn0OUIn6iWjkkdw4z8RhALzIGNhRMG7qNmJKp73qkI6WcwhOF1kPmiyOdoToJ%2FvWB8R0WwR81zXZfEiQukOZ4CK%2FKd7w5CytPzSo4psjXHVigk74nx4xiOsE6UbjkBFbPQtTavOMGiKtsXrCFLzOgPDJhx1su4Up1VtFHPegyJRZ8rEF1BgfCHPsIRsF0QDp4QEsOeGUdL9j0tNSxlic3ls2toWVk7u2EP8obJUwU0%2BiAZWIJWDTHeln0ReuHj4IAmf0owroH6wQY6pgEi%2FVhezj6VtAKYc1my3nTkx7rL08RWwTRwW73hl5kSCzZzU0ofzWx4G%2FwZcnL9%2FofUxDJxiJpwy7mSDlagYLwsMNx62bkMYF9YqBXxynHS0tVLStnMDXnuIdgObMXlV0ozgXhQYaVnFW5IpxRf6or3HcWcREBMsj%2FxoRL4RfEMy8tbWy3c7oHtzB%2FkQTyhYy4LSGFStQilsGMPjg3rU3mbQFGuTz1X&X-Amz-Signature=16a090a38992071538513e0c354d58d53cce51a93af9aaf89222c6c22acd07a2&X-Amz-SignedHeaders=host&x-id=GetObject", "expiry_time": "2025-06-03T06:01:27.952Z" } } ] }, "publishDate": { "id": "sZx%3B", "type": "date", "date": { "start": "2025-03-07", "end": null, "time_zone": null } }, "title": { "id": "title", "type": "title", "title": [ { "type": "text", "text": { "content": "React의 useEffectEvent는 뭘까 ?", "link": null }, "annotations": { "bold": false, "italic": false, "strikethrough": false, "underline": false, "code": false, "color": "default" }, "plain_text": "React의 useEffectEvent는 뭘까 ?", "href": null } ] } }
응답이 오는 것을 확인할 수 있습니다.
"thumbnail": { "id": "pWE%60", "type": "files", "files": [ { "name": "thumbnail-maker_202537.jpg", "type": "file", "file": { "url": "https://prod-files-secure.s3.us-west-2.amazonaws.com/0cde9f09-34e9-4761-9262-2c7efcbe4b59/993873e6-77db-4795-9890-686743b5c04d/thumbnail-maker_202537.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=ASIAZI2LB466Z775I5YQ%2F20250603%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20250603T050127Z&X-Amz-Expires=3600&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEDUaCXVzLXdlc3QtMiJGMEQCIBlp2maakipYfpUM9GC%2BImqQsuXTr1lLnUAbdq9S6sqaAiA8BT8cgpIrpjiuSX9cT%2B%2B3MOGBqwl%2B0FOkiIidQbGAtiqIBAj%2B%2F%2F%2F%2F%2F%2F%2F%2F%2F%2F8BEAAaDDYzNzQyMzE4MzgwNSIMC50wq%2F4J29FabnCwKtwDKr5pSHoVb01pkAHFpjMKVAKm2wK4qFsUNGRXmOWAelkeqW8mmoFopZyKBe1iYZzQ8tTDyWA%2Fh6VPBfOnS5t9y05BYR1DhjTV3KmhylVNnFZwVa14A5lgGzPuwtHj0O3SYA4fmJPwPmLr06BzjnCzzel9GTY3bzf%2BMPDX9Wehmfs6Z1YzF%2B3FFR7NyJj4scAr6mTG3xoQEdjZ6HMKvTFsNCmBK40KAZ3PnUeHgC8HQN%2FyCf1Ukpwoq4wPhAvOo7DKJOJwabfNH4wsjmCmaY%2FBo4KfWJd6mg%2BxbdzSbAhtXGR%2BGCVHN6QqE0ECcoaP6Bwqr%2B0MZ2oBFSoms%2FTP0jCSFTF4xBl%2BK2ua22tDrczfzAx4rzHvNDWw0XOn0OUIn6iWjkkdw4z8RhALzIGNhRMG7qNmJKp73qkI6WcwhOF1kPmiyOdoToJ%2FvWB8R0WwR81zXZfEiQukOZ4CK%2FKd7w5CytPzSo4psjXHVigk74nx4xiOsE6UbjkBFbPQtTavOMGiKtsXrCFLzOgPDJhx1su4Up1VtFHPegyJRZ8rEF1BgfCHPsIRsF0QDp4QEsOeGUdL9j0tNSxlic3ls2toWVk7u2EP8obJUwU0%2BiAZWIJWDTHeln0ReuHj4IAmf0owroH6wQY6pgEi%2FVhezj6VtAKYc1my3nTkx7rL08RWwTRwW73hl5kSCzZzU0ofzWx4G%2FwZcnL9%2FofUxDJxiJpwy7mSDlagYLwsMNx62bkMYF9YqBXxynHS0tVLStnMDXnuIdgObMXlV0ozgXhQYaVnFW5IpxRf6or3HcWcREBMsj%2FxoRL4RfEMy8tbWy3c7oHtzB%2FkQTyhYy4LSGFStQilsGMPjg3rU3mbQFGuTz1X&X-Amz-Signature=16a090a38992071538513e0c354d58d53cce51a93af9aaf89222c6c22acd07a2&X-Amz-SignedHeaders=host&x-id=GetObject", "expiry_time": "2025-06-03T06:01:27.952Z" } } ] },
이 응답에 주목해 보겠습니다.
expiry_time값이 있는데요. notion은 자체 s3에 expiry_time 시간 동안만 asset을 저장합니다. 저는 블로그를 SSG로 빌드를 해서 배포하기 때문에 저 expiry_time가 만료되면 더 이상 이미지를 불러올 수 없겠죠.
notion-to-md 응답
이번에는 notion-to-md의 pageToMarkdown 응답 값을 살펴보겠습니다.
{ "type": "image", "blockId": "1afa463e-8303-8081-87f1-f5d413c0c3b7", "parent": "", "children": [] }
이런 식으로 응답이 오는 것을 확인할 수 있습니다.
publish 기능을 활용하여 image 가져오기
Notion 페이지를 publish를 하면 이런 식으로 https://nimble-monkey-1a1.notion.site/React-useEffectEvent-1afa463e830380b59415c0a2b503a729 url이 나옵니다.
이제 우리는 https://nimble-monkey-1a1.notion.site base url을 얻었습니다. 이제 image src로 들어온 https://prod-files-secure.s3.us-west-2.amazonaws.com/0cde9f09-34e9-4761-9262-2c7efcbe4b59/bb8a9cc…. 값을 수정하여 publish page에서 이미지를 가져오는 함수를 가져오겠습니다.
const NOTION_PUBLIC_URL = "https://nimble-monkey-1a1.notion.site"; interface CreateNotionImageUrlParams { fileUrl: string; id: string; width?: number; } export function createNotionImageUrl({ fileUrl, id, width, }: CreateNotionImageUrlParams): string { const encodedUrl = encodeURIComponent(fileUrl.split("?")[0]); return `${NOTION_PUBLIC_URL}/image/${encodedUrl}?table=block&id=${id}&cache=v2&width=${width}`; }
이제 Image를 사용하는 부분에서
return ( <Image {...rest} className={css({ display: "block", margin: "0 auto", marginBottom: SPACE.sm, })} alt={alt || ""} src={createNotionImageUrl({ fileUrl: src || "", width: IMAGE_SIZE, id: blockId, })} width={IMAGE_SIZE} height={IMAGE_SIZE} /> ); }
이런 식으로 사용할 수 있습니다.