๐Ÿ‘‹ ์†์— ์ต๋Š” Next.js Part.1 ์ •๋ฆฌํ•˜๊ธฐ

Haizelยท2024๋…„ 3์›” 19์ผ
post-thumbnail

๐ŸŒฑ ์ธํ”„๋Ÿฐ์—์„œ [์†์— ์ต๋Š” Next.js] Part 1 - ๊ณต์‹ ๋ฌธ์„œ ํ›‘์–ด๋ณด๊ธฐ๋ฅผ ์ฝ์€ ํ›„ ๊ฐ•์˜ ๋‚ด์šฉ์„ ์ •๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.
ํ•„์š”ํ•œ ๋ถ€๋ถ„์— ๋Œ€ํ•ด์„œ๋งŒ ์ •๋ฆฌํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ถ€์กฑํ•œ ๋ถ€๋ถ„์ด ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

01. App Router


  • App ํด๋” ์•„๋ž˜ ๋‘๋ฉด ์•Œ์•„์„œ ํŒŒ์ผ ์‹œ์Šคํ…œ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ผ์šฐํŒ…๋˜๋Š” ๊ฒƒ์„ ๋งํ•œ๋‹ค.

02. ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ


โ‘  ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ = React Server Component(RSC)

  • RSC๋Š” ๋ฆฌ์•กํŠธ 18์—์„œ ๋„์ž…๋œ ๊ธฐ์ˆ ๋กœ, ์ด๋ฅผ Next์—์„œ ์‚ฌ์šฉํ•  ๋•Œ "์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ"๋ผ๊ณ  ๋ถ€๋ฅด๋Š” ๊ฒƒ์ด๋‹ค.
  • Next.js์—์„œ ๊ธฐ๋ณธ์€ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์ด๋‹ค.

โ‘ก ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ•„์š”ํ•œ ์ด์œ , ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์˜ ์žฅ์ 

  1. ๋ฐ์ดํ„ฐ ํŒจ์นญ์—์„œ ์„ฑ๋Šฅ์ƒ ์ด์  : ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋‹ˆ๊นŒ ๋น ๋ฅด๋‹ค. ๋‹น์—ฐํ•˜๋‹ค. ์™œ๋ƒํ•˜๋ฉด ๋ฌผ๋ฆฌ์ ์œผ๋กœ ๋ณด๋ฉด ์„œ๋ฒ„๊ฐ€ ํด๋ผ์ด์–ธํŠธ๋ณด๋‹ค ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ๋” ๊ฐ€๊นŒ์šฐ๋‹ˆ๊นŒ
  2. ๋ณด์•ˆ ์ƒ ์ด์  : ๋ธŒ๋ผ์šฐ์ €์—์„œ ๊ด€๋ฆฌํ•ด์„  ์•ˆ๋˜๋Š” ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ ํด๋ผ์ด์–ธํŠธ์— ๋‘์ง€ ์•Š๊ณ  ์„œ๋ฒ„์—๋งŒ ๋‘๋‹ˆ๊นŒ
  3. ์บ์‹ฑ : ๋ Œ๋”๋ง๋œ ๊ฐ’์„ ์บ์‹ฑํ•ด๋‘๊ณ  ์žˆ๋‹ค ์žฌ์‚ฌ์šฉํ•˜๋‹ˆ๊นŒ ์—ฌ๋Ÿฌ ๋ฒˆ ์š”์ฒญ์ด ์™€๋„ ์ด๋ฏธ ๋งŒ๋“ค์–ด์ง„ ๊ฒฐ๊ณผ๋ฅผ ๋‚ด๋ ค์ฃผ๋ฉด ๋˜๋‹ˆ๊นŒ
  4. ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ฒˆ๋“ค ํฌ๊ธฐ ๊ฐ์†Œ : ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์˜ ๊ฒฝ์šฐ, ์„œ๋ฒ„์—์„œ ๋ Œ๋”๋ง๊นŒ์ง€ ๋‹ค ๋œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋‚ด๋ ค์ฃผ๋ฏ€๋กœ ํ•˜์ด๋“œ๋ ˆ์ด์…˜์— ํ•„์š”ํ•œ ์ž๋ฐ”์Šคํฌ๋ฆฝ ํŒŒ์ผ์ด ์ค„์–ด๋“ค๊ฒŒ ๋œ๋‹ค.

03. ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ


  • ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์ƒ๋‹จ์— "use client"๋ผ๊ณ  ๋ช…์‹œ ํ•„์š”

๐Ÿšจ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ ์‚ฌ์šฉ ์‹œ ์ฃผ์˜ํ•  ์ 
โ†’ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์—์„œ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ importํ•  ์ˆ˜ ์—†๋‹ค.
โ†’ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์—์„œ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์“ฐ๊ณ  ์‹ถ์„ ๋•, props๋กœ ๋„˜๊ฒจ์ฃผ์–ด์•ผ ํ•œ๋‹ค.

"use client"

import { useState } from 'react';

export default function ExampleClientComponent({
	children,
} : {
	children : React.ReactNode
}) {
	const [count, setCount] = useState(0);

	return (
	<button onClick={() => setCount(count+1)}>
	{children}
	</button>
	)
}

// -> ์ด์ฒ˜๋Ÿผ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์—์„œ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์“ฐ๊ณ  ์‹ถ๋‹ค๋ฉด children๊ณผ ๊ฐ™์ด props๋กœ ์ „๋‹ฌํ•ด ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

โ‘  ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋Š” ํด๋ผ์ด์–ธํŠธ์—์„œ๋งŒ ๋ Œ๋”๋ง๋˜๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์•„๋‹ˆ๋‹ค.

  • ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋Š” ๊ธฐ์กด์˜ Next.js๊ฐ€ pre-rendering์ด๋ผ๋Š” ๋ฐฉ์‹์œผ๋กœ ์ œ๊ณตํ•˜๋˜ ๋ฐฉ์‹์œผ๋กœ ์—ฌ์ „ํžˆ ๋ Œ๋”๋งํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ = ํด๋ž˜์‹ ์ปดํฌ๋„ŒํŠธ๋ผ๊ณ  ์ดํ•ดํ•˜๋ฉด ์ข€ ๋” ์‰ฝ๊ฒŒ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋„ SSR, SSG ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ๋ Œ๋”๋ง ๊ฐ€๋Šฅํ•˜๋‹ค.
    โ†’ ์ฆ‰ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ, ์„œ๋ฒ„์—์„œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฏธ๋ฆฌ ๋ Œ๋”๋งํ•ด ๋‘˜ ์ˆ˜ ์žˆ๋‹ค.

โ‘ก ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๊ฐ€ SSR์ด ๋œ๋‹ค๋ฉด, RSC๋ž‘ ๋ญ๊ฐ€ ๋‹ค๋ฅธ ๊ฑธ๊นŒ?

  • ์•ž์„œ ๋งํ–ˆ๋“ฏ์ด ์„œ๋ฒ„ ํด๋ผ์ด์–ธํŠธ๋Š” "๋ฐ์ดํ„ฐ ํŒจ์นญ, ๋ณด์•ˆ, ์บ์‹ฑ, ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ ๋ฒˆ๋“ค ํฌ๊ธฐ ์ค„์–ด๋“ฌ"์˜ ๊ฐ•์ ์ด ์žˆ๋‹ค.

โ‘ข 'use client' ์ตœ๋Œ€ํ•œ ๋’ค๋กœ ๋ฏธ๋ฃจ๊ธฐ

  • ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋Š” ์ œ์ผ ๋ง๋‹จ์—!
  • Next.js์—์„œ๋Š” ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ตœ๋Œ€ํ•œ ํŠธ๋ฆฌ์˜ ๋์— ์œ„์น˜ํ•˜๋„๋ก ๋‘๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

๐Ÿง ์™œ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํŠธ๋ฆฌ์˜ ๋์— ์œ„์น˜ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์„๊นŒ?
โ†’ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ตœ๋Œ€ํ•œ ํ™œ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ ์ด๋‹ค.

  • ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํŠธ๋ฆฌ์˜ ์ƒ์œ„์— ์žˆ์œผ๋ฉด, ํ•˜์œ„์— ์žˆ๋Š” ์ปดํฌ๋„ŒํŠธ๋Š” ์ž์‹ ์š”์†Œ๋กœ ์ „๋‹ฌ๋˜์ง€ ์•Š์€ ์ด์ƒ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ™œ์šฉํ•˜๊ธฐ ์–ด๋ ต๋‹ค.
  • ๋•Œ๋ฌธ์— ๊ฐ€๋Šฅํ•˜๋ฉด ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํŠธ๋ฆฌ์˜ ๋์— ๋‘๋ฉด ์•ฑ ์ „์ฒด์—์„œ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์˜ ๋น„์ค‘์„ ๋Š˜๋ฆด ์ˆ˜ ์žˆ๋‹ค.
  • ์ด๋Š” ๊ฒฐ๊ณผ์ ์œผ๋กœ ๋ฒˆ๋“ค ํฌ๊ธฐ์˜ ๊ฐ์†Œ๋กœ ์ด์–ด์ ธ ์ „์ฒด์ ์ธ ์„ฑ๋Šฅ ํ–ฅ์ƒ์œผ๋กœ ์ด์–ด์ง„๋‹ค.

โ‘ฃ ์ •๋ฆฌํ•˜์ž๋ฉด,

  1. ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋Š” ์„œ๋ฒ„์—์„œ ๋ Œ๋”๋ง ๋˜์–ด์ง„ React ์ปดํฌ๋„ŒํŠธ์ด๊ณ , ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋Š” Pre-render์ด ๊ฐ€๋Šฅํ•œ ๊ธฐ์กด์˜ ์ปดํฌ๋„ŒํŠธ์ด๋‹ค.
  • ๋”ฐ๋ผ์„œ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์™€ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ ์ ˆํžˆ ์กฐํ•ฉํ•ด ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค.
  • ์ด๋•Œ ๋˜๋„๋ก์ด๋ฉด ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋Š” ํŠธ๋ฆฌ์˜ ๋์— ์œ„์น˜ํ•˜๋„๋ก ๋…ธ๋ ฅํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.
  1. ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋Š” ์„œ๋ฒ„์—์„œ ๋ฏธ๋ฆฌ ๋ Œ๋”๋ง๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ „์ฒด์ ์ธ JS ๋ฒˆ๋“ค ํฌ๊ธฐ๊ฐ€ ๊ฐ์†Œํ•˜๋ฏ€๋กœ First View๋ฅผ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, TTI์—์„œ๋„ ๊ฐ„์ ‘์ ์ธ ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ๋‹ค.

โœ๏ธ TTI(Time To Interact)

  • ์‚ฌ์šฉ์ž๊ฐ€ ํด๋ฆญ์„ ํ•˜๊ฑฐ๋‚˜ ์ธํ„ฐ๋ ‰์…˜์ด ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋˜๋Š”๋ฐ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„

โœ๏ธ TTV(Time To View)

  • ์‚ฌ์šฉ์ž๊ฐ€ ์›น์‚ฌ์ดํŠธ๋ฅผ ๋ณด๋Š”๋ฐ๊นŒ์ง€ ๊ฑธ๋ฆฌ๋Š” ์‹œ๊ฐ„

04. Next.js์˜ ์˜คํ•ด์™€ ์ง„์‹ค


โ‘  SSR ๋ฐฉ์‹๊ณผ === ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ ๋ฐฉ์‹?

SSR ๋ฐฉ์‹์€ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์™€ ๋‹ค๋ฅธ ๋ฐฉ์‹์ด๋‹ค.
โ†’ ์„œ๋ฒ„์—์„œ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•œ๋‹ค๋Š” ์ ์—์„  ๋™์ผํ•˜์ง€๋งŒ, ๋ Œ๋”๋ง์˜ ๊ฒฐ๊ณผ๊ฐ’๊ณผ ๋™์ž‘ ๋ฐฉ์‹์ด ๋‹ค๋ฅด๋‹ค.

โถ SSR

  • Pre-rendering ๋ฐฉ์‹์œผ๋กœ ์„œ๋ฒ„์—์„œ HTML์„ ๋งŒ๋“ค์–ด๋‚ธ๋‹ค. ์ดํ›„ ํด๋ผ์ด์–ธํŠธ๋กœ ์ „๋‹ฌ๋œ JS ๋ฒˆ๋“ค ํŒŒ์ผ๊ณผ ์ˆ˜ํ™”๋˜๋Š” ๊ณผ์ •์„ ๊ฑฐ์ณ React ์ปดํฌ๋„ŒํŠธ๋กœ ์‚ฌ์šฉ๋œ๋‹ค.
  • ์ด๊ฒƒ์ด ๊ธฐ์กด์˜ React ์ปดํฌ๋„ŒํŠธ์ด๋ฉฐ, ์ด๋ฅผ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋ผ๊ณ  ํ•œ๋‹ค.

โท ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ

  • ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋Š” JSON๊ณผ ์œ ์‚ฌํ•ญ ํ˜•ํƒœ๊ฐ€ ๋งŒ๋“ค์–ด์ง„๋‹ค. ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋Š” ๊ทธ ์ž์ฒด๋กœ React ์ปดํฌ๋„ŒํŠธ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ณ„๋„์˜ JS ๋ฒˆ๋“ค ์—†์ด ๋ธŒ๋ผ์šฐ์ €์—์„œ ์ปดํฌ๋„ŒํŠธ๋กœ ์‚ฌ์šฉ๋œ๋‹ค.
  • ์ด๋Š” ์ƒˆ๋กœ์šด ์ปดํฌ๋„ŒํŠธ์ด๊ณ , React Server Component(RSC) ๋ผ๊ณ  ํ•œ๋‹ค.

โธ SSR + RSC

  • ๋‘ ๊ฐ€์ง€์˜ ๋ Œ๋”๋ง ๋ฐฉ์‹์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๋Œ€ํ‘œ์ ์ธ ๊ฒฝ์šฐ๋กœ๋Š”, ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๊ฐ€ children์š”์†Œ๋กœ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ์—๊ฒŒ ์ „๋‹ฌ๋˜๋Š” ๊ฒฝ์šฐ์ด๋‹ค.
  • ์ด๋•Œ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์„œ๋ฒ„์—์„œ ๋ Œ๋”๋ง๋˜์–ด ๋ธŒ๋ผ์šฐ์ €๋กœ ์ „๋‹ฌ๋˜๊ฒŒ ๋œ๋‹ค.

๐Ÿ“ข Hydration์ด๋ž€

  • Hydration์ด๋ž€ HTML ์ฝ”๋“œ๊ฐ€ React ์ปดํฌ๋„ŒํŠธ๋กœ ์ธ์‹๋˜๋„๋ก JavaScript ์ฝ”๋“œ์™€ ๋งค์นญ์‹œํ‚ค๋Š” ๋‹จ๊ณ„์ด๋‹ค.
  • ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋Š” ์„œ๋ฒ„์—์„œ Pre-rendering ๋ฐฉ์‹์œผ๋กœ HTML์„ ๋งŒ๋“ค์–ด ์ „๋‹ฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— Hydration ๋‹จ๊ณ„๊ฐ€ ์—†๋‹ค.
    - ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋Š” ์„œ๋ฒ„์—์„œ ๋‚ด๋ ค์˜ฌ ๋•Œ RSC Payload๋ผ๋Š” ์ผ์ข…์˜ JSON์œผ๋กœ ์ „๋‹ฌ๋˜๋ฉฐ ๊ณง์žฅ React ์ปดํฌ๋„ŒํŠธ๋กœ ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ๋‹ค.

05. Routing


โ‘  NEXT.JS๋Š” ํด๋” ์ด๋ฆ„์„ ๋”ฐ๋ผ URL Patch๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ๋‹ค.

  • Next.js์—์„œ๋Š” ๋ผ์šฐํŠธ๋ฅผ ํ‘œํ˜„ํ•˜๋Š” ํด๋”์ธ ๊ฒฝ์šฐ์—” ๋ผ์šฐํŠธ ์„ธ๊ทธ๋จผํŠธ(Route Segment) ๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค.

โ‘ก ์•ฝ์†๋œ ํŒŒ์ผ ์ด๋ฆ„

  • Next.js์—์„œ๋Š” ์ƒํ™ฉ์— ๋งž๋Š” UI๋ฅผ ์ •์˜ํ•  ๋•Œ ์“ฐ๋Š” ํŒŒ์ผ๋ช…์ด ๋ฏธ๋ฆฌ ์ •ํ•ด์ ธ์žˆ๋‹ค.

์œ„ ์•ฝ์†๋œ ํŒŒ์ผ๋ช…์— ๋”ฐ๋ผ ํŒŒ์ผ ์ด๋ฆ„์„ ์ง€์ •ํ•ด ์„ธ๊ทธ๋จผํŠธ(ํด๋”) ์•ˆ์— ์ •์˜ํ•˜๋ฉด Next.js๊ฐ€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์•Œ์•„์„œ React ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ฐฐ์น˜ํ•ด์ค€๋‹ค.


โ‘ข ๋™์ ์œผ๋กœ ๋ณ€ํ•  ์ˆ˜ ์žˆ๋Š” URL Patch ๋งŒ๋“ค๊ธฐ

  • Next.js์—์„œ๋Š” Dynamic Routes๋ผ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•œ๋‹ค.
export default function Page({ params }: { params: { id: string } }) {
	return <div>My Post: {params.id}</div>
}

โœ๏ธ Dynamic Routes

  • Dynamic Routes๋Š” ํŒŒ์ผ ์ด๋ฆ„์„ ๋Œ€๊ด„ํ˜ธ๋กœ ๋ฌถ์–ด์ฃผ๋Š” ๊ฑธ ์›์น™์œผ๋กœ ํ•œ๋‹ค.
  • ์‚ฌ์šฉ๋ฐฉ๋ฒ•์€ ๋Œ€๊ด„ํ˜ธ ์•ˆ์— ์ง€์ •ํ•œ ์ด๋ฆ„์„ Page ์ปดํฌ๋„ŒํŠธ์˜ ์ธ์ž๋กœ ๋ฐ›์„ ์ˆ˜ ์žˆ๋‹ค.

06. ํŽ˜์ด์ง€๊ฐ„ ์ด๋™


  • Next.js์—์„œ ์ œ๊ณตํ•˜๋Š” Link ์ปดํฌ๋„ŒํŠธ๋Š” HTML์˜ a ํƒœ๊ทธ์˜ ํ™•์žฅ๋œ ๋ฒ„์ „์ด๋‹ค.
import Link from "next/link";

export default function Page() {
	return <Link href="/dashboard">Dashboard</Link>
}

โ‘ก useRouter ์‚ฌ์šฉํ•˜๊ธฐ

  • ์ด๋•Œ ์ฃผ์˜ํ•  ์ ์€ "useRouter"๋ฅผ ์‚ฌ์šฉํ•  ๋• ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋ฅผ ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ๋กœ ์ž‘์„ฑํ•ด์ค˜์•ผ ํ•œ๋‹ค.
'use client';
import { useRouter } from "next/navigation";

export default function Page() {
	const router = useRouter();

	return (
	<button onClick={() => router.push('/dashboard')}>
		Dashboard
	</button>
	)
}

๐Ÿ“ข Link / Router์— ๋Œ€ํ•œ ๋” ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋ณ„๋„ ํฌ์ŠคํŒ…์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค!


07. ์Šคํƒ€์ผ๋ง with CSS Modules


โ‘  CSS Modules

  • CSS Modules์€ CSS ํด๋ž˜์Šค์˜ ์ด๋ฆ„ ์ค‘๋ณต ๋ฌธ์ œ๋ฅผ ํ”ผํ•  ์ˆ˜ ์žˆ๋Š” ์ง€์—ญ ์Šค์ฝ”ํ”„์˜ ์Šคํƒ€์ผ ๋ฐฉ์‹์ด๋‹ค.
  • .module.css ํ™•์žฅ์ž๋กœ ์ด๋ฆ„์„ ๋ถ™์—ฌ ์Šคํƒ€์ผ์„ ์ •์˜ํ•˜๊ณ  ์ปดํฌ๋„ŒํŠธ์—์„œ ๊ฐ€์ ธ์™€ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
// styles.modules.css

.dashboard {
  padding: 24px;
}
// layout.tsx

import styles from './styles.module.css'
 
export default function DashboardLayout({
  children,
}) {
  return <section className={styles.dashboard}>{children}</section>
}

08. ๋ฐ์ดํ„ฐ ํŒจ์นญ, ์บ์‹ฑ, ์žฌ๊ฒ€์ฆ


๐Ÿ“‚ ๋ฐ์ดํ„ฐ ํŒจ์นญ(Data Fetching)

Next.js์—์„œ ๋‹ค์ด๋‚˜๋ฏนํ•˜๊ฒŒ ์–ด๋–ค id ๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์•„์™”์„ ๋•Œ, ์ดํ›„์— ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ๋™์ ์œผ๋กœ ๋ฐ›์•„์˜ค์ง€ ๋ชปํ•œ๋‹ค๋ฉด ๋™์ ์ธ ํŽ˜์ด์ง€๋ฅผ ๊ทธ๋ฆด ์ˆ˜ ์—†๋‹ค.
๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ์ดํ„ฐ ํŒจ์นญ์„ "์–ผ๋งˆ๋‚˜ ๋ฏธ๋ คํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ–ˆ๋Š”๊ฐ€", ๊ทธ๋ฆฌ๊ณ  "๋ฐ์ดํ„ฐ ํŒจ์นญ์—์„œ ์–ผ๋งˆ๋‚˜ ์†๋„๋ฅผ ๋งŽ์ด ์ค„์ผ ์ˆ˜ ์žˆ๋Š”๊ฐ€" ์— ๋”ฐ๋ผ ์œ ์ € ๊ฒฝํ—˜(UX) ๋ฐ ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋งŽ์€ ์˜ํ–ฅ์„ ๋ผ์นœ๋‹ค.

๐Ÿ“ข ๋ฐ์ดํ„ฐ ํŒจ์นญ์€ ์„œ๋ฒ„์™€ ํด๋ผ์ด์–ธํŠธ ๋ชจ๋‘์—์„œ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, NEXT.js์—์„œ๋Š” ๋ณด์•ˆ๊ณผ ์„ฑ๋Šฅ์ƒ์˜ ์ด์ ์ด ๋งŽ๊ธฐ ๋•Œ๋ฌธ์— ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ํŒจ์นญํ•˜๋Š” ๊ฒƒ์„ ๊ถŒ์žฅํ•œ๋‹ค.

๋˜ ์ฃผ์˜ํ•  ์ ์€ Next๋Š” ํŒจ์นญ๋œ ๋ฐ์ดํ„ฐ, ์ฆ‰ ๋™์ ์œผ๋กœ ๋ฐ›์•„์˜จ ๋ฐ์ดํ„ฐ๋ฅผ ์บ์‹ฑํ•˜๊ณ  ์žˆ๋‹ค๋Š” ์ ์ด๋‹ค.


๐Ÿ“‚ ๋ฐ์ดํ„ฐ ์บ์‹ฑ(Caching)

๊ธฐ๋ณธ์ ์œผ๋กœ fetch์— ์˜ํ•ด ๋ฐœ์ƒํ•œ ์š”์ฒญ์€ ์ž๋™์œผ๋กœ ์บ์‹ฑ๋œ๋‹ค. ๋”ฐ๋ผ์„œ ์—ฌ๋Ÿฌ๋ฒˆ ์š”์ฒญํ•ด๋„ ์ €์žฅํ•ด๋‘” ๊ฐ“์„ ๋ฐ˜ํ™˜ํ•ด ๋ถˆํ•„์š”ํ•œ API ํ˜ธ์ถœ์„ ์ค„์—ฌ์ค€๋‹ค.
์ฆ‰ ์บ์‹œ ๋ฐ์ดํ„ฐ๋Š” ์„œ๋ฒ„์— ์ €์žฅ๋˜์–ด ๋นŒ๋“œ ํƒ€์ž„ ํ˜น์€ ์ดํ›„ ์š”์ฒญ์—๋„ ์žฌ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด๋‹ค.

// fetch์˜ cache ํŒŒ๋ผ๋ฏธํ„ฐ์˜ ๊ธฐ๋ณธ ๊ฐ’ : 'force-cache'  
fetch('https://...', { cache: 'force-cache' })

๋ฐ์ดํ„ฐ ์บ์‹ฑ์€ ์•„๋ž˜ ๊ทธ๋ฆผ์—์„œ ๋ณด๋ผ์ƒ‰ ์˜์—ญ์— ํ•ด๋‹นํ•œ๋‹ค.


๐Ÿ“‚ ๋ฐ์ดํ„ฐ ์žฌ๊ฒ€์ฆ (Data Revalidating)

์บ์‹ฑ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์•„๋‹Œ ์ตœ์‹ ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์›ํ•œ๋‹ค๋ฉด, ๋ฐ์ดํ„ฐ ์žฌ๊ฒ€์ฆ์„ ํ†ตํ•ด ์บ์‹ฑ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฌดํšจํ™”ํ•  ์ˆ˜ ์žˆ๋‹ค.
์ด๋•Œ ์บ์‹ฑ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์žฌ๊ฒ€์ฆํ•˜๋Š” ๋ฐฉ๋ฒ•์—๋Š” ๋‘ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

โ‘  ์‹œ๊ฐ„ ๊ธฐ๋ฐ˜ ์žฌ๊ฒ€์ฆ(Time-based revalidation)

parameter์˜ value๋กœ ์ •์˜ํ•œ ํŠน์ • ์‹œ๊ฐ„์ด ์ง€๋‚œ ํ›„์— ์ž๋™์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์žฌ๊ฒ€์ฆํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

// 3600์ดˆ(60๋ถ„) ๋™์•ˆ ์œ ํšจํ•œ fetch
fetch('https://...', { next: { revalidate: 3600 } })
  1. ์ด๋•Œ ์‹œ๊ฐ„์ด ์ง€๋‚œ ์บ์‹œ ๋ฐ์ดํ„ฐ๋Š” ์‹ ์„ ํ•˜์ง€ ์•Š์€ ๋ฐ์ดํ„ฐ๋กœ ํŒ๋ณ„๋  ๋ฟ, ์‚ญ์ œ๋˜์ง€๋Š” ์•Š๋Š”๋‹ค.
  2. ์‹œ๊ฐ„ ๊ธฐ๋ฐ˜ ์žฌ๊ฒ€์ฆ์€ ๋ฐ์ดํ„ฐ๊ฐ€ ์ž์ฃผ ๋ณ€๊ฒฝ๋˜์ง€ ์•Š๊ณ , ์‹ ์„ ๋„๊ฐ€ ์ค‘์š”ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ์— ์œ ์šฉํ•˜๋‹ค.

  1. ํ•ด๋‹น URL๋กœ ํ˜ธ์ถœ๋œ ๋ฐ์ดํ„ฐ๋Š” "60์ดˆ ๋™์•ˆ ์œ ํšจํ•œ ๋ฐ์ดํ„ฐ"๋กœ ์„ ์–ธํ•œ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ ๋‹ค.
  2. ์•„์ง 60์ดˆ๊ฐ€ ์ง€๋‚˜์ง€ ์•Š์€ ๊ฒฝ์šฐ, ์œ„ ํ•จ์ˆ˜๋ฅผ ์žฌํ˜ธ์ถœํ•˜์ง€ ์•Š๊ณ  ์บ์‹ฑ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•œ๋‹ค.
  3. ๐Ÿšจ 60์ดˆ๊ฐ€ ์ง€๋‚œ ๊ฒฝ์šฐ, ์œ ํšจ์‹œ๊ฐ„์ด ์ง€๋‚ฌ๊ธฐ ๋•Œ๋ฌธ์— ์œ„ ํ•จ์ˆ˜๋ฅผ ๋‹ค์‹œ ํ˜ธ์ถœํ•œ๋‹ค.

๐Ÿšจ ์ฃผ์˜ํ•  ์ !

์ด๋•Œ 60์ดˆ ํ›„์— ๋ฐ”๋กœ ์ƒˆ๋กœ์šด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, 60์ดˆ๊ฐ€ ์ง€๋‚œ ํ›„ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ–ˆ์„ ๋•Œ ํ•ด๋‹น ๋ฐ์ดํ„ฐ๊ฐ€ ์œ ํšจํ•˜์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ์„ ์•Œ๊ฒŒ๋˜๋Š” ๊ฒƒ ๋ฟ์ด๋‹ค.
์‹ค์ œ๋กœ Revalidate๊ฐ€ ์ผ์–ด๋‚œ ์ดํ›„์— ๋ฐ์ดํ„ฐ๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ์‹ ์„ ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

โถ 60์ดˆ๊ฐ€ ์ง€๋‚œ ํ›„ ์ฒซ๋ฒˆ์งธ ํ˜ธ์ถœ ๋•Œ

"์ง€๊ธˆ ์ฃผ๋Š” ๋ฐ์ดํ„ฐ๋Š” ์ด๋ฏธ ์œ ํšจ์‹œ๊ฐ„์ด ์ง€๋‚œ ๋ฐ์ดํ„ฐ์•ผ, ์ฆ‰ ์œ ํšจํ•˜์ง€ ์•Š์•„. ํ•˜์ง€๋งŒ ์ผ๋‹จ ์˜ˆ์ „ ๋ฐ์ดํ„ฐ๋Š” ์ด๊ฑฐ์˜€์–ด"๋ผ๊ณ  ๋ณด๋‚ด์ค€๋‹ค.

์ดํ›„ Revalidate ํ•ด ๋‹ค์‹œ ์œ ํšจํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.

โท ๋‘๋ฒˆ์งธ ํ˜ธ์ถœ ๋•Œ

60์ดˆ ์ดํ›„ ๋‘ ๋ฒˆ์งธ ํ˜ธ์ถœ๋ถ€ํ„ฐ ์‹ ์„ ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค.


โ‘ก On-demand ์žฌ๊ฒ€์ฆ

demand, ์ฆ‰ ์ˆ˜์š”๊ฐ€ ์žˆ์„ ๋•Œ ์žฌ๊ฒ€์ฆํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ, ์‹œ๊ฐ„ ๊ธฐ๋ฐ˜๊ณผ ๋‹ฌ๋ฆฌ ์ˆ˜๋™์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์žฌ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

ํƒœ๊ทธ(tag)ํ˜น์€ ๊ฒฝ๋กœ(path)๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํŠน์ • ๋ฐ์ดํ„ฐ ๊ทธ๋ฃน์„ ์ผ๊ด„ ์žฌ๊ฒ€์ฆํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด๋•Œ ์žฌ๊ฒ€์ฆ๋œ ์บ์‹œ ๋ฐ์ดํ„ฐ๋Š” ์‚ญ์ œ๋œ๋‹ค.

On-demand ์žฌ๊ฒ€์ฆ ๋ฐฉ๋ฒ•์€ ์ฆ‰๊ฐ์ ์œผ๋กœ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ํ™•๋ณดํ•ด์•ผํ•˜๋Š” ๊ฒฝ์šฐ ์œ ์šฉํ•˜๋‹ค.

// ๋ฐ์ดํ„ฐ์— ํƒœ๊ทธ๋ฅผ ๋‹ฌ์•„๋‘”๋‹ค.
fetch('https://...', { next: { tags: ['collection'] } })

// ํƒœ๊ทธ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์žฌ๊ฒ€์ฆํ•œ๋‹ค.
revalidateTag('collection')

๐Ÿ’ก ๊ทธ๋ฆผ๊ณผ ๊ฐ™์ด ์žฌ๊ฒ€์ฆ์ด ํ•„์š”ํ•  ๋•Œ Revalidate ํƒœ๊ทธ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ํƒœ๊ทธ ๊ฐ’์„ ์ „๋‹ฌํ•˜๋ฉด ํ•ด๋‹น ์บ์‹œ๊ฐ€ ์žฌ๊ฒ€์ฆ๋œ๋‹ค.



๐Ÿ“ ์ฐธ๊ณ  ๋ฌธ์„œ

profile
ํ•œ์ž… ํฌ๊ธฐ๋กœ ๋ฒ ์–ด๋จน๋Š” ๊ฐœ๋ฐœ์ง€์‹ ๐Ÿฐ

0๊ฐœ์˜ ๋Œ“๊ธ€