nextjs - deptno/deptno.github.io GitHub Wiki

next.js

next.js

next

next@13

  • client side cache λ₯Ό κ°€μ§€ server μ—μ„œ λ°›μ•„μ˜¨ μ»΄ν¬λ„ŒνŠΈλ“€μ„ μž¬ν™œμš©ν•œλ‹€
  • partial rendering
    • client navigation 이 μΌμ–΄λ‚œ 경우 ν•΄λ‹Ή μ»΄ν¬λ„ŒνŠΈ λΆ€λΆ„λ§Œμ„ μƒˆλ‘œ κ·Έλ¦°λ‹€
  • directory 에 page.tsx κ°€ μžˆμ–΄μ•Ό λΌμš°νŒ…μ΄ κ°€λŠ₯ν•΄μ§„λ‹€
  • route groups
    • (name) directory
    • url ꡬ쑰에 영ν–₯을 μ£Όμ§€ μ•Šκ³  layout λ“± 곡톡 μ»΄ν¬λ„ŒνŠΈ μ‚¬μš©μ„ κ°€λŠ₯ν•˜κ²Œ ν•œλ‹€
    • λ‹€λ₯Έ route groups 에 같은 라우트(url) 을 μƒμ„±ν•˜λŠ” 경우 μ—λŸ¬λ‘œ κ°„μ£Όλœλ‹€
    • route groups κ°„μ˜ navigation 은 full page load κ°€ λ°œμƒν•œλ‹€
  • dynamic route
    • /[name]/page.tsx 의 경우 ({params}: {params: { name: string }}) 을 λ°›κ²Œ λœλ‹€
    • catch all segments
      • /[...name]/page.tsx name 이 ν•˜λ‚˜μ˜ λŽμŠ€κ°€ μ•„λ‹ˆλΌ μ—¬λŸ¬ 뎁슀인 κ²½μš°μ—λ„ λ§€μΉ­λœλ‹€
      • ({params}: {params: { name: string[] }})
    • optional catch all segments
      • /[...name]/page.tsx name 이 μ—†λŠ” / 의 κ²½μš°μ—λ„ λ§€μΉ­λœλ‹€
  • page.tsx λŠ” 기본적으둜 server component μ§€λ§Œ 'use-client' λ₯Ό 톡해 client component μ‚¬μš©μ΄ κ°€λŠ₯
  • layout.tsx
    • 쀑첩가λŠ₯
    • subtree μ—μ„œ μž¬μ‚¬μš©
    • subtree λΌμš°νŒ…μ‹œ re-render λ˜μ§€ μ•ŠλŠ”λ‹€
    • root layout 이 μ•„λ‹ˆλΌλ©΄ optional
    • root layout (μ΅œμƒμœ„ layout.tsx) λŠ” required
    • root layout (μ΅œμƒμœ„ layout.tsx) λŠ” , νƒœκ·Έλ₯Ό ν¬ν•¨ν•΄μ•Όν•œλ‹€
    • root layout 의 κ²½μš°λŠ” server component λ§Œκ°€λŠ₯ν•˜λ‹€ λ‚˜λ¨Έμ§€λŠ” client component 둜 ν™œμš©μ΄ κ°€λŠ₯ν•˜λ‹€
    • fetch κ°€ κ°€λŠ₯ν•˜λ‹€
    • layout fetch λŠ” μžμ‹μ—κ²Œ 전달될 수 μ—†μ§€λ§Œ children μ—μ„œ 쀑볡 패치λ₯Ό ν•˜λ”λΌλ„ next.js κ°€ 퍼포먼슀 영ν–₯ 없이 쀑볡을 μ œκ±°ν•˜μ—¬ 처리
  • template.tsx
    • client component 둜 μΆ”μΈ‘λœλ‹€
    • μœ„μΉ˜μƒμœΌλ‘œλŠ” layout 의 child
    • λ§ˆμ°¬κ°€μ§€λ‘œ 쀑첩이 κ°€λŠ₯ν•˜λ‹€
    • navigation μ‹œ μž¬μ‚¬μš©λ˜λŠ” layout κ³Ό 달리 mount μ‹œμ λ§ˆλ‹€ λ‹€μ‹œ instance ν™” λœλ‹€
    • λ‘˜ 쀑 ν•˜λ‚˜λ₯Ό μ“°λŠ” 것이라면 layout 이 더 ꢌμž₯λœλ‹€
  • navigation
    • - server and client components, μΆ”μ²œ 됨
    • useRouter - client components
    • 쑰건이 μ•ˆλ˜μ„œ hard navigation 이 μΌμ–΄λ‚˜λŠ” 경우 loading.tsx κ°€ 호좜됨
  • server components λŠ” client side μ—μ„œ cached λœλ‹€
    • useRouter().refresh() λ₯Ό 톡해 invaliding 이 κ°€λŠ₯ν•˜λ‹€
  • μ»΄ν¬λ„ŒνŠΈκ°€ viewport μ•ˆμ— λ“€μ–΄μ˜€λ©΄ 기본적으둜 prefetch λœλ‹€ - prefetch λŠ” default κ°€ true - dynamic route 의 κ²½μš°μ—λŠ” loading.tsx 만 λ¨Όμ € prefetch
  • cache
    • if cache soft, navigation
      • static route
      • dynamic route
        • [path] κ°€ λ³€ν•˜μ§€ μ•Šμ€ 경우
      • back/forward navigation
    • if cache is invalidated, hard navigation
  • next.js λŠ” focus and scroll management λ₯Ό 함

tbd

  • parallel routes - 2 page λ₯Ό ν•œλ²ˆμ— κ·Έλ¦°λ‹€(e.g. dashboard)

next@13 app directory setup

yarn dlx create-next-app@latest --experimental-app [project]
cd [project]

corepack enable
yarn set version stable

yarn config set nodeLinker pnpm # ν•„μˆ˜λŠ” μ•„λ‹˜, 기본은 node_modules
yarn # migrate to yarn 3.x

server and client components

  • server component μ—¬λŸ¬κ³³μ—μ„œ 같은 data fetch λŠ” μΊμ‹œλ₯Ό 톡해 κ³΅μœ λ˜μ–΄ ν•œλ²ˆλ§Œ fetch κ°€ μΌμ–΄λ‚œλ‹€
  • client component μ—μ„œ server component λ₯Ό λ Œλ”λ§ν•  수 μ—†λ‹€
    • client component μ—μ„œ children 으둜 server component λ₯Ό λ°›μ•„μ„œ λ Œλ”λ§ν•œλ‹€
  • server component μ—μ„œλŠ” client component λ Œλ”λ§μ΄ κ°€λŠ₯ν•˜λ‹€
  • client component λŠ” μ΅œμƒλ‹¨μ— 'use client' λ””λ ‰ν‹°λΈŒ 선언이 ν•„μš”ν•˜λ‹€
    • κΈ°μ‘΄ λ§Žμ€ λΌμ΄λΈŒλŸ¬λ¦¬λ“€μ€ λ””λ ‰ν‹°λΈŒκ°€ μ—†μœΌλ―€λ‘œ server component μ—μ„œ μ‚¬μš©ν•˜λŠ” 경우 λž˜ν•‘μ΄ ν•„μš”ν•˜λ‹€

static and dynamic rendering

  • μ•„λž˜ κΈ°λŠ₯을 μ‚¬μš©ν•¨μœΌλ‘œμ¨ 동적 λ Œλ”λ§μ„ μ‚¬μš©ν•  수 μžˆλ‹€
    • dynamic function
      • server component
        • cookies()
        • headers()
      • client component -> μ‚¬μš© ꢌ고 (loading.ts)
        • useSearchParams()
    • dynamic data fetching
      • dynamic
        • no-store
        • revalidate: 0
    • 정리 | rendering | data fetching | dynamic functions | |-----------|---------------|-------------------| | static | cached | no | | dynamic | cached | yes | | dynamic | not cached | yes | | dynamic | not cached | yes |
export const dynamic = 'auto'
// 'auto' | 'force-dynamic' | 'error' | 'force-static'

tailwindcss

yarn add -D tailwindcss postcss autoprefixer
yarn tailwindcss init -p # -p λŠ” postcss μ„€μ • μΆ”κ°€

cat app/globals.css << EOF # hello
@tailwind base;
@tailwind components;
@tailwind utilities;
EOF

cat tailwind.config.js << EOF
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./app/**/*.{ts,tsx}",
    "./pages/**/*.{ts,tsx}",
    "./components/**/*.{ts,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
EOF

ui framework based on tailwindcss (eg. daisyui)

yarn add daisyui
cat tailwind.config.js << EOF
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./app/**/*.{ts,tsx}",
    "./pages/**/*.{ts,tsx}",
    "./components/**/*.{ts,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [
    require('daisyui'),
  ],
}
EOF

config

standalone

  • [email protected] standalone 적용 ν›„
    • image size: 465 -> 363 Mb 으둜 빠짐 -> 22% down
    • memory usage: @ λŠ” website 에 ν•œλ²ˆ 접속 ν›„μ˜ μƒνƒœ
      • 69@129Mi -> 24@65->83Mi
      • 69@117Mi -> 25@64->108Mi
      • μ΅œμ ν™”κ°€ 쑰금 더 λ“€μ–΄κ°€λŠ”κ±΄μ§€ λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰μ΄ 빠짐

error

next.js 13 + next-auth

signin 을 λˆ„λ₯΄λ©΄ μ œλŒ€λ‘œ λ™μž‘ν•˜μ§€ μ•ŠλŠ”μ΄μŠˆ λΈŒλΌμš°μ €μ—μ„œλŠ” μ•„λž˜ λ©”μ‹œμ§€κ°€ μ°νžŒλ‹€

next dev --turbo

[400] /api/auth/providers (194ms)
[400] /api/auth/_log (30ms)
[400] /api/auth/error (97ms)

μ•„λž˜λŠ” λΈŒλΌμš°μ €μ—μ„œ μ°νžˆλŠ” 둜그

Error: This action with HTTP GET is not supported by NextAuth.js

--turbo μ˜΅μ…˜μ„ λΉΌλ‹ˆ λ™μž‘ν•œλ‹€. dynamic route μͺ½μ— λ¬Έμ œκ°€ μžˆλŠ” κ²ƒμœΌλ‘œ 보인닀. 버전 정보λ₯Ό μΆ”κ°€ν•΄ λ‘”λ‹€.

    "next": "13.1.6",
    "next-auth": "^4.19.2",

next-auth/src λ₯Ό μ°Έμ‘°ν•΄μ„œ μ—λŸ¬λ‚˜λŠ” 경우

μ°Έμ‘°κ°€ src둜 걸린건지 ν™•μΈν•΄μ„œ μˆ˜μ •ν•  것

./node_modules/.store/next-auth-virtual-8ec2bd5fde/node_modules/next-auth/src/core/lib/assert.ts:134:27
Type error: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Adapter<boolean>'.
  No index signature with a parameter of type 'string' was found on type 'Adapter<boolean>'.

  132 |       "useVerificationToken",
  133 |       "getUserByEmail",
> 134 |     ].filter((method) => !adapter[method])
      |                           ^
  135 |
  136 |     if (missingMethods.length) {
  137 |       return new MissingAdapterMethods(

link

⚠️ **GitHub.com Fallback** ⚠️