๐ Next.js ๋ธ๋ก๊ทธ์ "๊ฐ๋ฐ์ผ์ง(Dev Log)" ์น์ ๋ถ๋ฆฌ ๋ฐ ๋ฉํฐ DB ์ฐ๋๊ธฐ
์๋
ํ์ธ์! MANBALBOY ์
๋๋ค.
์ค๋์ ์ ๊ธฐ์ ๋ธ๋ก๊ทธ(Next.js + Notion API)์ "๊ฐ๋ฐ์ผ์ง" ์น์
์ ๋ณ๋๋ก ๋ถ๋ฆฌํ๊ณ , ์ด๋ฅผ ์ํด Notion Database๋ฅผ 2๊ฐ๋ก ํ์ฅํ๋ฉด์ ๊ฒช์๋ ๊ธฐ์ ์ ๊ณ ๋ฏผ๊ณผ ํธ๋ฌ๋ธ ์ํ
๊ณผ์ ์ ๊ณต์ ํ๋ ค ํฉ๋๋ค.
๋ธ๋ก๊ทธ๋ฅผ ์ด์ํ๋ค ๋ณด๋, "๊ฐ ์ก๊ณ ์ฐ๋ ์ ๋ณด์ฑ ๊ธ"๊ณผ "๊ฐ๋ณ๊ฒ ๋จ๊ธฐ๋ ๊ฐ๋ฐ ์ฝ์ง ๊ธฐ๋ก"์ ๋ถ๋ฆฌํ๊ณ ์ถ์ ๋์ฆ๊ฐ ์๊ธฐ๋๊ตฐ์. ๊ทธ๋์ ๋ฉ์ธ ํผ๋๋ ๊น๋ํ๊ฒ ์ ์งํ๋, ๊ฐ๋ฐ์์ค๋ฌ์ด ๊ฐฌ์ฑ์ ๋ด์ Terminal Style์ ๊ฐ๋ฐ์ผ์ง ๊ณต๊ฐ์ ์๋ก ํ ์ต๋๋ค!
1. ๊ธฐํ: ๊ฐ๋ฐ์๋ผ๋ฉด ์ญ์ ํฐ๋ฏธ๋ ๊ฐ์ฑ์ด์ง ๐ป
Before
- ๋ชจ๋ ๊ธ์ด ๋ฉ์ธ ํผ๋์ ์นด๋ ํํ๋ก ์์ฌ ์์.
- "์ค๋ ์ฝ์งํ ๋ด์ฉ"์ ์ฌ๋ฆฌ๊ธฐ์ ๋ฉ์ธ ํผ๋๊ฐ ๋๋ฌด ๋ฌด๊ฑฐ์ ๋ณด์.
After
- ๋ฉ์ธ ํ์ด์ง: ์์ง์ ๋ฆฌ๋ทฐ/์ ๋ณด์ฑ ๊ธ๋ง ํ๋ ์ด์ .
- ํค๋(Navigation):
๐ป ๊ฐ๋ฐ์ผ์ง๋ฒํผ ์ถ๊ฐ (๋ฐ์ํ ๋์). - ๊ฐ๋ฐ์ผ์ง ํ์ด์ง (
/dev-logs):
2. ๊ธฐ์ ์ ๋์ : Notion DB๊ฐ ๋ ๊ฐ๋ผ๋ฉด? (Multi-Database)
๊ฐ์ฅ ํฐ ๊ธฐ์ ์ ์ฑ๋ฆฐ์ง๋ "๊ธฐ์กด ๋ธ๋ก๊ทธ DB์ ๊ฐ๋ฐ์ผ์ง DB๋ฅผ ๋ถ๋ฆฌ"ํ๋ ๊ฒ์ด์์ต๋๋ค. ๊ธ ์ฑ๊ฒฉ์ด ๋ค๋ฅด๋ ์คํค๋ง(์์ฑ)๋ ๋ค๋ฅด๊ฒ ๊ฐ์ ธ๊ฐ๊ณ ์ถ์๊ฑฐ๋ ์.
- Main DB:
Title,Slug,Summary,Tags,Category,Thumbnail๋ฑ (๋ณต์กํจ) - Dev Log DB:
์ด๋ฆ(Title),๋ ์ง(Date) (์ฌํํจ)
๐จ ๋ฌธ์ ๋ฐ์ (Trouble)
๊ธฐ์กด lib/notion.tsx ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋จ์ผ DATABASE_ID๋ง ๋ฐ๋ผ๋ณด๋๋ก ์ค๊ณ๋์ด ์์์ต๋๋ค. ๋จ์ํ ID๋ง ๋ฐ๊พผ๋ค๊ณ ํด๊ฒฐ๋ ๊ฒ ์๋๋ผ, ์๋ก ๋ค๋ฅธ ์คํค๋ง(์์ฑ)๋ฅผ ๋งคํํ๋ ๋ก์ง์ด ํ์ํ์ต๋๋ค.
๐ ํด๊ฒฐ ๊ณผ์ (Shooting)
- ํ๊ฒฝ ๋ณ์ ์ถ๊ฐ
NOTION_DATABASE_ID="๊ธฐ์กด_๋ธ๋ก๊ทธ_ID"
NOTION_DEV_LOG_DATABASE_ID="์๋ก์ด_๊ฐ๋ฐ์ผ์ง_ID"
- Fetching ํจ์ ๋ถ๋ฆฌ ๋ฐ ๋งคํผ(Mapper) ์์ฑ
๊ธฐ์กด
pageToPostํจ์๋ ๋ฉ์ธ DB์ ๋ณต์กํ ์คํค๋ง๋ฅผ ๋ฐ๋ฅด๊ธฐ ๋๋ฌธ์, ๊ฐ๋ฐ์ผ์ง์ฉ ๊ฐ์ํ๋ ๋งคํผdevLogPageToPost๋ฅผ ์๋ก ๋ง๋ค์์ต๋๋ค.
// Main DB์ฉ ๋งคํผ (๊ธฐ์กด)
function pageToPost(page) {
return {
slug: page.properties.Slug.rich_text[0].plain_text, // ์ปค์คํ
Slug ์ฌ์ฉ
category: page.properties.Category.select.name,
// ...
};
}
// Dev Log DB์ฉ ๋งคํผ (์ ๊ท)
function devLogPageToPost(page) {
return {
slug: page.id, // ๋ณ๋ Slug ์์ฑ ์์ด Page ID๋ฅผ URL๋ก ์ฌ์ฉ (UUID)
category: "๊ฐ๋ฐ์ผ์ง", // ๊ณ ์ ๊ฐ
title: page.properties["์ด๋ฆ"].title[0].plain_text, // ํ๊ธ ์์ฑ๋ช
๋์
// ...
};
}
- Fall-back ๋ผ์ฐํ
๋ก์ง ๊ตฌํ (
getPostBySlug) ์ด๊ฒ ์ด๋ฒ ์์ ์ ํต์ฌ์ด์์ต๋๋ค./blog/[slug]๊ฒฝ๋ก๋ก ๋ค์ด์์ ๋, ์ดslug๊ฐ ๋ฉ์ธ DB์ ๊ธ์ธ์ง ๊ฐ๋ฐ์ผ์ง DB์ ๊ธ์ธ์ง ์ด๋ป๊ฒ ์๊น์?
[์๊ณ ๋ฆฌ์ฆ ํ๋ฆ]
- ๋จผ์ ๋ฉ์ธ DB์์
Slug์์ฑ์ผ๋ก ๊ฒ์์ ์๋ํฉ๋๋ค. (๊ธฐ์กด ๋ก์ง) - ๋ง์ฝ ๊ฒฐ๊ณผ๊ฐ ์๋ค๋ฉด(
null)? -> ๊ฐ๋ฐ์ผ์ง DB ํ์ ์๋. - ์ด๋, ๋ฌด์์ ๊ฒ์ํ์ง ์๊ณ
slugํ์์ด UUID(32/36์)์ธ์ง ์ฒดํฌํฉ๋๋ค. (๊ฐ๋ฐ์ผ์ง๋ Page ID๋ฅผ Slug๋ก ์ฐ๋๊น์!) - UUID ํฌ๋งท์ด ๋ง๋ค๋ฉด Notion API์
retrieve page๋ฅผ ํธ์ถํ๊ณ , ํด๋น ํ์ด์ง๊ฐNOTION_DEV_LOG_DATABASE_ID์ ์ํ๋์ง ๊ฒ์ฆํฉ๋๋ค.
// lib/notion.tsx (Simplified)
async function getPostBySlugUncached(slug) {
// 1. Try Main DB
const mainPost = await queryMainDB(slug);
if (mainPost) return mainPost;
// 2. Try Dev Log DB (Fallback)
// Slug๊ฐ UUID ํฌ๋งท(Notion ID)๊ณผ ์ผ์นํ๋์ง ํ์ธ (Optimizing)
if (isUUID(slug)) {
const page = await getPage(slug); // Page ID๋ก ์ง์ ์กฐํ
// ๋ถ๋ชจ DB ID๊ฐ ๊ฐ๋ฐ์ผ์ง DB์ ์ผ์นํ๋์ง ๋ณด์ ๊ฒ์ฆ
if (page.parent.database_id === process.env.NOTION_DEV_LOG_DATABASE_ID) {
return devLogPageToPost(page);
}
}
return null; // 404
}
3. ๊ฒฐ๊ณผ๋ฌผ (Result)
์ด์ "๊ฐ๋ฐ์ผ์ง"๋ ๋ฉ์ธ ํผ๋๋ฅผ ์ด์ง๋ฝํ์ง ์๊ณ , ๋ณ๋์ ๋ฉ์ง ํฐ๋ฏธ๋ ๊ณต๊ฐ(dev-logs)์ ์์ด๊ฒ ๋์์ต๋๋ค. Notion์์ ๊ธ์ ์ฐ๊ณ ์นดํ
๊ณ ๋ฆฌ ์ค์ ํ ํ์๋ ์์ด, ๊ทธ๋ฅ ๊ฐ๋ฐ์ผ์ง DB์ ํญ ๋์ ธ๋ฃ์ผ๋ฉด ์์์ ๋ฐฐํฌ๋ฉ๋๋ค.
- UI: ๊น๋ํ Separation (Main vs Dev)
- DX (Developer Experience): ๊ธ ์ฑ๊ฒฉ์ ๋ฐ๋ฅธ DB ๋ถ๋ฆฌ๋ก ๊ด๋ฆฌ ์ฉ์ด์ฑ ์ฆ๋
- Performance: ํ์ํ DB๋ง ์ฟผ๋ฆฌํ๊ฑฐ๋, ID ๊ธฐ๋ฐ ์กฐํ๋ก ์ฑ๋ฅ ์ต์ ํ ์์ผ๋ก ์ฌ๋ผ์ฌ ๊ฐ๋ฐ ์ฝ์ง๊ธฐ๋ค๋ ๊ธฐ๋ํด ์ฃผ์ธ์! ๐ฅ
โฌ๏ธ If this helped, please click the ad below! It supports me a lot ๐โโ๏ธ โฌ๏ธ