Next.js ์บ์ฑ์ผ๋ก ์น ์๋ฒ ์ฑ๋ฅ ์ต์ ํ
Next.js ์บ์ฑ์ผ๋ก ์น ์๋ฒ ์ฑ๋ฅ ์ต์ ํ ๊ด๋ จ
์ฌํด ์ด ์ ํฌ ํ์์๋ ์ ๊ท ์ปค๋จธ์ค ์๋น์ค๋ฅผ ์คํํ์ต๋๋ค. ์๋ฌด๋๋ ์ปค๋จธ์ค์์๋ SEO๊ฐ ์ค์ํ๋ค๋ณด๋ Next.js
์ App Router๋ฅผ ์ฌ์ฉํด์ SSR(Server Side Rendering)์ ๊ตฌ์ฑํ๋๋ฐ์.
์คํ์ ๋ถํํ
์คํธ ๊ณผ์ ์์ ์์๋ณด๋ค ๋ฎ์ TPS(Transaction Per Second)๊ฐ ํ์ธ๋์ด ๋นํฉํ์์ต๋๋ค. ์ด ๊ธ์์๋ ์ ํฌ ํ์ด Next.js
์ Full Route Cache๋ฅผ ์ ๊ทน ํ์ฉํจ์ผ๋ก์จ, ์๋ฒ ๋ ๋๋ง์ ์ฌ์ฉ๋๋ CPU ๋ฆฌ์์ค๋ฅผ ์ต์ํํ ๊ฒฝํ์ ์๊ฐํฉ๋๋ค.
์น ์๋ฒ์ ์ฑ๋ฅ์ ์ ์ค์ํ ๊น์?
์ถ๊ตฌ๊ฒฝ๊ธฐ์์ ์ข์ ํจ์ค๋ฅผ ๋ฐ์ ๊ณต๊ฒฉ์๊ฐ ์์ ๋๋ฆฌ๊ฒ ํ๋ ๋ฐ๋์ ๊ณจ์ ๋์น๋ ๊ฒฝ์ฐ๊ฐ ์ข ์ข ์๋๋ฐ์. ๋ง์ฐฌ๊ฐ์ง๋ก ์น ํ์ด์ง์์๋ ์ข์ ์ปจํ ์ธ , ์ข์ ์ํ์ ๋๋ฆฌ๊ฒ ๋ก๋ฉํ๋ ๋ฐ๋์ ๊ณ ๊ฐ์ ๋์น๋ ์ฌ๋ก๋ฅผ ์ฝ๊ฒ ํ์ธํ ์ ์์ต๋๋ค.
- Pinterest๋ ๋ก๋ฉ์๋๋ฅผ 40% ๊ฐ์ ํ ํ, ํธ๋ํฝ์ด 15% ์ฆ๊ฐํ๊ณ ํ์๊ฐ์
์ด 15% ์ฆ๊ฐํ์ต๋๋ค. (
pinterest-engineering
) - Amazon์ ๋ก๋ฉ์๋๊ฐ 1์ด ๋นจ๋ผ์ง๋ฉด, ๋งค์ถ์ด 68์ต๋ฌ๋ฌ๊ฐ ์ฆ๊ฐํ๋ค๊ณ ๋ฐํํ์ต๋๋ค.
- ์จ๋ผ์ธ ์ผํ๋ชฐ์ ๋น ๋ฅธ ๋ก๋ฉ์๋๋ ์ฌ์ดํธ์ ์ ๋ขฐ๋๊น์ง ํฅ์ ์ํจ๋ค๋ ํต๊ณ๋ ์์ต๋๋ค.
์น ์๋ฒ์ ์ฑ๋ฅ์ ๋น์ฐํ๊ฒ๋ ์นํ์ด์ง์ ๋ก๋ฉ์๋๋ฅผ ๊ฒฐ์ ํ๋ ์ค์ํ ์์ ์ค ํ๋์ ๋๋ค. ์น ํ์ด์ง ์์ฒญ ๊ณผ์ ์ ํํํ ์๋ ๋ค์ด์ด๊ทธ๋จ์์ ๋ ธ๋์ ๋ถ๋ถ, ์ฆ TTFB(Time to First Byte)์ ํด๋นํ๋ ์์ญ์ด ์น ์๋ฒ์ ์ฑ๋ฅ์ผ๋ก๋ถํฐ ์ํฅ์ ๋ฐ์ต๋๋ค.
์น๋ฆฌ๋ฅผ ํ๋ ๋ฐ์ (์ด์ต์ ๋ด๋ ๋ฐ์) ๊ณจ์ ์ ๋ฃ๋ ๊ฒ๋งํผ์ด๋ ์ค์ํ ๊ฒ ๊ณจ์ ๋ ๋จนํ๋ ๊ฒ์ธ๋ฐ์. ์น ์๋ฒ์ ์ฑ๋ฅ์ ๊ฐ์ ํ๋ฉด ๋์ผํ ์์ค์ ํธ๋ํฝ์ ์ฒ๋ฆฌํ๋ ๋ฐ ํ์ํ ์ธํ๋ผ ๋น์ฉ์ ๋ฎ์ถค์ผ๋ก์จ ์ด์ต์ ๊ธฐ์ฌํ ์ ์์ต๋๋ค.
์น ์๋ฒ์ ์ฑ๋ฅ์ ํฅ์์ํค๊ธฐ ์ํด์ ์ฐ๋ฆฌ๋ ๋ฌด์์ ํด์ผ ํ ๊น์?
์น ์๋ฒ์ ์ฑ๋ฅ์ ํฅ์์ํค๊ธฐ ์ํด ์๋ ค์ง ๋ฐฉ๋ฒ (@surksha8
)๋ค์ ์ฌ๋ฌ ๊ฐ์ง ์์ง๋ง, ์ฐ๋ฆฌ์ ์๋์ง๋ ํ์ ์ ์ด๊ธฐ ๋๋ฌธ์ ํจ๊ณผ๊ฐ ๊ฐ์ฅ ๋์ ๋ฐฉ๋ฒ์ ์ ํ์ ์ผ๋ก ์ ์ฉํด์ผ ํฉ๋๋ค. ๊ทธ๋ฌ๊ธฐ ์ํด ์์์ ์ ๊น ์ธ๊ธํ ์ ํฌ ํ ์ ๊ท ์๋น์ค์ ํน์ง์ ์ดํด๋ดค์ต๋๋ค.
- ๊ณต์ฐ ํฐ์ผ ๊ตฌ๋งค์๋ค์๊ฒ๋ง ๊ณต์ฐ ๊ด๋ จ ์ํ์ ํ๋งคํ๋ ์๋น์ค๋ค ๋ณด๋ ์ํ์ ๊ฐ์๊ฐ ์ ์ต๋๋ค. (๊ทธ๋ง์ ๋ ๊ณต์ฐ์ด ๋๋๋ฉด ์ฌ๋ผ์ง๋๋ค.)
- ํซํ ๊ณต์ฐ์ ์คํ ์์ ์ ๊ฐ์๊ธฐ ์์ฒญ๋ ํธ๋ํฝ์ด ๋ฐ์ํ ์ ์์ต๋๋ค.
- ํธ๋ํฝ์ ์ํ ๋ชฉ๋ก, ์ํ ์์ธ ํ์ด์ง์ ์ง์ค๋ ๊ฒ์ผ๋ก ์์๋ฉ๋๋ค.
- ์ํ ๋ชฉ๋ก, ์ํ ์์ธ ํ์ด์ง๋ ์ํ ์ฌ๊ณ ๊ฐ์ ์ ์ธํ๋ฉด ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ด ๊ฑฐ์ ์์ต๋๋ค.
์ด๋ฏธ ๋์น์ฑ์ จ๊ฒ ์ง๋ง, ์ ํฌ๋ ์ํ ๊ด๋ จ ํ์ด์ง์ ์๋ฒ ๋ ๋๋ง ๊ฒฐ๊ณผ์ ์บ์ฑ์ ์ ์ฉํ๋ ๊ฒ์ด ๊ฐ์ฅ ํจ๊ณผ๊ฐ ํด ๊ฒ์ด๋ผ ์๊ฒฌ์ ๋ชจ์์ต๋๋ค. ์ํ์ ๊ฐ์๊ฐ ์ ๊ณ , ํธ๋ํฝ์ด ๋ฐ์ํ ๋ ํน์ ์ํ์ผ๋ก ์ง์ค๋๋ ํน์ง์ Cache Hit Ratio์ ๋๋ฌด๋ ์ ๋ฆฌํ ์กฐ๊ฑด์ ๋๋ค.
Next.js์ ์บ์ฑ ๋งค์ปค๋์ฆ
Next.js๋ 13 ๋ฒ์ ์์ App router์ ํจ๊ป ์๋ก์ด ์บ์ฑ ๋งค์ปค๋์ฆ์ ์๊ฐํ์ต๋๋ค.
๋งค์ปค๋์ฆ | ๋์ | ์ฅ์ | ๋ชฉ์ | ๊ธฐ๊ฐ |
---|---|---|---|---|
Request Memoization | fetch ํจ์์ return๊ฐ | ์๋ฒ | React Component tree์์ data์ ์ฌ์ฌ์ฉ | request ์๋ช ์ฃผ๊ธฐ ๋์ |
Data Cache | Data | ์๋ฒ | ์ ์ ์์ฒญ์ด๋ deployment์ ์ํด ์ ์ฅ๋ ๋ฐ์ดํฐ | ์๊ตฌ์ (revalidate ๊ฐ๋ฅ) |
Full Route Cache | HTML, RSC Payload | ์๋ฒ | ๋ ๋๋ง cost ๊ฐ์ ๋ฐ ์ฑ๋ฅ ํฅ์ | ์๊ตฌ์ (revalidate ๊ฐ๋ฅ) |
Router Cache | RSC Payload | ํด๋ผ์ด์ธํธ | ๋ค๋น๊ฒ์ด์ ์ ์ํ ์๋ฒ ์์ฒญ ๊ฐ์ | ์ธ์ ๋๋ ์ ํด์ง ์๊ฐ ๋์ |
์ด ๊ธ์์๋ ์น ์๋ฒ์ ์ฑ๋ฅ์ ์ง์คํ๊ณ ์๊ธฐ ๋๋ฌธ์, ์๋ฒ์ ์ ์ฉ๋๋ ์บ์ ๋งค์ปค๋์ฆ์ ๋ํด์๋ง ๊ฐ๋จํ ์๊ฐํ๊ฒ ์ต๋๋ค.
Request Memoization
์น ์๋ฒ๋ก ํ์ด์ง ์์ฒญ์ด ๋ค์ด์ค๋ฉด ํ์ด์ง์ ํ์ํ ๋ฐ์ดํฐ๋ค์ fetchํ๊ฒ ๋๋๋ฐ, ์ด๋ ๋์ผํ endpoint๋ก์ API fetch๋ฅผ ์ฌ๋ฌ ์ปดํฌ๋ํธ์์ ์ํํ ํ์๊ฐ ์๋ค๋ฉด Request Memoization์ด ๋์ํฉ๋๋ค. (React๊ฐ fetch
ํจ์๋ฅผ ํ์ฅํด๋์๊ธฐ ๋๋ฌธ์ ๋ณ๋ ์ค์ ์ ํ์ ์์ต๋๋ค.)
์์ ์ปดํฌ๋ํธ์์ API fetch ๊ฒฐ๊ณผ๋ฅผ prop drilling ํ๋๊ฒ ๋์ , ๊ฐ ์ปดํฌ๋ํธ์์ fetch๋ฅผ ์ํํ๋๋ก ๊ตฌํํด๋ ์ค์ API ์์ฒญ์ ์ต์ด 1ํ๋ง ์ ์ก๋๊ณ ๋๋จธ์ง๋ ์๋ต๊ฐ์ ์ฌ์ฌ์ฉํฉ๋๋ค.
Request Memoization์ ์๋ฒ์์ ํธ์ถ๋๋ GET
๋ฉ์๋์๋ง ์ ์ฉ๋๋ฏ๋ก, POST
๋ DELETE
API ๋๋ ํด๋ผ์ด์ธํธ์์ ํธ์ถ๋๋ API์๋ ์ ์ฉ๋์ง ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ํ ๋ฒ์ ์๋ฒ ๋ ๋๋ง ๋์๋ง ์ ํจํ๊ธฐ ๋๋ฌธ์ ๋ฐ๋ก revalidate
ํ ํ์๊ฐ ์์ ๋ฟ ์๋๋ผ ํ ์๋ ์์ต๋๋ค.
Data Cache
์ฐ๋ฆฌ๊ฐ ์ผ๋ฐ์ ์ผ๋ก ์๊ฐํ ์ ์๋ API ์บ์ฑ์ ๋๋ค.
// Revalidate at most every hour
fetch('https://...', { next: { revalidate: 3600 } })
Next.js
๊ฐ ํ์ฅํด๋์ fetch
ํจ์์ next.revalidate
์ต์
์ ๋๊ธฐ๋ฉด Data Cache๊ฐ ๋์ํฉ๋๋ค. ์ฑ๊ณต์ ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์๋ค๋ฉด ๊ทธ ์๋ต๊ฐ์ ์ ์ฅํด๋์๋ค๊ฐ ๋์ผํ ๊ฒฝ๋ก๋ก fetch
ํจ์๋ฅผ ์คํํ ๋ ์ค์ API ํธ์ถ์ ๊ฑด๋๋ฐ๊ณ ์ ์ฅํด๋์ ์๋ต๊ฐ์ ๋ฐํํฉ๋๋ค.
ํ๋์ ์์ฒญ ๋์๋ง ์ ํจํ Request Memoization๊ณผ ๋ค๋ฅด๊ฒ Data Cache๋ ์ผ์ ์๊ฐ ๋์์ ์น ์๋ฒ๋ก ๋ค์ด์ค๋ ๋ชจ๋ ์์ฒญ์ ๋ํด ๋์ํฉ๋๋ค. ๋ง์ฝ next.revalidate
๋ฅผ 1์ด๋ก ์ค์ ํ๋ค๋ฉด, 1์ด์ 1000๋ช
์ ์ฌ์ฉ์๊ฐ ์ ์ํด๋ ์ค์ API ์์ฒญ์ 1ํ ์ ์ก๋ฉ๋๋ค.
Data Cache๋ฅผ ์ค๋ช ํ๋ ์ ์ด๋ฏธ์ง์์ ํ ๊ฐ์ง ์ง๊ณ ์ถ์ ๋ถ๋ถ์ revalidate ์๊ฐ์ด ์ง๋๋๋ผ๋ ์ฒซ ์์ฒญ์ ์บ์ฑ๋ ๊ฐ์ (STALE ์ํ์ฌ๋) ๋ฐํํ๋ค๋ ๊ฒ์ ๋๋ค. ๋ฐํ ํ ๋ฐฑ๊ทธ๋ผ์ด๋์์ API๋ฅผ ํธ์ถํด์ ๊ฐ์ ์ ๋ฐ์ดํธํ๋๋ฐ, ๊ฐ๋ฐ์ ์๋์ ๋ค๋ฅด๊ฒ ๋์ํ ์ ์๊ธฐ ๋๋ฌธ์ ์บ์๋ฅผ ์ ์ฉํ ๋ ์ฃผ์๊ฐ ํ์ํฉ๋๋ค.
router.refresh
๋ก๋ Data Cache๊ฐrevalidate
๋์ง ์๊ณ , revalidatePath๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค. (์ด๋๋ ์ฆ์revalidate
๋๊ธฐ ๋๋ฌธ์, ๋ค์ ์ฒซ ์์ฒญ์๋ ์๋ก์ด ๊ฐ์ ๋ฐํํฉ๋๋ค.)
Full Route Cache
์น ์๋ฒ์ ์ฑ๋ฅ์ ๋์ ๋๊ฒ ํฅ์์ํค๋ ค๋ฉด Full Route Cache๋ฅผ ์ ์ฉํด์ผ ํฉ๋๋ค. ์๋ฒ ๋ ๋๋ง ๊ณผ์ ์์ ์น ์๋ฒ์ ๋ฆฌ์์ค(ํนํ CPU)๋ฅผ ๋๋ถ๋ถ ์ฌ์ฉํ๊ฒ ๋๋๋ฐ, Full Route Cache๋ ์๋ฒ ๋ ๋๋ง ๊ฒฐ๊ณผ๋ฅผ ์ฌ์ฌ์ฉํจ์ผ๋ก์จ ์ด๋ฅผ ์ค์ผ ์ ์์ต๋๋ค.
Full Route Cache๋ฅผ ์ ์ฉํ๋ ค๋ฉด ํ์ด์ง๋ฅผ Static ๋ ๋๋ง ๋๋๋ก ๊ตฌ์ฑํด์ผ ํฉ๋๋ค. ๋ค์ ๋งํด Dynamic Function์ ์ฌ์ฉํ์ง ์์์ผ ํ๋๋ฐ, ๊ทธ๋ ์ง ์์ผ๋ฉด ๊ทธ๋ฆผ๊ณผ ๊ฐ์ด Full Route Cache ๋จ๊ณ๊ฐ SKIP ๋ฉ๋๋ค. Full Route Cache๋ฅผ ์ข ๋ ์์ธํ ์๊ณ ์ถ๋ค๋ฉด ๊ณต์๋ฌธ์๋ฅผ ์ฐธ๊ณ ํ์๊ธธ ๋ฐ๋๋๋ค.
Next.js์ ์บ์ฑ ์ ์ฉํ๊ธฐ
1. ์บ์ฑ ๋์ ์ ํ๊ธฐ
๊ฐ์ธํ๋ ํ์ด์ง(์ฅ๋ฐ๊ตฌ๋, ๊ฒฐ์ ๋ฑ)์ ํ์ํ ์ผํ๋ชฐ ํน์ฑ์ ํธ๋ํฝ์ด ์ ์ ๋ฟ ์๋๋ผ, ๋์ผํ ์๋ต์ ๋ด๋ ค์ค์๋ ์ ๋๊ธฐ ๋๋ฌธ์ ์บ์ฑ ์ ์ฉ ๋์์์ ์ ์ธํ์ต๋๋ค. ์์ ๋งํ๋ฏ ์ํ ๋ชฉ๋ก/์์ธ ํ์ด์ง๋ ๋น๋ก๊ทธ์ธ ์ํ์์๋ ๋๊ตฌ๋ ์กฐํ ๊ฐ๋ฅํ๊ณ ํธ๋ํฝ์ด ๋ชฐ๋ฆด ๊ฐ๋ฅ์ฑ์ด ์๊ธฐ ๋๋ฌธ์ ์์ฃผ ์ ์ ํ ๋์์ ๋๋ค.
2. ๋ณ๊ฒฝ ๋ฒ์ ํ์ ๋ฐ ํ ์คํธ ์ฝ๋ ๋ณด๊ฐ
Full Route Cache๊ฐ ๋์ํ๊ฒ ํ๋ ค๋ฉด Dynamic routes์ ISR์ ์ ์ฉํ๊ณ , ์ฌ์ฉ ์ค์ธ Dynamic Function์ ์ ๊ฑฐํด์ผ ํ์ต๋๋ค. cookies
, headers
, pathParams
, searchParams
๋ชจ๋ ์ฌ๊ธฐ์ ๊ธฐ์์ ์ฌ์ฉ ์ค์ด๋ค ๋ณด๋ ๊ด๋ฒ์ํ ๋ณ๊ฒฝ์ด ํ์ํ๊ณ , ์์ ์ ์ธ ๋ณ๊ฒฝ์ ์ํด์ ๋จ์๋ฅผ ์ชผ๊ฐ๊ณ ๊ด๋ จํด์ ํ
์คํธ ์ฝ๋๋ฅผ ๋ณด๊ฐํ์ต๋๋ค.
3. AS-IS ๋ถํ ํ ์คํธ
์ฑ๊ณผ๋ฅผ ์์นํํ๋ ๊ฒ์ ์ง์ฅ์ธ์๊ฒ ์ค์ํ ๋๋ชฉ์ ๋๋ค. ๊ฒฐ๊ณผ์ ์ํฅ์ ์ค ์ ์๋ ์กฐ๊ฑด๋ค์ ํต์ ํ์ฌ ๋ถํํ ์คํธ๋ฅผ ์งํํ์ต๋๋ค. (with. nGrinder)
4. ์บ์ ๋๋ฒ๊น ์ปดํฌ๋ํธ ๊ตฌํ
๋ธ๋ผ์ฐ์ ์ ๋ ๋๋ง ๋ ํ์ด์ง๊ฐ Full Route Cache๋ฅผ HIT ํ๋์ง๋ฅผ ํ์ธํ๊ธฐ ์ํด ๊ฐ๋จํ ์๋ฒ ์ปดํฌ๋ํธ ํ๋๋ฅผ ์ถ๊ฐํ์ต๋๋ค.
function DebugCache({path}: {path: string}) {
return (
<div>
{dayjs().valueOf()}
<RevalidateButton path={path}/>
</div>
);
}
'use client'
function RevalidateButton({ path }: {path: string}) {
return <button onClick={() => revalidateFullRouteCache(path)}>revalidate</button>
}
'use server'
import { revalidatePath } from 'next/cache';
export async function revalidateFullRouteCache(path) {
if (path) {
revalidatePath(path, 'layout');
}
}
๋ธ๋ผ์ฐ์ ์์ ์๋ก๊ณ ์นจ์ ํด๋ dayjs().valueOf()
๊ฐ์ด ๋์ผํ๋ค๋ฉด Full Route Cache๊ฐ HIT ํ๋ค๊ณ ํ๋จํ ์ ์์ต๋๋ค.
5. ์ฝ๋ ๋ณ๊ฒฝ
๋ชฉํ๋ generateStaticParams
๋ฅผ ํตํด์ ISR ๋ฐฉ์์ static route๋ก ๋ฐ๊ฟ์ผ๋ก์จ Full Route Cache๋ฅผ ์ ์ฉํ๋ ๊ฒ์
๋๋ค.
5.1 URL PATH
์ ํฌ ์๋น์ค๋ ๋ค๊ตญ์ด๋ฅผ ์ง์ํ๋ฉฐ url path์ ์ธ์ด๊ฐ์ด ํฌํจ๋์ด์์ต๋๋ค. ์ง์ํ๋ ์ธ์ด๋ ๊ณ ์ ๋ผ ์๊ธฐ ๋๋ฌธ์, generateStaticParams
์ ๋ฐ๋ก ์ ์ฉํด์ static ํ์ด์ง๋ก ๋ง๋ญ๋๋ค.
// app>[lang]>layout.tsx
export function generateStaticParams() {
return SUPPORTED_LANGS.map(locale => ({ locale }));
}
์ํ ์์ธ ํ์ด์ง๋ url path์ ์ํ ID ๊ฐ์ด ํฌํจ๋์ด์์ต๋๋ค. ํ์ง๋ง ๋ค๊ตญ์ด์๋ ๋ค๋ฅด๊ฒ ์ํ์ ID ๊ฐ์ ๊ณ ์ ๋ ๊ฐ์ด ์๋๊ธฐ ๋๋ฌธ์ generateStaticParams
์์ ๋น ๋ฐฐ์ด์ ๋ฆฌํดํด์ค๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ISR๋ก ๋์ํ๊ฒ๋ฉ๋๋ค.
// app>[lang]>(static)>์ํ์์ธ>[id]>page.tsx
export async function generateStaticParams() {
return [];
}
๊ทธ๋ฆฌ๊ณ ์ํ ๋ชฉ๋ก ํ์ด์ง์์๋ ํ๊ทธ๋ฅผ ์ ํํด์ ์ํ๋ค์ ์กฐํํ ์ ์๋๋ฐ์.
์ ํ๋ ํ๊ทธ๋ฅผ ๊ธฐ์กด์ searchParams
๋ก ๋ค๋ค๋๋ฐ, static route๋ฅผ ์ํด์ pathParams
๋ก ๋ณ๊ฒฝํ์ต๋๋ค.
/products?tagId={tagId}
-> /{tagId}/products
๊ทธ๋ ๊ฒ ํ๋ฉด ์ํ ์์ธ ํ์ด์ง์ ๋์ผํ๊ฒ generateStaticParams
๋ฅผ ์ฌ์ฉํด์ ISR๋ก ๋์ํ๊ฒ ํ ์ ์์ต๋๋ค.
// app>[lang]>(static)>์ํ๋ชฉ๋ก>[tagId]>page.tsx
export async function generateStaticParams() {
return [];
}
5.2 Dynamic Functions
์ ํฌ ์๋น์ค๋ ์ฌ๋ฌ ๊ฐ์ง ์ธ์ฆ ์ฒด๊ณ๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ ์ผ๊ด๋ ์ธ์ฆ ์ฒ๋ฆฌ๋ฅผ ์ํด middleware์์ cookies
, headers
, searchParams
๋ฅผ ์ฌ์ฉํด์ ์ ์ฒ๋ฆฌํ๊ณ ์์ต๋๋ค. ๋ชจ๋ dynamic function์ด๊ธฐ ๋๋ฌธ์ middleware ๋์ ํด๋ผ์ด์ธํธ์์ ์ ์ฒ๋ฆฌํ๋๋ก AuthProvider ์ถ๊ฐํ์ต๋๋ค.
// before
// middleware(Server)์์ Client๋ก ๋ง์ด๊ทธ๋ ์ด์
๋ผ์ผ ํ๋ ์ฝ๋ ์์์
๋๋ค.
export function middleware(request: NextRequest) {
const { nextUrl } = request;
nextUrl.searchParams.set(SEARCH_PARAM_KEYS.REGION_TYPE, regionType);
const responseForSetCookie = NextResponse.redirect(nextUrl);
responseForSetCookie.cookies.set(COOKIE_KEYS.USER_TYPE, getUserType());
...
...
return responseForSetCookie;
}
// after
'use client';
function AuthProvider({ children }: PropsWithChildren) {
useEffect(() => {
document.cookie = `${COOKIE_KEYS.USER_TYPE}=${getUserType()}; domain=.melon.com; path:/;`;
const searchParams = new URLSearchParams(window.location.search);
searchParams.set(SEARCH_PARAM_KEYS.REGION_TYPE, getRegionType());
...
...
}, []);
return children;
}
์ด์ธ์๋ API๋ฅผ ํธ์ถํ๋ ํจ์์์ ์ธ์ฆ์ ์ํด ์ฌ์ฉํ๊ณ ์๋ dynamic function์ (์ธ์ฆ์ด ํ์ ์๋) ์ํ ๋ชฉ๋ก/์์ธ ํ์ด์ง์์๋ ์ฌ์ฉํ์ง ์๋๋ก ์ฒ๋ฆฌํ์ต๋๋ค.
// before
// API ํธ์ถํ๋ ๋ถ๋ถ์์ ์ ๊ฑฐ๋ผ์ผ ํ๋ ์ฝ๋ ์์์
๋๋ค.
const { cookies } = await import('next/headers');
const cookieStore = cookies();
return {
[HEADER_KEYS.CHANNEL_TYPE]: cookieStore.get(COOKIE_KEYS.CHANNEL_TYPE)?.value as ChannelTypes,
[HEADER_KEYS.REGION_TYPE]: cookieStore.get(COOKIE_KEYS.REGION_TYPE)?.value as RegionTypes,
};
Header/Footer ์ปดํฌ๋ํธ์์๋ ์ธ์ฆ์ด ํ์ํ(๊ฐ์ธํ๋) ๋ฐ์ดํฐ๊ฐ ์ฌ์ฉ๋๊ณ ์์์ต๋๋ค. ๋๋ฌธ์ ์บ์ ์ ์ฉ์ด ์ด๋ ค์์ ์๋ฒ ์ปดํฌ๋ํธ์์ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๋ก ๋ณ๊ฒฝํ์ต๋๋ค. ์ด๋ ๊ฒ ๋๋ฉด Header์ Footer๊ฐ ํด๋ผ์ด์ธํธ์์ dynamic import ๋๋๋ฐ, Layout Shift๊ฐ ๋ฐ์ํ์ง ์๋๋ก Placeholder ์ปดํฌ๋ํธ๋ ์ถ๊ฐํด ์คฌ์ต๋๋ค.
const Footer = dynamic(
() => import('@/common/Footer.client'),
{ ssr: false, loading: () => <ComponentPlaceholder /> },
);
5.3 Full Route Cache ์ ์ฉ
static route๊ฐ ๊ฐ๋ฅํ๊ฒ ๋์๋ค๋ฉด, layout.tsx
์ revalidate ์๊ฐ์ ์ค์ ํด์ Full Route Cache๊ฐ ๋์ํ๋๋ก ํด์ค๋๋ค.
// app>[lang]>(static)>์ํ์์ธ>[id]>layout.tsx
export const revalidate = 1; // seconds
Info
Full Route Cache๋ Data Cache๊ฐ HIT ๋์์ ๋์๋ง ๋์ํฉ๋๋ค. ๋ฐ๋ผ์ Full Route Cache๋ฅผ ์ ์ฉํ๋ ค๋ ํ์ด์ง์ ๋ชจ๋ fetch
์๋ next.revalidate
๊ฐ์ด (Full Route Cache์ revalidate ๊ฐ๋ณด๋ค ํฌ๊ฑฐ๋ ๊ฐ๊ฒ) ์ค์ ๋ผ์ผ ํฉ๋๋ค.
next.js 14.0.2 (vercel/next.js
) ์ด์ ๋ฒ์ ์์ standalone ๋น๋ ํ๋ฉด, 304 ์๋ต์ ๋ฌดํ์ผ๋ก ์บ์ฑ ํ๋ ๋ฒ๊ทธ (vercel/next.js
)๊ฐ ์กด์ฌํฉ๋๋ค. ์ ํฌ๋ 13๋ ๋ฒ์ ์ ์ฌ์ฉํ๊ณ ์๊ธฐ ๋๋ฌธ์, major ๋ฒ์ ์
๋์ ํด๋น ๋ถ๋ถ๋ง ๋ชฝํคํจ์นํด์ ํด๊ฒฐํ์ต๋๋ค.
ํจ๊ณผ๋ ๊ต์ฅํ๋ค
Full Route Cache ์ ์ฉ ์ธ์๋ ์ ์ ๋ฆฌ์์ค๋ค(js, css)์ Load Balancer์์ ์บ์ฑํ๋๋ก ์ ์ฉํ ํ์ ๋ถํํ ์คํธ๋ฅผ ์งํํ์ต๋๋ค.
์ฌ๋ฌ ๋ฒ์ ๋ถํํ ์คํธ ๋น๊ต๊ตฐ ์ค ๋ํ ํ ์์ ๊ฐ์ ธ์ ๋ดค์ต๋๋ค. ๋ค๋ฅธ ์กฐ๊ฑด์ ๋ถํํ ์คํธ๋ ๋น์ทํ ์์ค์ ์ฐจ์ด๋ฅผ ๋ณด์ ๋๋ค. ์ ๊ฒ๋ 5๋ฐฐ์์ ๋ง๊ฒ๋ 10๋ฐฐ๊น์ง๋ TPS๊ฐ ๊ฐ์ ๋ ๊ฒ์ ํ์ธํ ์ ์์์ต๋๋ค. TPS์ ๊ฐ์ ์ ๋น์ฐํ๊ฒ๋ ์๋ต์๊ฐ(Mean Test Time)๊ณผ CPU ์ฌ์ฉ๋ฅ ์ด ๊ฐ์ ๋์์์ ์๋ฏธํฉ๋๋ค.
์ ๋ฆฌ
์ด ๊ธ์์๋ ์์ธํ ๋ค๋ฃจ์ง ์์์ง๋ง React Server Component๋ฅผ ๋น๋กฏํด์ Next.js์ App Router๊ฐ ์์ง์ ๊ณต์์ ์ผ๋ก experimental
๋จ๊ณ์ธ ๊ธฐ๋ฅ์ด๋ ์จ์ด์๋ ๋ฒ๊ทธ๋ค์ด ์๋ค ๋ณด๋ ํ๋ก๋์
๋จ๊ณ๊น์ง ๊ตฌํํ๋ ๋ฐ์ ์ด๋ ค์์ด ๋ง์์ต๋๋ค. ๋๋ฒ๊น
ํด์ด ๋ ๊ณ ๋ํ๋๋ฉด ์ข๊ฒ ๋ค ์๊ฐํ๊ณ , ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ์ ์ ํ ๋์๋ ํ์ํด ๋ณด์์ต๋๋ค. ๊ทธ๋ผ์๋ ์ด ๊ธ์ ์ฌ๋ก์ฒ๋ผ ์ฅ์ ์ ์ ์ ํ๊ฒ ํ์ฉํ๋ค๋ฉด ๊ณจ์ ๋ ๋ง์ด ๋ฃ๋ ๊ณต๊ฒฉ์๊ฐ ๋ ์ ์์ง ์์๊น ์๊ฐํ๋ฉฐ ๊ธ์ ๋ง์นฉ๋๋ค.