Next.js์ React์ ์ฐจ์ด, Next.js์ Page Router์ App Rotuer์ ์ฐจ์ด์ ๋ํด ์ ๋ฆฌํด๋ณด๋ ค๊ณ ํ๋ค. ๊ณ์ ์ฝ๋๋ก๋ง ์ง์ ์ง๋ดค์ง, ๋จธ๋ฆฌ ์์ ๊ฐ๋ ์ ๋ฆฌ๋ฅผ ํด๋ณด๊ณ ์ ํฌ์คํ ์ ํด๋ณด๊ธฐ๋ก ํ๋ค!
React vs Next.js
๋จผ์ React๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ, Next.js๋ ํ๋ ์์ํฌ๋ผ๊ณ ๊ฐ ๊ณต์ ์ฌ์ดํธ์ ์๊ฐ๋ฅผ ํด๋จ๋ค.

React๋ UI ๊ฐ๋ฐ์ ์ํ JavaScript ๋ผ์ด๋ธ๋ฌ๋ฆฌ Next.js๋ React ์ ์ฉ์ ์น ๊ฐ๋ฐ ํ๋ ์์ํฌ
๊ทธ๋ผ ์ฌ๊ธฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํ๋ ์์ํฌ์ ์ฐจ์ด๋ ๋ฌด์์ผ๊น?
๋ผ์ด๋ธ๋ฌ๋ฆฌ vs ํ๋ ์์ํฌ
๊ธฐ๋ฅ ๊ตฌํ์ ์ฃผ๋๊ถ์ด ๋๊ตฌ์๊ฒ ์๋๊ฐ?
์ฃผ๋๊ถ์ด ๊ฐ๋ฐ์์๊ฒ ์๋ค๋ฉด? ๐๐ป ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฃผ๋๊ถ์ด ๊ฐ๋ฐ์์๊ฒ ์๋ค๋ฉด? ๐๐ป ํ๋ ์์ํฌ
์ฃผ๋๊ถ์ ๊ฐ๋ฐ์๊ฐ ๊ฐ์ง๋ค๋ฉด, ๊ธฐ๋ฅ ๊ตฌํ์ ์ํ๋ ๋ฐฉํฅ์ผ๋ก ๊ฐ๋ฐ์๊ฐ ๋ง๋ค์ด๊ฐ ์ ์๊ณ ์ฐ๊ณ ์ถ์ ๋๊ตฌ์ ๊ธฐ์ ์ ๋ง์๋๋ก ์ธ ์ ์๋ค. => ์์ ๋โฌ๏ธ, ๋์ ๊ฐ๋ฐ์๊ฐ ๋ค ๊ตฌํํด์ผ ํจ..
๋ฐ๋๋ก ์ฃผ๋๊ถ์ ํ๋ ์์ํฌ๊ฐ ๊ฐ์ง๋ค๋ฉด, ํ๋ ์์ํฌ๊ฐ ์ ๊ณตํ๋ ๊ธฐ๋ฅ์ ์ด์ฉํ๊ณ ํ์ฉํ๋ ๋ฒ์ ๋ด์์๋ง ์ถ๊ฐ ๋๊ตฌ๋ฅผ ๊ฐ์ ธ๊ฐ ์ธ ์ ์๋ค. => ์์ ๋โฌ๏ธ, ๋์ ๋ณต์กํ ๊ธฐ๋ฅ๋ค์ ์ ๊ณตํด์ฃผ๊ธฐ์ ๊ฐ์ ธ๋ค ์ฐ๋ฉด ๋๋ค!
CSR vs SSR
๋ค๋ฅธ ์ฐจ์ด์ ์ผ๋ก๋ React๋ CSR(Client Side Rendering)์, Next.js๋ SSR(Server Side Rendering)์ด๋ผ๋ ํน์ง์ ๊ฐ์ง๊ณ ์๋ค.
์ด๋ ํฌ๊ฒ ์ฌ์ ๋ ๋๋ง์ ํ๋๋, ์ํ๋๋์ ์ฐจ์ด๊ฐ ์๋ค.
์ฌ์ ๋ ๋๋ง์ด๋, ๋ธ๋ผ์ฐ์ ์ ์์ฒญ์ ์ฌ์ ์ ๋ ๋๋ง์ด ์๋ฃ๋ HTML์ ์๋ตํ๋ ๋ ๋๋ง ๋ฐฉ์์ด๋ค. CSR์ ๋จ์ ์ ํจ์จ์ ์ผ๋ก ํด๊ฒฐํ๋ ๊ธฐ์ ์ด๋ค!
๊ทธ๋ ๋ค๋ฉด CSR์ ๋ฌด์์ผ๊น?
CSR(Client Side Rendering)
React.js ์ฑ์ ๊ธฐ๋ณธ์ ์ธ ๋ ๋๋ง ๋ฐฉ์์ด๋ค. ํด๋ผ์ด์ธํธ(๋ธ๋ผ์ฐ์ )์์ ์ง์ ํ๋ฉด์ ๋ ๋๋งํ๋ค.
์ด๋ฏธ์ง ์ถ์ฒ: ํ ์
ํฌ๊ธฐ๋ก ์๋ผ๋จน๋ Next.js
- ์ ์ ๊ฐ ๋ธ๋ผ์ฐ์ ๋ฅผ ํตํด ์ด๊ธฐ ์ ์ ์์ฒญ์ ์๋ฒ์ ๋ณด๋ด๊ณ , ๋ฆฌ์กํธ ์น์๋ฒ๋ ๋น๊ป๋ฐ๊ธฐ์ธ index.html ํ์ผ์ ๋ธ๋ผ์ฐ์ ์๊ฒ ์ ์กํ๋ค.
- ๋ธ๋ผ์ฐ์ ๋ ์๋ฒ๋ก๋ถํฐ ๋ฐ์ html ํ์ผ์ ํ๋ฉด์ ๋ ๋๋งํ๋ค.
- ์ด๋, ์ฌ์ฉ์๋ ํฐํ๋ฉด์ ๋ณด๊ฒ ๋๋ค.
- ์๋ฒ๋ ๋ธ๋ผ์ฐ์ ์๊ฒ JS ํ์ผ๋ค์ ๋ฒ๋ค๋งํ๊ณ ์ด ๋ฒ๋ค๋งํ ๊ฒ์ ๋ธ๋ผ์ฐ์ ์๊ฒ ๋ณด๋ธ๋ค.
- ๋ธ๋ผ์ฐ์ ๋ ์ด ๋ฒ๋ค๋ง๋ JS๋ฅผ ์คํํ๊ณ ์ด๋, ๋ฆฌ์กํธ ์ฑ์ด ๊ตฌ๋๋์ด ์ค์ ํ๋ฉด์ ๋ํ๋๊ฒ ๋๋ค.
- ๊ฒฐ๊ณผ์ ์ผ๋ก ์ฌ์ฉ์๋ ์์ฒญํ ์นํ์ด์ง๋ฅผ ๋ณผ ์๊ฐ ์๊ฒ ๋๋ค.
CSR์ ์ด๋ ๋ฏ ์ด๊ธฐ ๋ก๋์๋ ํฐํ๋ฉด์ด ๋์จ๋ค๋ ๋จ์ ์ด ์๋ค. ํ์ง๋ง ์ฅ์ ์ผ๋ก, ํ์ด์ง ์ด๋์ด ๋งค์ฐ ๋น ๋ฅด๊ณ ์พ์ ํ๋ค๋ ํน์ง์ด ์๋ค.
์ ๊ทธ๋ด๊น?๐ค
์ด๋ ์๋ฒ๊ฐ JS ๋ฒ๋ค๋ง์ ํ๊ณ ๋ ํ, ๋ธ๋ผ์ฐ์ ์๊ฒ ์ ์กํ์ ๋ ๋ฒ๋ค๋ง๋ JS ํ์ผ์๋ ๋ฆฌ์กํธ ์ฝ๋๋ค์ด ๋ค์ด์๊ณ ์ฆ, ํด๋น ์๋น์ค์ ๋ชจ๋ ์ปดํฌ๋ํธ ์ฝ๋๋ค์ด ๋ค์ด ์๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ดํ ์ฌ์ฉ์๊ฐ ๋ค๋ฅธ ํ์ด์ง์ ์ ์ํ๋๋ผ๋, ๋ธ๋ผ์ฐ์ ๊ฐ ์์ฒด์ ์ผ๋ก ๋ฆฌ์กํธ ์ฑ์ ์คํํด์ ๋น ๋ฅธ ์๋๋ก ํ์ด์ง ์ด๋์ด ๊ฐ๋ฅํ๊ฒ ๋๋ค!
ํ์ง๋ง ์์ ๋งํ๋ฏ์ด FCP(์์ฒญ ์์ ์์ ๋ถํฐ ์ปจํ ์ธ ๊ฐ ์ฒ์ ๋ํ๋๋๋ฐ ๊ฑธ๋ฆฌ๋ ์๊ฐ) ๊ฐ ๊ธธ์ด์ ธ ์ด๊ธฐ ์ ์ ์๊ฐ์ด ๊ธธ๋ค๋ ๋จ์ ์ด ์๋ค.
SSR(Server Side Rendering)
SSR์ ์์ ๋งํ ์ฌ์ ๋ ๋๋ง์ผ๋ก CSR์ ๋จ์ ์ ์ด๊ฒจ๋ธ๋ค.
- ์ ์ ๊ฐ ๋ธ๋ผ์ฐ์ ๋ฅผ ํตํด ์๋ฒ์๊ฒ ์ด๊ธฐ ์ ์ ์์ฒญ์ ํ๋ค.
- ์๋ฒ๋ ์ง์ JS ์ฝ๋(๋ฆฌ์กํธ ์ฝ๋)๋ฅผ ์คํํด์ ๋ชจ๋ ๋ฆฌ์กํธ ์ปดํฌ๋ํธ๋ค์ html๋ก ๋ณํ(์ฌ์ ๋ ๋๋ง)์ ํ๋ค.
- ๋ ๋๋ง์ด ์๋ฃ๋ ํ, html ํ์ผ์ ๊ทธ๋๋ก ๋ธ๋ผ์ฐ์ ์๊ฒ ์ ์กํ๋ค.
- ๋ธ๋ผ์ฐ์ ๋ ์ ์ก ๋ฐ์ html ํ์ผ์ ํ๋ฉด์ ๊ทธ๋๋ก ๋ ๋๋งํ๋ค.
- ๊ฒฐ๊ณผ์ ์ผ๋ก ์ฌ์ฉ์๋ ์์ฒญํ ์นํ์ด์ง๋ฅผ ๋ณผ ์๊ฐ ์๊ฒ ๋๋ค.
์ ๋ฆฌํ์๋ฉด, ์๋ฒ์์ ๋ฐ๋ก js๋ฅผ ์คํํด์ html ํ๋ฉด์ ๊ทธ๋ ค๋ธ ์ํ๋ก ๋ธ๋ผ์ฐ์ ์ ๋ณด๋ธ๋ค๋ ๋ป!! => FCP ๊ฐ์
Hydration(์ํ)
๋ธ๋ผ์ฐ์ ๋ ์๋ฒ๋ก๋ถํฐ html์ ๋ฐ๊ณ ์ฌ์ฉ์์๊ฒ ๋๊ฒจ์ค ํ ์ฌ์ฉ์๊ฐ ์ฌ์ดํธ์ ํด๋ฆญ์ ์๋ํ๋ฉด ์๋์ ํ์ง ์๊ฒ ๋๋คโฆ ์๊ทธ๋ด๊น?
์ด๋ ๋ธ๋ผ์ฐ์ ๊ฐ js ํ์ผ์ ๊ฐ๊ณ ์์ง ์๊ธฐ ๋๋ฌธ์ด๋ค. ์ดํ ์๋ฒ๋ JS ๋ฒ๋ค๋ง ํ์ผ์ ๋ธ๋ผ์ฐ์ ์๊ฒ ์ ์กํ๊ณ , ๊ทธ์ ์์ผ ๋ธ๋ผ์ฐ์ ๋ html๊ณผ JS๋ฅผ ์ฐ๊ฒฐ์์ผ ์คํํ๋ค. ์ดํ, ์ฌ์ฉ์๋ ์ํธ์์ฉ์ด ๊ฐ๋ฅํ๊ฒ ๋๋ค!!
์ด๋ ๊ฒ html๊ณผ js๋ฅผ ์ฐ๊ฒฐ์ํค๋ ๊ณผ์ ์ Hydration ๊ณผ์ ์ด๋ผ๊ณ ํ๋ค.
์ด๋ฏธ์ง ์ถ์ฒ: ํ ์
ํฌ๊ธฐ๋ก ์๋ผ๋จน๋ Next.js
๊ทธ๋ ๊ธฐ์ SSR์ ์ด๊ธฐ ๋ก๋ ์๋๊ฐ ๋น ๋ฅด๋ค๋ ์ฅ์ ์ด ์๋ค. ๋ํ, ์ดํ ํ์ด์ง ์ด๋ ์๋๋ ์ด๋ฏธ ๋ธ๋ผ์ฐ์ ๊ฐ JS ๋ฒ๋ค๋ง ํ์ผ์ ๊ฐ์ง๊ณ ์๊ธฐ ๋๋ฌธ์ CSR๊ณผ ๊ฐ์ ๋ฐฉ์์ผ๋ก ์ฒ๋ฆฌ๋๋ค.
๊ฒฐ๊ณผ์ ์ผ๋ก SSR์ ์ฌ์ ๋ ๋๋ง ๋ฐฉ์์ ๋น ๋ฅธ FCP ๋ฌ์ฑ(CSR์ ๋จ์ ํด์) + ๋น ๋ฅธ ํ์ด์ง ์ด๋(CSR์ ์ฅ์ ์น๊ณ) ์ ํน์ง์ ๊ฐ๊ฒ ๋๋ค. ๐ฅณ๐
Page Router
ํ์ฌ ๋ง์ ๊ธฐ์ ์์ ์ฌ์ฉ๋๊ณ ์๋ ์์ ์ ์ธ ๋ผ์ฐํฐ
Page Router๋ pages ํด๋์ ๊ตฌ์กฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ์ด์ง ๋ผ์ฐํ ์ ์ ๊ณตํ๋ค.
ํ์ผ๋ช ๋๋ ํด๋๋ช ๊ธฐ๋ฐ์ ๋ผ์ฐํ ์ ๊ฐ์ง๋ค.

๋์ ๋ผ์ฐํ ์ ๋๊ดํธ๋ก ๋์ ํ์ผ๋ช ์ ๋ง๋ค์ด์ฃผ๋ฉด ๋๋ค.

_app.tsx์ _docupent.tsx์ ์ญํ
pages ํด๋ ์์ ์๋ ์ด ๋ ํ์ผ์ ํ์ด์ง์ ์ญํ ์ ํ์ง ์๋๋ค. next ํ๋ก์ ํธ์ ๋ชจ๋ ํ์ด์ง์ ์ ์ฉํ ๊ณตํต๋ ๋ก์ง์ด๋ ๋ ์ด์์, ๋ฐ์ดํฐ์ ๋ํด ํ์ํ ํ์ผ๋ค์ด๋ค.
_app.tsx
React์์์ App ์ปดํฌ๋ํธ์ ๊ฐ์ ๊ฐ๋ ์ด๋ค. ์ฆ, ๋ฃจํธ ์ปดํฌ๋ํธ๋ก ์๊ฐํ๋ฉด ๋๋ค. ์ฌ๊ธฐ์ ๊ณตํต ๋ ์ด์์๊ณผ ๋น์ฆ๋์ค ๋ก์ง์ ์์ฑํ ์ ์๋ค.
_docupent.tsx
๋ชจ๋ ํ์ด์ง์ ๊ณตํต์ผ๋ก ์ ์ฉ๋์ด์ผ ํ๋ next ์ฑ์ html ์ฝ๋๋ฅผ ์ค์ ํ๋ ์ปดํฌ๋ํธ์ด๋ค. React ์ฑ์ index.html๊ณผ ๊ฐ์ ๊ฐ๋ ์ด๋ค. ์ฌ๊ธฐ์ ๋ฉํ ํ๊ทธ๋ ํฐํธ, ์๋ํํฐ script ๋ฑ์ ๋ฃ์ ์ ์๋ค.
์ค์ฒฉ ๋ผ์ฐํฐ
์ค์ฒฉ ๋ผ์ฐํ ์ ํ๊ณ ์ถ๋ค๋ฉด ํด๋๋ช ๊ธฐ๋ฐ ๋ผ์ฐํ ๋ฐฉ์์์ ์ํ๋ ํ์ผ๋ช ์ ์ ํ์ผ์ ๋ง๋ค๋ฉด ๊ทธ ํ์ผ๋ช ์ด ์ค์ฒฉ ๋ผ์ฐํ ์ด ๋๋ค.(๋น์ฐํ ์ด๋ ํ์ผ๋ช ์ด ์๋ ํด๋๋ช ๊ธฐ๋ฐ์ผ๋ก ์ ํด๋๋ฅผ ๋ง๋ค๊ณ index.tsx๋ฅผ ๋ง๋ค์ด๋ ๊ฐ๋ฅํ๋ค.)
useRouter
๋ณดํต ์ํ๋ ๊ฒ์์ด๋ฅผ ์ ๋ ฅํ ๋ ์ฟผ๋ฆฌ ์คํธ๋ง์ด๋ผ๋ ํํ๋ก ๋ฐ์ดํฐ๊ฐ ์ ๋ฌ๋๋๋ฐ ์ด ์ฟผ๋ฆฌ ์คํธ๋ง์ ๊ฐ์ ๊บผ๋ด์ ์ฌ์ฉํ๊ธฐ ์ํด next/router์ useRouter๋ฅผ ํ์ฉํ๋ค.
๊ทผ๋ฐ? next/router์ next/navigation์ด ์๋ค. ๋์ ์ฐจ์ด๋ ๋ญ๊น?๐ค
next/navigation์ App router์์ ์ฌ์ฉ๋๋ ํจํค์ง๋ค. useSearchParams๋ฅผ ํ์ฉํ๋ค. Page ๋ผ์ฐํฐ์์๋ next/router์ useRouter๋ก ๋ถ๋ฌ์ฌ ์ ์์ผ๋ ๋์ ์ฐจ์ด๋ฅผ ์์ง ๋ง์.
useRouter() ๋ฅผ ์ฝ์๋ก ์ฐ์ด๋ณด๋ฉด ๋ค์์ฒ๋ผ ๋์จ๋ค.

์ ์ฌ์ง์ ๋ณด๋ฉด ํ ์ฝ์์ ๊ต์ฅํ ๋ง์ ํ๋กํผํฐ๊ฐ ์ฐํ ๊ฑธ ํ์ธํ ์ ์๋ค. back๊ณผ push์ฒ๋ผ ๋ค๋ฅธ ๊ฒฝ๋ก๋ก ์ด๋ํ๋ ๋ฉ์๋, ์ฟผ๋ฆฌ ์คํธ๋ง์ ๋ฐ์์ค๋ query ๋ฑ์ด ๋ค์ด์๋ค.
๊ทผ๋ฐ ์ ์ฝ์์ด ๋ ๋ฒ ์ฐํ๋๊ฐ? ์์ธํ ๋ณด๋ฉด query์ ์ฐจ์ด๊ฐ ์๋ค. next ์ฑ์ด ์ฟผ๋ฆฌ ์คํธ๋ง์ ์ฝ์ผ๋ฉด์ ์ปดํฌ๋ํธ๋ฅผ ํ ๋ฒ ๋ ๋ ๋๋งํ๊ธฐ ๋๋ฌธ์ด๋ค.
๋์ ๋ผ์ฐํ
ํ์ผ๋ช ์ ๋๊ดํธ๋ก ๊ฐ์ธ๋ฉด, ์ด๋ ๋์ ๋ผ์ฐํ ์ ํ ์ ์๋๋ฐ ๋ณดํต id, slug ๋ฑ์ผ๋ก ๋ง์ด ์ด๋ค.
pages/book/[id].tsx๋ก ํ์ผ์ ๋ง๋ค๋ฉด, /book/{id} ํํ๋ก ํ์ด์ง๊ฐ ๋์ ์ผ๋ก ๋ง๋ค์ด์ง๋ค.
์ฌ๊ธฐ์ ๋ง์ฝ
/book/{id}/{id2}/{di3}์ด๋ฐ ์์ ๋ค์ ๋ผ์ฐํ ์ด ๋ ๋ถ์ผ๋ฉด ์ด๋ป๊ฒ ๋ ๊น?
์๋ ํ์ด์ง๋ก ๋ฌ๋ค.(404)
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ , Catch-all Segments๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
Catch-all Segments
[id].tsx๋ฅผ [โฆid].tsx๋ก ๋ฐ๊ฟ๋ณด์.
pages/shop/[...slug].js๋ /shop/clothes์ /shop/clothes/tops, /shop/clothes/tops/t-shirts ๋ชจ๋ ํฌํจํ๋ค.
Catch-all Segments
๊ทผ๋ฐ ์ฌ๊ธฐ์ /shop ๊ฒฝ๋ก๋ก ๋ค์ด๊ฐ๋ฉด, 404 ํ์ด์ง๊ฐ ๋ฌ๋ค. ๋ง์ฝ ๋ค์ ์ถ๊ฐ ๊ฒฝ๋ก๊ฐ ๋ถ๋ ์๋ถ๋ ์กฐ๊ฑด ์์ด ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด [[...slug]].tsx์ฒ๋ผ ๋๊ดํธ๋ก ํ ๋ฒ ๋ ๊ฐ์ธ์ฃผ๋ฉด ๋๋ค.
Pre-Fetching
์ฌ์ฉ์๊ฐ ํ์ฌ ๋ณด๊ณ ์๋ ํ์ด์ง์์ ์ด๋ํ ์ ์๋ ๋ชจ๋ ํ์ด์ง๋ฅผ ์ฌ์ ์ ๋ฏธ๋ฆฌ ๋ถ๋ฌ์ค๋ ๊ฒ์ ์๋ฏธํ๋ค.
๊ทผ๋ฐ ๋ถ๋ช Next.js๋ html์ ์ฌ์ ๋ ๋๋ง ํ๊ณ ์ดํ ์๋ฒ๊ฐ js ๋ฒ๋ค๋ง ๋๊ฑธ ๋ณด๋ด์ ํ์ด์ง ์ด๋ ์์ CSR ๋ฐฉ์์ผ๋ก ๋ธ๋ผ์ฐ์ ๊ฐ js๋ฅผ ์คํํด์ ๋น ๋ฅด๊ฒ ์ด๋๋๋ค๊ณ ํ๋๋ฐ Pre-Fetching์ ์ ํ์ํ๊ฑธ๊น?๐ค
Next.js๋ js ํ์ผ๋ค์ ํ์ด์ง๋ณ๋ก ๋ฏธ๋ฆฌ ์คํ๋ฆฌํ ํด์ ์ ์ฅ์ ํด๋๋ค. ์ฆ, ์ง๋ฌธ์์ ๋งํ ์๋ฒ๊ฐ js ๋ฒ๋ค๋ง ๋ ๊ฑธ ๋ณด๋ธ๋ค๋ ๊ฒ์ ํด๋น ํ์ด์ง์ ์คํ๋ฆฌํ ๋ ๋ฒ๋ค๋ง ํ์ผ์ด๋ผ๋ ๊ฒ์ด๋ค!
๋ง์ฝ ์ด๋ ๊ฒ ํ์ง ์๊ณ ๋ชจ๋ ํ์ด์ง์ js ํ์ผ์ ๋์ ธ์ฃผ๊ฒ ๋๋ฉด, ์ฉ๋์ด ์ปค์ง๊ณ ํ์ด๋๋ ์ด์ ์ด ๋ฆ์ด์ง๋ค. ๊ทธ๋ฌ๋ฉด ์ฌ์ฉ์๊ฐ ์ํธ์์ฉํ ์ ์๋ ์๊ฐ(TTI)์ด ๋ฆ์ด์ง๊ฒ ๋๋ค.
์ดํ, ๋ค๋ฅธ ํ์ด์ง๋ก ์ด๋ํ๊ฒ ๋ ๋ ํด๋น ํ์ด์ง๊ฐ ํ์๋ก ํ๋ js ๋ฒ๋ค๋ง์ ์๋ฒ๋ก๋ถํฐ ๋ ๋ฐ์์์ผ ํ๋๋ฐ ์ด๋ฐ ๊ฒฝ์ฐ, ํ์ด๋๋ ์ด์ ๋ฉด์์๋ ์ข์์ง๋ผ๋ ํ์ด์ง ์ด๋์ ๋๋ ค์ง๊ณ ๋นํจ์จ์ ์ด๊ฒ ๋๋ค.
์ด๋ฐ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด Pre-Fetchig์ด ์๋ ๊ฒ์ด๋ค.
ํ๋ฆฌํจ์นญ์ ์ด๊ธฐ ์ ์์ด ๋๋ ํ, ๋ค์ ํ์ด์ง ์ด๋์ด ์ด๋ฃจ์ด์ง๊ธฐ ์ ์ ์ฐ๊ฒฐ๋ ๋ชจ๋ ํ์ด์ง์ Js ๋ฒ๋ค ํ์ผ์ ๋ถ๋ฌ์ค๊ฒ ๋๋ค.

์ฐธ๊ณ ๋ก ํ๋ฆฌํจ์นญ์ dev ๋ชจ๋์์๋ ์ผ์ด๋์ง ์๋๋ค. production ๋ชจ๋์์ ํ์ธํ ์ ์๋ค! ๊ทธ๋ฌ๋ ๋น๋ํ๊ณ ์ด์ ํ๊ฒฝ์์ ํ์ธํด๋ณด์.

dev ๋ชจ๋์ผ ๋ ๋คํธ์ํฌ ํญ์ด๋ค. search์ book ํ์ด์ง์ js ํ์ผ๋ค์ด ๋ณด์ด์ง ์๋๋ค.

prod ๋ชจ๋์ผ ๋ ๋คํธ์ํฌ ํญ์ด๋ค. search์ book ํ์ด์ง์ js ํ์ผ๋ค์ด ๋ณด์ด๋ ๊ฒ์ ํ์ธํ๋ค. ํ๋ฆฌํจ์นญ์ด ์ ๋๋ก ์ด๋ฃจ์ด์ง ๊ฒ์ด๋ค.
์ฌ๊ธฐ์ ๋ ํ๋์ ๋ฌธ์ ๋ฅผ ๋ณผ ์ ์๋ค. ํ์ฌ ์ฝ๋๋ ๋ค์๊ณผ ๊ฐ๋ค.
export default function App({ Component, pageProps }: AppProps) {
const router = useRouter();
const onClickButton = () => {
router.push("/test");
};
return (
<>
<header>
<Link href="/">ํ</Link>
<Link href="/search">๊ฒ์</Link>
<Link href="/book/1">์ฑ
1</Link>
<div>
<button onClick={onClickButton}>/test ํ์ด์ง๋ก ์ด๋</button>
</div>
</header>
<Component {...pageProps} />
</>
);
}
์ฌ๊ธฐ์ Link๋ก ๊ฐ์ธ์ง ํ์ด์ง์ ๋ํด์๋ ํ๋ฆฌํจ์นญ์ด ์ ์ด๋ฃจ์ด์ง๋๋ฐ, /test ํ์ด์ง์ ๋ํด์๋ ํ๋ฆฌํจ์นญ์ด ์ด๋ฃจ์ด์ง์ง ์๋๋ค. ์ฆ, /test๋ก ํ์ด์ง ์ด๋ ์, test js ํ์ผ์ด ์๋ก ๋คํธ์ํฌ ํญ์ ๋ถ๋ฌ์์ง๋ค.
router.push ํ์ด์ง ์ฒ๋ฆฌํ ๊ฒ๋ ํ๋ฆฌํจ์นญ์ด ๋๊ฒ ํ๋ ค๋ฉด ์ด๋ป๊ฒ ํด์ผํ ๊น?
App ์ปดํฌ๋ํธ๊ฐ ๋ง์ดํธ๋์์ ๋(์ฒ์ ํ๋ฉด์ ๊ทธ๋ ค์ก์ ๋) ํ๋ฆฌํจ์น ํ๋๋ก ํ๋ฉด ๋๋ค.
์ฝ๋๋ ๋ค์ ๊ฑธ ๋ก์ง์ ์ถ๊ฐํ๋ฉด ๋๋ค.
useEffect(() => {
router.prefetch('/test');
}, [])
๋ฐ๋๋ก Link ํ๊ทธ์ prefetch๋ฅผ ํด์ ํ๊ณ ์ถ์๋ฉด?
prefetch={false} ์์ฑ์ ์ถ๊ฐํด์ฃผ๋ฉด ๋๋ค.

test js์ ํ๋ฆฌํจ์นญ, search js์ ํ๋ฆฌํจ์นญ ํด์ ๋ฅผ ํ์ธํ ์ ์๋ค!
API Routes
Next.js์์ API๋ฅผ ๊ตฌ์ถํ ์ ์๊ฒ ํด์ฃผ๋ ๊ธฐ๋ฅ
/pages/api ํด๋ ์์ ํ์ผ์ ๋ง๋ค๋ฉด, ๊ทธ ํ์ผ ๊ฒฝ๋ก = API ์๋ํฌ์ธํธ๊ฐ ๋๋ค.
์๋ฅผ ๋ค์ด
pages/api/hello.tsโGET /api/hellopages/api/user/index.tsโGET /api/userpages/api/user/[id].tsโGET /api/user/123์ด๋ฐ ์์ผ๋ก ๋์ ๋ผ์ฐํ ๋ ๊ฐ๋ฅํ๋ค.
๊ธฐ๋ณธ ํํ๋ ๋ค์๊ณผ ๊ฐ๋ค:
// pages/api/hello.ts
import type { NextApiRequest, NextApiResponse } from 'next';
export default function handler(req: NextApiRequest, res: NextApiResponse) {
// ์์ฒญ ๋ฉ์๋์ ๋ฐ๋ผ ๋ถ๊ธฐ
if (req.method === 'GET') {
return res.status(200).json({ message: 'Hello API' });
}
// ํ์ฉํ์ง ์๋ ๋ฉ์๋ ์ฒ๋ฆฌ
res.setHeader('Allow', ['GET']);
return res.status(405).end('Method Not Allowed');
}
CSS Module ๋ฐฉ์์ด๋?
ํ์ด์ง ๋ณ๋ก ํด๋์ค ๋ค์์ด ๊ฒน์ณ์ ๋ฐ์ํ ์ ์๋ ๋ฌธ์ (์คํ์ผ ์ถฉ๋)๋ฅผ ์๋์ผ๋ก ์ ๋ํฌํ ํด๋์ค ๋ค์์ผ๋ก ๋ณํํด์ค์ผ๋ก์จ ํด๊ฒฐํ ๋ฐฉ์์ด๋ค.
SSR, SSG, ISR
Next.js๋ ์ธ ๊ฐ์ง์ ์ฌ์ ๋ ๋๋ง ๋ฐฉ์์ด ์๋ค.
SSR(Server Side Rendering)
- ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ์ฌ์ ๋ ๋๋ง ๋ฐฉ์
- ์์ฒญ์ด ๋ค์ด์ฌ ๋๋ง๋ค ์ฌ์ ๋ ์ ๋ง์ ์งํ
SSR์ ํ์ด์ง ๋ด๋ถ์ ๋ฐ์ดํฐ๋ฅผ ํญ์ ์ต์ ์ผ๋ก ์ ์งํ ์ ์๋ค๋ ์ฅ์ ์ด ์์ง๋ง, ๋ฐ์ดํฐ ์์ฒญ์ด ๋ฆ์ด์ง ๊ฒฝ์ฐ ๋ชจ๋ ๊ฒ ๋ฆ์ด์ง๋ค๋ ๋จ์ ์ด ์๋ค.
export const getServerSideProps = async (
context: GetServerSidePropsContext
) => {
const id = context.params!.id;
const book = await fetchOneBook(Number(id));
return {
props: { book },
};
};
SSG(Static Site Generation)
- SSR์ ๋จ์ ์ ํด๊ฒฐํ๋ ์ฌ์ ๋ ๋๋ง ๋ฐฉ์
- ๋น๋ ํ์์ ํ์ด์ง๋ฅผ ๋ฏธ๋ฆฌ ์ฌ์ ๋ ๋๋ง ํด ๋
SSG ๋ฐฉ์์ ์ฌ์ ๋ ๋๋ง์ ๋ง์ ์๊ฐ์ด ์์๋๋๋ผ๋ ์ฌ์ฉ์์ ์์ฒญ์๋ ๋งค์ฐ ๋น ๋ฅธ ์๋๋ก ์๋ตํ๋ค๋ ์ฅ์ ์ด ์๋ค.
ํ์ง๋ง, ๋งค๋ฒ ๋๊ฐ์ ํ์ด์ง๋ง ์๋ตํ๊ธฐ์ ์ต์ ๋ฐ์ดํฐ ๋ฐ์์ ์ด๋ ต๋ค๋ ๋จ์ ์ด ์๋ค.
์ ๋๋ก๋ SSG ๋์์ ํ์ธํ๊ธฐ ์ํด ๊ผญ ์ด์ ํ๊ฒฝ์์ ํ ์คํธ๋ฅผ ํด๋ณด์!
export const getStaticProps = async () => {
const [allBooks, recoBooks] = await Promise.all([
fetchBooks(),
fetchRandomBooks(),
]);
return {
props: {
allBooks,
recoBooks,
},
};
};
๋ง์ฝ ๋์ ๊ฒฝ๋ก๋ฅผ ๊ฐ์ง ํ์ผ์ SSG๋ก ๋ถ๋ฌ์จ๋ค๋ฉด ์ด๋ป๊ฒ ํด์ผ ํณ๊น? ์์ฒ๋ผ getStaticProps๋ฅผ ์ด์ฉํด์ ์คํ์ ํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ด ์๋ฌ๊ฐ ๋๋ค.

๋น๋ํ์์ ์ฌ์ ๋ ๋๋ง์ ์งํํ๊ธฐ ๋๋ฌธ์ ์ด๋ค ๊ฒฝ๋ก๋ค์ด ์กด์ฌํ ์ ์๋์ง ์ ์ ์๋๋ก ์ค์ ํ๋ ๊ณผ์ ์ด ํ์ํ ๊ฒ์ด๋ค.
์ด ์ค์ ํ๋ ์ญํ ์ ํจ์๊ฐ ๋ฐ๋ก getStaticPaths์ด๋ค.
export const getStaticPaths = () => {
return {
paths: [
{ params: { id: "1" } },
{ params: { id: "2" } },
{ params: { id: "3" } },
],
fallback: true,
// false - 404 NotFound
// blocking - SSR ๋ฐฉ์
// true - SSR ๋ฐฉ์ + ๋ฐ์ดํฐ๊ฐ ์๋ ํด๋ฐฑ ์ํ์ ํ์ด์ง๋ถํฐ ๋ฐํ
};
};
export const getStaticProps = async (context: GetStaticPropsContext) => {
const id = context.params!.id;
const book = await fetchOneBook(Number(id));
return {
props: { book },
};
};
์ฌ๊ธฐ์ fallback ์ต์ ์ ๋ฌด์์ผ๊น?
fallback์ ์กด์ฌํ์ง ์๋ ๊ฒฝ๋ก๋ก ๋ค์ด๊ฐ์ ๋ ์ด๋ป๊ฒ ๋์ฒดํ ์ง ์ค์ ํ๋ ์ต์ ์ด๋ค.
false์ผ ๋๋ 404 NotFound๋ฅผ,
true์ผ ๋๋ SSR ๋ฐฉ์์ผ๋ก ์
๋ฐ์ดํธ๋ฅผ ์งํํ๋, ์ฌ์ฉ์๊ฐ ๋ฐ์์ ๋น ๋ฅด๊ฒ ํ์ธํ ์ ์๊ฒ ๋ฐ์ดํฐ๊ฐ ์๋ ํด๋ฐฑ ์ํ ํ์ด์ง๋ถํฐ ๋ฐํ,
blocking์ SSR ๋ฐฉ์์ผ๋ก ์
๋ฐ์ดํธ๋ง ์งํํ๋ ์ต์
์ด๋ค.
ISR(Static Site Generation)
SSG ๋ฐฉ์์ผ๋ก ์์ฑ๋ ์ ์ ํ์ด์ง๋ฅผ ์ผ์ ์๊ฐ์ ์ฃผ๊ธฐ๋ก ๋ค์ ์์ฑํ๋ ๋ฐฉ์์ด๋ค.
์ฆ, ๋น ๋ฅธ ์๋ต์ ํด์ฃผ๋ SSG์ ์ฅ์ ๊ณผ ์ต์ ๋ฐ์ดํฐ ๋ฐ์์ด ๊ฐ๋ฅํ SSR์ ์ฅ์ ์ ๋ชจ๋ ์ง๋ ๋ฐฉ์์ด๋ผ๊ณ ํ ์ ์๋ค.
๊ทผ๋ฐ ๋ง์ฝ ์ฌ์ฉ์๊ฐ ๊ฒ์๊ธ ์์ ๊ณผ ๊ฐ์ ์ด๋ค ํน์ ํ ํ๋ ์ดํ์ ์ฆ์ ์ ๋ฐ์ดํธ๊ฐ ๋์ด์ผ ํ ๋๋ ์ด๋ป๊ฒ ํด์ผ ํ ๊น?
์ด๋ On-Demand ISR๋ก Next.js ์๋ฒ์ ์ง์ revalidate ์์ฒญ ๋ ๋ ค์ฃผ๋ฉด ๋๋ค.
import { NextApiRequest, NextApiResponse } from "next";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
await res.revalidate("/");
return res.json({ revalidate: true });
} catch (err) {
console.log(err);
res.status(500).send("Revalidation Failed");
}
}
Page Router์ ์ฅ๋จ์
ํ์ด์ง ๋ผ์ฐํ ์ ์ฅ์ ์ ๋จผ์ ์ ๋ฆฌํด๋ณด์.
| ์ฅ์ | ๋จ์ |
|---|---|
| ํ์ผ ์์คํ ๊ธฐ๋ฐ ๊ฐํธํ ํ์ด์ง ๋ผ์ฐํ ์ ๊ณต | ๋ฒ๊ฑฐ๋ก์ด ํ์ด์ง๋ณ ๋ ์ด์์ ์ค์ |
| ๋ค์ํ ๋ฐฉ์์ ์ฌ์ ๋ ๋๋ง ์ ๊ณต | ํจ์ด์ง ์ปดํฌ๋ํธ์ ์ง์ค๋๋ ๋ฐ์ดํฐ ํจ์นญ |
| - | ๋ถํ์ํ ์ปดํฌ๋ํธ๋ JS ๋ฒ๋ค์ ํฌํจ |
์ฌ๊ธฐ์ ํ์ด์ง ๋ผ์ฐํฐ์ ๋จ์ ์ ๋ณด์ํ App Router๊ฐ ๋ฑ์ฅํ๊ฒ ๋๋ค.
๋ค์์ App Router์ ๋ํด ์ ๋ฆฌํ๊ฒ ์ต๋๋น๐๐ป