Next.js ํ”„๋ ˆ์ž„์›Œํฌ ๊ตฌ์กฐ

  • Pages: url๊ณผ ๋งค์นญ๋˜๋Š” ๊ฒƒ๋“ค

Date Fetching

  • SSR: ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง์œผ๋กœ, getServerSideProps ํ•จ์ˆ˜๋ฅผ ํ™œ์šฉํ•ด ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. ์„œ๋ฒ„์—

์š”์ฒญ์ด ์ƒ๊ธธ ๋•Œ๋งˆ๋‹ค ์™ธ๋ถ€ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•ด์„œ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋‹ค.

  • CSR: ํด๋ผ์ด์–ธํŠธ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง์œผ๋กœ, ์ผ๋ฐ˜ React๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™๋‹ค.
  • SSG: ์ •์  ์‚ฌ์ดํŠธ ์ œ๋„ˆ๋ ˆ์ด์…˜, ๋นŒ๋“œ ํƒ€์ž„ ๋•Œ ๋ฏธ๋ฆฌ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์™€ ํ™”๋ฉด์„ ๊ทธ๋ ค๋†“๋Š”๋‹ค. yarn dev๋กœ ์‹คํ–‰ํ•œ ํ™˜๊ฒฝ์—์„œ๋Š”

SSR๊ณผ ๋™์ผํ•˜๊ฒŒ ๋™์ž‘ํ•˜๊ณ  ๋นŒ๋“œ ํ›„ ์‹คํ–‰ํ–ˆ์„ ๋•Œ ์ˆœ๊ธฐ๋Šฅ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค. ๋นŒ๋“œ ์‹œ ๊ฐ€์ ธ์˜จ ๋ฐ์ดํ„ฐ๋กœ ๊ทธ๋ ค์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ์•„๋ฌด๋ฆฌ ์ƒˆ๋กœ๊ณ ์นจ์„ ํ•ด๋„ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๊ฑฐ๋‚˜ ๋ฆฌํŒจ์น˜ ๋˜์ง€ ์•Š๋Š”๋‹ค.

  • ISR: getStaticProps ์•ˆ์—์„œ ๋™์ž‘ํ•˜๋Š”๊ฑฐ์ง€๋งŒ ํŠน์ • ์ฃผ๊ธฐ๋ฅผ ์ง€์ •ํ•ด์ฃผ๋ฉด ๊ทธ ์ฃผ๊ธฐ๋งˆ๋‹ค ๋ฆฌ์ œ๋„ˆ๋ ˆ์ด์…˜ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธํ•  ์ˆ˜ ์žˆ๋‹ค.

SSR์€ ๋งค๋ฒˆ ์„œ๋ฒ„๋กœ ํ˜ธ์ถœํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์„œ๋ฒ„ ๋ถ€ํ•˜๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์–ด SSG + ISR์„ ๋™์‹œ์— ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•œ๋‹ค.

SSR VS SSG

SSR์€ ๋งค๋ฒˆ ์„œ๋ฒ„์— ์š”์ฒญ์ด ์ด๋ฃจ์–ด์ง€๋ฏ€๋กœ ์„œ๋ฒ„ ๋ถ€ํ•˜๊ฐ€ ์˜ฌ ์ˆ˜ ์žˆ๋‹ค. ๋”ฐ๋ผ์„œ ๋นŒ๋“œ ์‹œ ํŒŒ์ผ์„ ์ƒ์„ฑํ•˜๋Š” SSG๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์„ฑ๋Šฅ ์ธก๋ฉด์—์„œ ๋” ์ข‹์€๋ฐ ๋‘๊ฐ€์ง€ ์ค‘ ๊ฒฐ์ •ํ•˜๋Š” ๊ฐ€์žฅ ํฐ ์š”์ธ์€ ์‚ฌ์šฉ์ž์˜ ์š”์ฒญ ์—†์ด ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ๋Š” ๋ฐ์ดํ„ฐ์ธ๊ฐ€. ํŒ๋‹จํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ๋งŒ์•ฝ ์‚ฌ์šฉ์ž ์š”์ฒญ ์—†์ด ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ผ๋ฉด ๋นŒ๋“œ ํƒ€์ž„ ๋•Œ ์ƒ์„ฑํ•˜๋Š” SSG ์‚ฌ์šฉ์„ ๊ถŒ์žฅํ•˜๊ณ , ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋ฉด SSR์„ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

Layout

pages/_app.js๋ฅผ ํ™œ์šฉํ•ด์„œ ํŽ˜์ด์ง€์— ๊ณตํ†ต์ ์œผ๋กœ ๋ณด์—ฌ์ง€๋Š” ๋ ˆ์ด์•„์›ƒ๋“ค์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋ฉ”์ธ Layout๊ณผ ๋ณ„๊ฐœ๋กœ SubLayout๋„ ๋งŒ๋“ค์–ด GetLayout ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ๋ ˆ์ด์•„์›ƒ์„ ์ง€์ •ํ•ด์ค„ ์ˆ˜ ์žˆ๋‹ค.

Routing

React๋Š” ๋ณ„๋„๋กœ react-router ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ด์ฃผ์–ด์•ผ ํ•˜์ง€๋งŒ Next.js๋Š” file-system ๊ธฐ๋ฐ˜์˜ ๋ผ์šฐํ„ฐ๋ฅผ ์ œ๊ณตํ•œ๋‹ค. /src/pages ๋˜๋Š” /pages ๋””๋ ‰ํ† ๋ฆฌ ๋‚ด๋ถ€์— ๋ผ์šฐํ„ฐ๋ฅผ ์ง€์ •ํ•ด์ฃผ๋ฉด ๋œ๋‹ค.

  • /src/pages์™€ /pages๊ฐ€ ๋ชจ๋‘ ์กด์žฌํ•œ๋‹ค๋ฉด /pages๊ฐ€ ์šฐ์„ ์ˆœ์œ„๊ฐ€ ๋†’๋‹ค.
  • [id], [id].js์™€ ๊ฐ™์€ ํ˜•ํƒœ๋กœ ์™€์ผ๋“œ ์นด๋“œ๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ํŒŒ์ผ ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๋””๋ ‰ํ† ๋ฆฌ๋ช…์œผ๋กœ๋„ ์„ค์ • ๊ฐ€๋Šฅ.
  • Slug - ๋‹ค์–‘ํ•œ ์œ„๊ณ„์˜ Dynamic Paths ์ œ
  • [...id].js ํ˜•ํƒœ์˜ ํŒŒ์ผ์€ ์™€์ผ๋“œ ์นด๋“œ๋ฅผ ์—ฌ๋Ÿฌ ๋ށ์Šค๋กœ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋‹ค. e.g. //localhost:3000/1/2/3
  • [[id]].js ํ˜•ํƒœ๋Š” ์™€์ผ๋“œ ์นด๋“œ๋ฅผ ๊ตณ์ด ์ž…๋ ฅํ•˜์ง€ ์•Š์•„๋„ ๊ธฐ๋ณธ ํŽ˜์ด์ง€๋กœ ๋กœ๋”ฉ๋œ๋‹ค. e.g. //localhost:3000
  • router๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ์„ธ๊ฐ€์ง€ ๋ฐฉ๋ฒ•
    1. location.replace("url") - ๋กœ์ปฌ state๊ฐ€ ์œ ์ง€๋˜์ง€ ์•Š์Œ (๋ฆฌ๋ Œ๋”๋ง)
    2. router.push(url) - ๋กœ์ปฌ state ์œ ์ง€ / data fetching ์ƒˆ๋กœ ์ผ์–ด๋‚จ
    3. router.push(url, as, { shallow: true }) - ๋กœ์ปฌ state ์œ ์ง€ / data fetching ์ผ์–ด๋‚˜์ง€ ์•Š์Œ
  • SSG๋กœ ์ƒ์„ฑํ•  ๋ชฉ๋ก์€ getStaticPaths ํ›…์„ ํ†ตํ•ด ๊ฐ€์ ธ์˜จ๋‹ค.
  • getStaticPaths์˜ fallback ์—ญํ• ์€ ๋นŒ๋“œ ํƒ€์ž„ ๋•Œ ์ƒ์„ฑ๋˜์ง€ ์•Š์€ page์— ๋Œ€ํ•œ ์ฒ˜๋ฆฌ๋ฅผ ์ •ํ•œ๋‹ค.
    • blocking: ์•ˆ๊ทธ๋ฆฌ๊ณ  ์žˆ๋‹ค๊ฐ€ ๋ฐ์ดํ„ฐ๊ฐ€ ์žˆ์„๋•Œ ๊ธฐ๋‹ค๋ ธ๋‹ค๊ฐ€ ์‹คํ–‰
    • false: ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๊ณ  404
    • true: fallback ๋™์ž‘์œผ๋กœ ๋กœ๋” ๋“ฑ์„ ๋ณด์—ฌ์คฌ๋‹ค๊ฐ€ ์‹คํ–‰

Shallow Routing

  • Dynamic Routes: Slug๋ฅผ ์‚ฌ์šฉํ•ด ๊ตฌํ˜„
  • ๋‹ค์ค‘ slug: [user]/[info].js / [โ€ฆslug].js
  • ์˜ต์…”๋„ slug: [[โ€ฆslug]].js
  • Shallow Routing: router.push(url, undefined, { shallow: true })

API Routes

  • API Routes๋Š” Routing๊ณผ ๋™์ผํ•˜๊ฒŒ ํŒŒ์ผ๊ธฐ๋ฐ˜์ด๊ณ  ๋‹ค์ด๋‚˜๋ฏน ํŒจ์Šค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. (์™€์ผ๋“œ์นด๋“œ)
  • Response
    • res.status(code) - API ์‘๋‹ต ๊ฒฐ๊ณผ๋ฅผ ์ฝ”๋“œ๋กœ ๋ฆฌํ„ดํ•  ์ˆ˜ ์žˆ๋‹ค.
    • res.json(body) - ์‘๋‹ต ๊ฒฐ๊ณผ๋กœ ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.
    • res.redirect(code, url) - ์‘๋‹ต ๊ฒฐ๊ณผ์— ๋Œ€ํ•œ ์ฝ”๋“œ๋ฅผ ์ฒซ ๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ์ฃผ๊ณ , ๋‘๋ฒˆ์งธ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋‹ค์Œ์œผ๋กœ ํ˜ธ์ถœํ•  API ์ฃผ์†Œ๋ฅผ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ๋‹ค.
    • res.send(body) - json()๊ณผ ์œ ์‚ฌํ•˜์ง€๋งŒ string/object/Buffer ๋“ฑ ๋‹ค์–‘ํ•œ ํ˜•ํƒœ์˜ ๊ฐ’์„ ๋ฆฌํ„ดํ•  ์ˆ˜ ์žˆ๋‹ค.

์กฐ๊ธˆ ๋” ์‹ฌํ™” ๋‚ด์šฉ

Next.js๋Š” Rust๋กœ ๋งŒ๋“ค์–ด์ง„ SWC ์ปดํŒŒ์ผ๋Ÿฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค. SWC ์ปดํŒŒ์ผ๋Ÿฌ๋Š” javascript ์ฝ”๋“œ๋ฅผ transformํ•˜๊ณ  minifyํ•˜๋Š” ์—ญํ• ์„ ํ•œ๋‹ค. ํŠธ๋žœ์ŠคํŒŒ์ผ๋Ÿฌ๋ฅผ ํ•˜๋Š” Babel๊ณผ minify์˜ Terser๋ฅผ ๋Œ€์ฒดํ•˜๊ณ  ์žˆ๋‹ค.

๋ฐ”๋ฒจ์ด๋ž€?
๋ฐ”๋ฒจ์€ ํŠธ๋žœ์ŠคํŒŒ์ผ๋Ÿฌ๋กœ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ์ตœ์‹  ๋ฌธ๋ฒ•์„ ๊ตฌ ๋ฒ„์ „ ๋ธŒ๋ผ์šฐ์ €์—์„œ๋„ ๋™์ž‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ์น˜ํ™˜ํ•ด์ฃผ๋Š” ์—ญํ• 

Preview Mode

getStaticProps๋Š” ๋นŒ๋“œ ํƒ€์ž„์— ์‹คํ–‰๋˜๋Š” ํ›…์ด์ง€๋งŒ Preview Mode๋กœ ๋‘ ๊ฐ€์ง€ ์ฟ ํ‚ค๊ฐ€ ์ƒ์„ฑ๋œ๋‹ค๋ฉด Request time์—๋„ getStaticProps ํ›…์„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

Dynamic Import

React.lazy ๊ธฐ๋Šฅ์„ ํ™•์žฅํ•˜์—ฌ Next.js์—์„œ ์ปดํฌ๋„ŒํŠธ๋ฅผ Lazy loadํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

// good
import Button from 'components/Button';

// better
import dynamic from 'next/dynamic';

const Button = dynamic(() => { import('components/Button') }, {
  loading: () => <div>Loading...</div>
})

Automatic Static Optimization

์ž๋™์ ์œผ๋กœ ์ •์ ์ธ ๊ฒƒ์€ ์ตœ์ ํ™”๋ฅผ ์ง„ํ–‰ํ•˜๋Š” ๊ธฐ๋Šฅ, ์ •์  ํŽ˜์ด์ง€๋Š” .html ํŒŒ์ผ๋กœ ์ƒ์„ฑํ•˜๊ณ  ์š”์ฒญ์— ๋งž์ถฐ ๋™์ž‘ํ•˜๋Š” ํŽ˜์ด์ง€๋Š” .js ํŒŒ์ผ๋กœ ๋นŒ๋“œํ•œ๋‹ค. getInitialProps๋‚˜ getServerSideProps๊ฐ€ ์žˆ๋‹ค๋ฉด .jsํŒŒ์ผ๋กœ ๋นŒ๋“œ

Router์˜ query๊ฐ’

Router์˜ query ๊ฐ’์€ CSR์˜ ๊ฒฝ์šฐ ์ดˆ๊ธฐ์— undefined์ผ ์ˆ˜ ์žˆ๋‹ค. ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ๋ Œ๋”๋ง์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ hydration ์ดํ›„ ์‹ค์ œ ์ž…๋ ฅ๋œ query ๊ฐ’๋งŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

Static HTML Export

next export ๋ช…๋ น์–ด๋ฅผ ํ†ตํ•ด ์˜๋„์ ์œผ๋กœ ์ •์  ํŒŒ์ผ๋กœ export ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ Next.js์—์„œ ์ œ๊ณตํ•˜๋Š” ๊ธฐ๋Šฅ ์ค‘ Node ์„œ๋ฒ„๊ฐ€ ์žˆ์–ด์•ผ๋งŒ ํ•˜๋Š” ๊ฒƒ๋“ค(Image, API Routes)์€ ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ•œ๋‹ค.

Custom App

  • Persisting layout between page changes
    • ํŽ˜์ด์ง€ ๋ณ€๊ฒฝ ์‹œ์—๋„ ๊ณ ์ •๋œ ๋ ˆ์ด์•„์›ƒ์„ ์‚ฌ์šฉํ•  ๋•Œ
  • Keeping state when navigating pages
    • ํŽ˜์ด์ง€๊ฐ€ navigate ๋˜์–ด๋„ ์œ ์ง€ํ•˜๊ณ  ์‹ถ์€ ์ƒํƒœ๊ฐ€ ์žˆ์„ ๋•Œ
  • Inject additional data into pages
    • ํŽ˜์ด์ง€์— ์ถ”๊ฐ€์ ์ธ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ์ž…ํ•˜๊ณ  ์‹ถ์„ ๋•Œ
  • [Add global CSS](https://nextjs.org/docs/basic-features/built-in-css-support#adding-a-global-stylesheet

์›น ์„ฑ๋Šฅ ์ธก์ • (Web Vitals)

  • Largest Contentful Paint(์ตœ๋Œ€ ์ฝ˜ํ…์ธ ํ’€ ํŽ˜์ธํŠธ, LCP): ๋กœ๋”ฉ ์„ฑ๋Šฅ ์ธก์ •, ์‚ฌ์šฉ์ž๊ฐ€ ์˜๋ฏธ์žˆ๋Š” ์ฝ˜ํ…์ธ ๋ฅผ ๋ณผ ๋•Œ๊นŒ์ง€ ์–ผ๋งˆ๋งŒํผ์˜ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆฌ๋Š”๊ฐ€, ์šฐ์ˆ˜ํ•œ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์ œ๊ณตํ•˜๋ ค๋ฉด 2.5์ดˆ ์ด๋‚ด์— LCP๊ฐ€ ๋ฐœ์ƒํ•ด์•ผ ํ•œ๋‹ค. (์ ์„์ˆ˜๋ก ์ข‹์Œ)
  • First Input Delay(์ตœ์ดˆ ์ž…๋ ฅ ์ง€์—ฐ, FID): ์ƒํ˜ธ์ž‘์šฉ ์ธก์ •, ์šฐ์ˆ˜ํ•œ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์ œ๊ณตํ•˜๋ ค๋ฉด ํŽ˜์ด์ง€ ๋‹น 100๋ฐ€๋ฆฌ์ดˆ ์ดํ•˜ (์ ์„์ˆ˜๋ก ์ข‹์Œ)
  • Cumulative Layout Shift(๋ˆ„์  ๋ ˆ์ด์•„์›ƒ ์‹œํ”„ํŠธ, CLS): ์‹œ๊ฐ์  ์•ˆ์ •์„ฑ ์ธก์ •, ์šฐ์ˆ˜ํ•œ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์ œ๊ณตํ•˜๋ ค๋ฉด ํŽ˜์ด์ง€์—์„œ 0.1 ์ดํ•˜ ์œ ์ง€ (์ ์„์ˆ˜๋ก ์ข‹์Œ)
  • (์ถ”๊ฐ€) Total Block Time(์ด ์ฐจ๋‹จ ์‹œ๊ฐ„, TBT): ์‚ฌ์šฉ์ž๊ฐ€ ์ธํ„ฐ๋ž™์…˜ํ•˜๊ธฐ ๊นŒ์ง€ ๋ธ”๋Ÿญํƒ€์ž„์ด ์–ด๋А์ •๋„ ๋ฐœ์ƒํ•˜๋Š”์ง€ ์ธก์ •