devlog study nextjs react app-router fullstack

Next.js ๐Ÿ“


์˜๋ฌธ๊ฐ–๊ธฐ

RootLayout ์•ˆ์— page.tsx๋ฅผ importํ•ด์„œ ๋„ฃ์€ ์ ์ด ์—†๋Š”๋ฐ ์–ด๋–ป๊ฒŒ ๋ Œ๋”๋ง๋˜์ง€?

Next.js App router๋Š” {children} ์ž๋ฆฌ์— ์ž๋™์œผ๋กœ ํ•˜์œ„ route component(page.tsx)๋ฅผ ๋ผ์›Œ๋„ฃ๋Š”๋‹ค

app/
 โ”œโ”€ layout.tsx        (RootLayout)
 โ”œโ”€ page.tsx          (ํ™ˆ ํŽ˜์ด์ง€ /)
 โ””โ”€ about/
     โ””โ”€ page.tsx      (/about)

์ด๋Ÿฐ ๊ตฌ์กฐ์ผ๋•Œ

  • user๊ฐ€ / URL ์š”์ฒญ โ†’ Next.js๋Š” app/page.tsx๋ฅผ ์ฐพ์•„์„œ
    โ†’ app/layout.tsx์˜ {children} ์ž๋ฆฌ์— ์ž๋™์œผ๋กœ ๋„ฃ์Œ
  • user๊ฐ€ /about URL ์š”์ฒญ
    โ†’ Next.js๋Š” app/about/page.tsx๋ฅผ ์ฐพ์•„์„œ
    โ†’ app/layout.tsx์˜ {children} ์ž๋ฆฌ์— ๋„ฃ์Œ

useSearchParams๋Š” query string์„ ๋น„๋™๊ธฐ๋กœ ๊ฐ€์ ธ์˜ค๋Š”๋ฐ ์™œ ๋น„๋™๊ธฐ ์ฝ”๋“œ๋ฅผ ์“ฐ์ง€ ์•Š์ง€?

useSearchParams๋Š” ๋™๊ธฐ์ ์ธ ๊ฐ’!

"use client";
import { useSearchParams } from "next/navigation";
 
export default function Page() {
  const searchParams = useSearchParams();
  const search = searchParams.get("q"); // ๊ทธ๋ƒฅ ๋™๊ธฐ์ ์œผ๋กœ ์‚ฌ์šฉ
  return <div>๊ฒ€์ƒ‰์–ด: {search}</div>;
}
  • ๋ธŒ๋ผ์šฐ์ €์—์„œ window.location.search๋ฅผ ์“ฐ๋ฉด ํ˜„์žฌ URL์˜ query string์„ ๋ฐ”๋กœ ๊ฐ€์ ธ์˜ด
    • ๋„คํŠธ์›Œํฌ ์š”์ฒญ(fetch)์ด ์•„๋‹ˆ๋ผ ํ˜„์žฌ ๋ธŒ๋ผ์šฐ์ € ์ฃผ์†Œ์ฐฝ์˜ ๊ฐ’์„ ๊ฐ€์ ธ์˜ด
  • React state์ฒ˜๋Ÿผ ๋ผ์šฐํŠธ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ์ž๋™์œผ๋กœ ๊ฐฑ์‹ ๋จ
    • โ€œdynamically evaluatedโ€
      • ๋นŒ๋“œ ์‹œ์ ์—๋Š” ์•Œ์ˆ˜ ์—†๊ณ , ์š”์ฒญ(request) ์‹œ์ ์—๋งŒ ์•Œ์ˆ˜ ์žˆ์Œ

์™œ buildtime error๊ฐ€ ๋‚˜์„œ <Suspense> ์ปดํฌ๋„ŒํŠธ๋กœ ๊ฐ์‹ธ์ค˜์•ผํ•˜๋Š”๊ฑธ๊นŒ?

useSearchParams๋Š” reactive state๋ผ์„œ ๋‚ด๋ถ€์ ์œผ๋กœ Suspense-ready๋กœ ๊ฐ์‹ธ๊ณ  ์žˆ๋‹ค โ‡’ runtime์—์„œ ์ค€๋น„๋ ๋•Œ๊นŒ์ง€ Suspense fallback์„ ๊ทธ๋ ค์ค˜์•ผํ•จ

  • useSearchParmas๋Š” Client component!
    • build time์—๋Š” ์กด์žฌํ•˜์ง€ ์•Š๋Š” ๊ฐ’
    • ๊ฐ’์ด ์ค€๋น„๋˜๊ธฐ ์ „์—๋Š” pending ์ƒํƒœ๋กœ ๋‘ (React Suspense ๊ธฐ๋ฐ˜ โ€œlazyโ€ ๊ฐ’)
      • ์ด ๋•Œ๋ฌธ์— <Suspense>๋กœ ๊ฐ์‹ธ์ค˜์•ผํ•จ

์™œ React ๋Œ€์‹ ์— Next.js๋ฅผ ์“ฐ๋Š”๊ฑธ๊นŒ?

Next.js๋Š” React์˜ ๋‹จ์ ์ธ ์ดˆ๊ธฐ ๋กœ๋”ฉ ์†๋„์™€ SEO ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ , SSRยทSSGยทISR ๊ฐ™์€ ๋‹ค์–‘ํ•œ ๋ Œ๋”๋ง ๋ฐฉ์‹์„ ์ œ๊ณตํ•ด์„œ ๋” ๋‚˜์€ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜๊ณผ SEO ๊ฒ€์ƒ‰๋…ธ์ถœ์„ ๋ณด์žฅํ•œ๋‹ค

CSR(Client Side Rendering) - React

  • ์ดˆ๊ธฐ์—๋Š” ๋นˆ HTML + JS๋งŒ ๋‚ด๋ ค์™€์„œ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์‹คํ–‰ํ•˜๋Š”๋ฐ ์‹œ๊ฐ„์ด ํ•„์š” : ์ฒซ ๋กœ๋”ฉ ์†๋„ ๋А๋ฆผ
  • ํฌ๋กค๋Ÿฌ๋Š” JS ์‹คํ–‰ ์ „์ด๋ผ ์ฝ˜ํ…์ธ ๋ฅผ ์ฝ์ง€ ๋ชปํ•ด์„œ SEO ๋ถˆ๋ฆฌ

Next.js

  • React์˜ ์ƒํƒœ๊ณ„๋ฅผ ๊ทธ๋Œ€๋กœ ์“ฐ๋ฉด์„œ๋„ ๋ผ์šฐํŒ…, API Routes ๋“ฑ ํ’€์Šคํƒ ๊ธฐ๋Šฅ ์ œ๊ณต

  • SSR (Server-Side Rendering): ์„œ๋ฒ„์—์„œ ์™„์„ฑ๋œ HTML์„ ๋‚ด๋ ค์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ์ดˆ๊ธฐ ์†๋„ ๋น ๋ฅด๊ณ  SEO ์นœํ™”์ 

  • SSG (Static Site Generation): ๋นŒ๋“œ์‹œ HTML ์ƒ์„ฑ โ†’ ์†๋„ ๋งค์šฐ ๋น ๋ฅด๊ณ  ํŠธ๋ž˜ํ”ฝ์— ๊ฐ•ํ•จ

  • ISR (Incremental Static Regeneration): static ํŽ˜์ด์ง€๋„ ์ผ์ •์ฃผ๊ธฐ๋งˆ๋‹ค ๊ฐฑ์‹  ๊ฐ€๋Šฅ โ†’ ์ตœ์‹ ์„ฑ + ์„ฑ๋Šฅ ๋ชจ๋‘ ๊ฐ–์ถค

  • ๋ฉด์ ‘์งˆ๋ฌธ์œผ๋กœ ์ž์ฃผ ๋‚˜์˜ด

    • ํ•ด๋‹น ํšŒ์‚ฌ ์„œ๋น„์Šค ๋ถ„์„ํ•ด์„œ ์–ด๋”” ํŽ˜์ด์ง€๋ฅผ ์–ด๋–ค ๋ Œ๋”๋ง ๋ฐฉ์‹์œผ๋กœ ์‚ฌ์šฉํ•˜๋ฉด ์ข‹์€์ง€ ํ•จ๊ป˜ ๋ง๋ถ™์—ฌ ๋Œ€๋‹ตํ•˜๋ฉด ์ข‹์€ ๋Œ€๋‹ต์œผ๋กœ ๋งŒ๋“ค์ˆ˜ ์žˆ์Œ!

๋‚ด์–ด๋ณด๊ธฐ

  • dynamic page์—์„œ ๋น„๋™๊ธฐ๋กœ ๊ฐ’์„ ๊ฐ€์ ธ์˜ค๋Š” ๊ฒฝ์šฐ์—๋„ static page๋ฅผ ๋ฏธ๋ฆฌ ๋งŒ๋“ค์–ด ๋‘˜์ˆ˜ ์žˆ์Œ

    • app-router ์˜ generateStaticParams : ์–ด๋–ค ์ข…๋ฅ˜์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๋Š”์ง€ ์ƒ˜ํ”Œ๋กœ ๋งŒ๋“ค์–ด๋‘ 
      • ์—ฌ๊ธฐ์—์„œ ๋ฆฌํ„ดํ•˜๋Š” ๊ฐ์ฒด์˜ ๊ฐ’์€ โ€œstringโ€๋งŒ ๊ฐ€๋Šฅํ•จ
    • page-router์˜ getStaticPath์™€ ๋™์ผํ•œ ๊ธฐ๋Šฅ
  • hidden input tag์—๋Š” readOnly attribute tag๋ฅผ ์„ค์ •ํ•ด์ค˜์•ผํ•จ

  • div or form ํƒœ๊ทธ๋กœ ๋ฒ„ํŠผ ๊ธฐ๋Šฅ์„ ๋งŒ๋“ค๊ณ  ์‹ถ์œผ๋ฉด โ†’ useRef์‚ฌ์šฉ!

streaming

  • ํฐ ๋ฐ์ดํ„ฐ๋ฅผ ์ž˜๊ฒŒ ์ชผ๊ฐœ์„œ ์—ฐ์†์ ์œผ๋กœ ๋ณด๋‚ด๋Š” ๊ธฐ์ˆ 
  • ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌ๋ฐ›์ง€ ์•Š์€ ์ƒํƒœ์—์„œ ํ™”๋ฉด์„ ๋Š๊น€์—†์ด ๋ณผ์ˆ˜ ์žˆ์Œ
    • async - ๋А๋ฆฌ๊ฒŒ ๋ Œ๋”๋ง ๋˜๋Š” ๋ถ€๋ถ„ : ๋Œ€์ฒด UI(๋กœ๋”ฉ๋ฐ”)๋ฅผ ๋ณด์—ฌ์ฃผ๊ธฐ
  • dynamic page์—์„œ ํ™œ์šฉ
    • cf. Static page๋Š” full route cache์— ์ €์žฅ๋จ
  • UX : user experience๊ฐ€ ์ค‘์š”ํ•จ!!

Page streaming

  • ๋™์ผ ๋ผ์šฐํ„ฐ์— ์žˆ๋Š” page์— ๋Œ€ํ•œ ์ŠคํŠธ๋ฆฌ๋ฐ์˜ ๋Œ€์ฒด UI(loading)๋ฅผ ์„ค์ •ํ•˜๋ ค๋ฉด loading.tsx์ƒ์„ฑ
  • ์ฃผ์˜์‚ฌํ•ญ
    • component์— ๋Œ€ํ•ด์„œ๋Š” ์„ค์ •X
    • query string ๊ฐ’์˜ ๋ณ€ํ™”์— ๋Œ€ํ•ด์„œ๋Š” page์ด๋™์ด ์•„๋‹ˆ๊ธฐ๋•Œ๋ฌธ์— ์„ค์ •ํ• ์ˆ˜ ์—†์Œ
    • ๋น„๋™๊ธฐ-dynamic page์— ๋Œ€ํ•ด์„œ๋งŒ ์ ์šฉ๋จ

Component streaming

  • Suspense ํ™œ์šฉ
  • Skeleton UI : ๋ผˆ๋Œ€๋งŒ ๋ณด์—ฌ์ฃผ๋Š” UI

Server Actionsโœจโœจโœจ

Parallel route & Intercepting route

  • @ : slot
  • (.)ํด๋”๋ช… : route group
    • ์ธํ„ฐ์…‰ํŒ…ํ•  ํŽ˜์ด์ง€๋ฅผ ๊ฐ–๊ณ  ์žˆ๋Š” ํด๋”
  • intercepting route๋Š” CSR๋ฐฉ์‹์œผ๋กœ ํŽ˜์ด์ง€๋ฅผ ์ด๋™ํ•˜๋Š” ๊ฒฝ์šฐ(navigation or )์ผ๋•Œ๋งŒ ๊ฐ€๋Šฅ
    • modal ํŽ˜์ด์ง€๋กœ ๋„์›€
  • ํŠน์ • ํŽ˜์ด์ง€๋ฅผ interceptํ•ด์„œ Modal๋กœ ๋„์šฐ๊ณ  ๋ฐฐ๊ฒฝ์€ parallel route๋ฅผ ํ™œ์šฉํ•ด ๊ธฐ์กด์˜ ํŽ˜์ด์ง€๋ฅผ ๋„์šฐ๋ฉด ์ž์—ฐ์Šค๋Ÿฌ์šด UI๋ฅผ ๋งŒ๋“ค์ˆ˜ ์žˆ์Œ

Component Props

  • ํŠน์ • ์ปดํฌ๋„ŒํŠธ์˜ prop๋“ค์„ ํ‚ค๋กœ ๋ฐ›์•„์„œ ๊ทธ ์ž์ฒด๋ฅผ ์‚ฌ์šฉ?

Auth.js

Runtime


์ฐธ๊ณ ํ•˜๊ธฐ


์ฐพ์•„๋ณด๊ธฐ

  • GET/POST ๊ธฐ๋ณธ ๊ฐœ๋…
  • WS / WAS
    • Edge : auth ํ•จ์ˆ˜
    • Node : signIn, signOut ํ•จ์ˆ˜