Next.js 13 ์ฐ์–ด๋จน๊ธฐ๐Ÿด

์ •์œ ์ • | yujeong choungยท2022๋…„ 11์›” 27์ผ
4

Next

๋ชฉ๋ก ๋ณด๊ธฐ
1/3
post-thumbnail

Next์˜ 2022 ์ปจํผ๋Ÿฐ์Šค๋ฅผ ํ†ตํ•ด Next 13์˜ beta ๋ฒ„์ „์ด ์†Œ๊ฐœ๊ฐ€ ๋˜๋ฉด์„œ ๋งŽ์€ ๋ถ€๋ถ„์— ๋ณ€ํ™”๊ฐ€ ์žˆ์Œ์„ ์•Œ ์ˆ˜ ์žˆ์—ˆ๋‹ค. (์šฐ์„  ๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ๋ถ€ํ„ฐ..!)

beta ๋ฒ„์ „์˜ document๋ฅผ ์ฝ์–ด๋‚ด๋ ค๊ฐ€๋‹ค๋ณด๋‹ˆ ์ƒ๊ฐ๋ณด๋‹ค ๋งŽ์€ ๋‚ด์šฉ์ด ๋‹ด๊ฒจ ์žˆ์–ด์„œ ํ•„์ž์˜ playground ๋ ˆํฌ์—์„œ ํ•˜๋‚˜์”ฉ ์ ์šฉํ•ด๋ณด๋ฉด์„œ ๋†€์•„(?)๋ณด๋˜ ์ค‘์— ๋ชจ๋“  ๊ฒƒ์„ ๋‹ค ์ตํžˆ๊ณ  ๊ธ€์„ ๋‚จ๊ธฐ๋‹ค๊ฐ€๋Š” ๋์ด ์—†์„ ๊ฒƒ ๊ฐ™์•„์„œ ์šฐ์„ ์ ์œผ๋กœ ํผ์ง€๋ง‰ํ•˜๊ฒŒ ์–ด๋–ค ๋ณ€ํ™”๊ฐ€ ์žˆ๋Š”์ง€๋ฅผ Next ์‚ฌ์ดํŠธ์˜ blog ๊ธ€์„ ๋ฒˆ์—ญ ํ•˜๋ฉด์„œ ์ ์–ด๋ณด๋ ค๊ณ  ํ•œ๋‹ค! (ํ˜น์‹œ๋‚˜ ์˜ค๋ฒˆ์—ญ์ด ์žˆ๋‹ค๋ฉด ์–ธ์ œ๋“  ์•Œ๋ ค์ฃผ์„ธ์š” โ˜บ๏ธ)

app Directory (beta)

  • appย ๋””๋ ‰ํ† ๋ฆฌ๋Š” ์•„์ง ๋ฒ ํƒ€ ๋ฒ„์ „์ด๋ผ ํ”„๋กœ๋•์…˜์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์€ ์ถ”์ฒœํ•˜์ง€ ์•Š๋Š”๋‹ค.
  • Next 13๋ฒ„์ „์—์„œ๋„ pages ๋””๋ ‰ํ† ๋ฆฌ๋Š” ์—ฌ์ „ํžˆ ์„œํฌํŠธ ๋  ์˜ˆ์ •์ด๋‹ค.

ํ–ฅํ›„ app ๋””๋ ‰ํ† ๋ฆฌ๊ฐ€ ์„œํฌํŠธํ•  ๋‚ด์šฉ๋“ค์€?

  • Layouts: ์ƒํƒœ๊ฐ’์„ ๋ณด์กดํ•˜๊ณ  ๋ฌด๊ฑฐ์šด ๋ฆฌ๋ Œ๋”๋ง์„ ํ”ผํ•˜๋ฉด์„œ ๋ผ์šฐํ„ฐ ์‚ฌ์ด์— UI๋ฅผ ์‰ฝ๊ฒŒ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Server Components: server-first๋ฅผ default๋กœ ๋งŒ๋“ ๋‹ค.
  • Streaming: UI๊ฐ€ ๋ Œ๋”๋ง ๋˜๋Š” ๋™์•ˆ ์ฆ‰๊ฐ์ ์ธ ๋กœ๋”ฉ ์ƒํƒœ๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋‹ค.
  • Support for Data Fetching: async ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ๋“ค๊ณผ ํ™•์žฅ๋œ fetch API๋กœ ์ปดํฌ๋„ŒํŠธ ๋ ˆ๋ฒจ์˜ fetching์ด ๊ฐ€๋Šฅํ•ด์ง„๋‹ค.

Layout

  • ์žฅ์ !! ์ƒํƒœ ๋ณด์กด, interactive ํ•˜๊ฒŒ ๋‚จ์œผ๋ฉด์„œ ๋ฆฌ๋ Œ๋”๋ง ํ•˜์ง€ ์•Š๋Š”๋‹ค!
app
 - page.js // index route (/)
 - blog
		- layout.js // ์—ฌ๋Ÿฌ ํŽ˜์ด์ง€ ์‚ฌ์ด์—์„œ UI๋ฅผ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋‹ค.

Server Components

  • app/ ๋””๋ ‰ํ† ๋ฆฌ๋Š” Server Components๋“ค์„ ์‚ฌ์šฉํ•œ๋‹ค.
  • Clinet Components:
    'use client';
    
    import { useState } from 'react';
    
    export default function Counter() {
      const [count, setCount] = useState(0);
    
      return (
        <div>
          <p>You clicked {count} times</p>
          <button onClick={() => setCount(count + 1)}>Click me</button>
        </div>);
    }
  • ์–ธ์ œ ์–ด๋–ค๊ฑธ ์‚ฌ์šฉํ• ๊นŒ?

Streaming

  • app/ ๋””๋ ‰ํ† ๋ฆฌ ์•ˆ์—์„œ๋Š” ์ ์ง„์ ์ธ ๋ Œ๋”๋ง๊ณผ ๋”๋ถˆ์–ด ๋ฐ์ดํ„ฐ๊ฐ€ ํ•„์š”์‹œ ๋˜์ง€ ์•Š๋Š” ํŽ˜์ด์ง€๋“ค์˜ ๋ถ€๋ถ„๋“ค์€ ์ฆ‰๊ฐ์ ์ธ ๋ Œ๋”๋ง์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
    • ๋˜ํ•œ, ๋ฐ์ดํ„ฐ๊ฐ€ fetching๋˜๊ณ  ์žˆ๋Š” ์ƒํƒœ์—์„œ๋Š” loading state๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋‹ค.
    • ์ฆ‰, ์‚ฌ์šฉ์ž๊ฐ€ ์ „์ฒด ํŽ˜์ด์ง€๊ฐ€ ๋กœ๋”ฉ๋ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆด ํ•„์š”๊ฐ€ ์—†๋‹ค.

Data Fetching

  • ๋”์ด์ƒ getServerSideProps,ย getStaticProps, getInitialProps โŒ
    • async/await์„ ํ™œ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ณ  promises๋ฅผ ์ปดํฌ๋„ŒํŠธ ์•ˆ์—์„œ ๋‹ค๋ฃฌ๋‹ค.
      // app/page.js
      async function getData() {
        const res = await fetch('https://api.example.com/...');
        // The return value is *not* serialized
        // You can return Date, Map, Set, etc.
        return res.json();
      }
      
      // This is an async Server Component
      export default async function Page() {
        const data = await getData();
      
        return <main>{/* ... */}</main>;
      }
    • ํ•˜์ง€๋งŒ fetch์˜ cache, revalidate ์˜ต์…˜์„ ํ™œ์šฉํ•˜์—ฌ ๊ธฐ์กด์˜ getServerSideProps,ย getStaticProps, getInitialProps ์ฒ˜๋Ÿผ ํ™œ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋ฉฐ SSG, SSR, ISR์˜ ๋ชจ๋“  ์žฅ์ ๋“ค์„ ํ•˜๋‚˜์˜ API์—์„œ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
      // This request should be cached until manually invalidated.
      // Similar to `getStaticProps`.
      // `force-cache` is the default and can be omitted.
      fetch(URL, { cache: 'force-cache' });
      
      // This request should be refetched on every request.
      // Similar to `getServerSideProps`.
      fetch(URL, { cache: 'no-store' });
      
      // This request should be cached with a lifetime of 10 seconds.
      // Similar to `getStaticProps` with the `revalidate` option.
      fetch(URL, { next: { revalidate: 10 } });
  • Server Components์—์„œ ๋ฐ์ดํ„ฐ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ
    • app ๋””๋ ‰ํ† ๋ฆฌ ์•ˆ์—์„œ๋Š” Server Components๋“ค ์•ˆ์—์„œ ๋ฐ์ดํ„ฐ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ๋ฅผ ๊ถŒ์žฅ
    • Server Components๋“ค์€ ํ•ญ์ƒ ์„œ๋ฒ„์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ fetch ํ•œ๋‹ค.
    • ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ์•ˆ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๊ฒƒ์€ ๊ฐ™์€ ํ™˜๊ฒฝ(์„œ๋ฒ„)์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ถˆ๋Ÿฌ์˜ค๊ณ  ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค.
  • ๋ฐ์ดํ„ฐ ํŒจ์นญ์€ layout.tsx, page.tsx ์™€ ์ปดํฌ๋„ŒํŠธ๋“ค ์•ˆ์—์„œ ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๊ฐ€๋Šฅํ•œํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ณณ์—์„œ ๋ฐ์ดํ„ฐ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ๋ฅผ ๊ถŒ์žฅํ•œ๋‹ค.
    ๋ฐ์ดํ„ฐ๋ฅผ props๋ฅผ ํ†ตํ•ด children ์ปดํฌ๋„ŒํŠธ๋“ค๋กœ ๋„˜๊ฒจ์ฃผ๋Š” ๊ฒƒ ๋Œ€์‹ ์— โ‡’ ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์—์„œ ์š”์ฒญํ•œ๋‹ค๊ณ  ํ•˜๋”๋ผ๋„ React์™€ Next๊ฐ€ ์บ์‹ฑ๊ณผ ์ค‘๋ณต๋˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์‚ญ์ œ (dedupe)ํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค!

Turbopack (Alpha)

  • Next 13์—์„œ๋Š” Rust ๋ฒ ์ด์Šค์ธ Turbopack์ด ์†Œ๊ฐœ๋˜์—ˆ๋‹ค.
    • ์›นํŒฉ๋ณด๋‹ค 700x fasterย updates ๋ฅผ ๊ฐ€์ง€๊ณ 
    • ๋ฐ”์ดํŠธ๋ณด๋‹ค 10x fasterย updates ๋ฅผ ๊ฐ€์ง€๋ฉฐ
    • ์›นํŒฉ๋ณด๋‹ค 4x fasterย cold starts ๋ฅผ ํ•œ๋‹ค๊ณ  ํ•œ๋‹ค.
  • ํ˜„์žฌ Turbopack์€ next dev ์—์„œ๋งŒ ์ง€์›๋˜๋ฉฐ next dev --turbo๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.

New next/image

  • Next 13์—์„œ๋Š” ์ƒˆ๋กœ์šด ๊ฐ•๋ ฅํ•œ Image ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์†Œ๊ฐœ๋˜์—ˆ๋‹ค.
    • ๋” ์ ์€ client-side ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋ณด๋‚ด๋ฉฐ
    • style๊ณผ configure์ด ๋” ์‰ฝ๊ณ 
    • ๋””ํดํŠธ๋กœ alt ํƒœ๊ทธ๋ฅผ ํ•„์š”์‹œํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ ‘๊ทผ์„ฑ์ด ๋” ๋›ฐ์–ด๋‚˜์กŒ์œผ๋ฉฐ
    • ์›น ํ”Œ๋žซํผ๊ณผ align ํ•ด์กŒ์œผ๋ฉฐ
    • native lazy loading์ด hydration์„ ํ•„์š”๋กœ ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋” ๋นจ๋ผ์กŒ๋‹ค.
import Image from 'next/image';
import avatar from './lee.png';

function Home() {
  // "alt" is now required for improved accessibility
  // optional: image files can be colocated inside the app/ directory
  return <Image alt="leeerob" src={avatar} placeholder="blur" />;
}

New @next/ font(beta)

  • Next 13์—์„œ๋Š” ์ƒˆ๋กœ์šด ํฐํŠธ ์‹œ์Šคํ…œ์„ ์†Œ๊ฐœํ–ˆ๋‹ค.
    • ์ปค์Šคํ…€ ํฐํŠธ๋“ค์„ ํฌํ•จํ•˜์—ฌ ์ž๋™์ ์œผ๋กœ ํฐํŠธ๋ฅผ ์ตœ์ ํ™” ์‹œ์ผœ์ค€๋‹ค.
    • ํ–ฅ์ƒ๋œ ํ”„๋ผ์ด๋ฒ„์‹œ์™€ ํผํฌ๋จผ์Šค๋ฅผ ์œ„ํ•˜์—ฌ ์™ธ๋ถ€๋กœ์˜ ๋„คํŠธ์›Œํฌ ์š”์ฒญ์„ ์ œ๊ฑฐํ•ด์ค€๋‹ค.
    • ์–ด๋–ค ํฐํŠธ ํŒŒ์ผ์ด๋˜ ๋‚ด์žฅ๋œ automatic self-hosting์„ ์ด์šฉํ•œ๋‹ค.
    • CSS์˜ size-adjust ์†์„ฑ์„ ํ™œ์šฉํ•˜์—ฌ ์ž๋™์ ์œผ๋กœ layout์˜ ๋ณ€ํ™”๋ฅผ ์—†์• ์ค€๋‹ค.
  • ์ฆ‰ ์ƒˆ๋กœ์šด ํฐํŠธ ์‹œ์Šคํ…œ์€ ํŽธ๋ฆฌํ•˜๊ฒŒ ๋ชจ๋“  ๊ตฌ๊ธ€ ํฐํŠธ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค. ๋นŒ๋“œ ํƒ€์ž„์— CSS์™€ font ํŒŒ์ผ๋“ค์ด ์ „๋ถ€ ๋‹ค์šด๋กœ๋“œ๊ฐ€ ๋˜๊ณ  ๊ทธ ํ›„์˜ ๋ชจ๋“  ์ •์ ์ธ assets๋“ค์€ self-hosted ๋˜์–ด์ง„๋‹ค.
    • ๋ธŒ๋ผ์šฐ์ €์—์˜ํ•ด์„œ ๊ตฌ๊ธ€๋กœ ์–ด๋–ค ์š”์ฒญ๋„ ๋ณด๋‚ด์ง€์ง€ ์•Š๊ฒŒ๋œ๋‹ค!

      import { Inter } from '@next/font/google';
      
      const inter = Inter();
      
      <html className={inter.className}>

  • Next 13๋ฒ„์ „ ์ด์ „์—๋Š” next/link์— a ํƒœ๊ทธ๊ฐ€ child๋กœ ์š”๊ตฌ๋˜์—ˆ์—ˆ๋Š”๋ฐ 13๋ฒ„์ „์—์„œ๋Š” ๋”์ด์ƒ ์ˆ˜๋™์ ์œผ๋กœ a ํƒœ๊ทธ๋ฅผ ๋„ฃ์ง€ ์•Š์•„๋„ ๋œ๋‹ค!
import Link from 'next/link'

// Next.js 12: `<a>` has to be nested otherwise it's excluded
<Link href="/about">
  <a>About</a>
</Link>

// Next.js 13: `<Link>` always renders `<a>`
<Link href="/about">
  About
</Link>

OG Image Generation

  • ์ƒˆ๋กœ์šด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ @vercel/og๊ฐ€ ์ถ”๊ฐ€๋˜์–ด Next.js๋งŒ์œผ๋กœ๋„ og ์ด๋ฏธ์ง€๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๊ฒŒ ๋˜์—ˆ๋‹ค.
// pages/api/og.jsx

import { ImageResponse } from '@vercel/og';

export const config = {
  runtime: 'experimental-edge',
};

export default function () {
  return new ImageResponse(
    (
      <div
        style={{
          display: 'flex',
          fontSize: 128,
          background: 'white',
          width: '100%',
          height: '100%',
        }}>
        Hello, World!
      </div>),
  );
}

Breaking Changes

  • ์ตœ์†Œ React ๋ฒ„์ „ 17.0.2 โ‡’ 18.2.0.
  • ์ตœ์†Œ Node ๋ฒ„์ „ 12.22.0 โ‡’ 14.6.0
  • swcMinify ์†์„ฑ์ด false โ‡’ true
  • ๊ธฐ์กด์— ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋˜ Image ์ปดํฌ๋„ŒํŠธ๋“ค์€ next/legacy/image ๋กœ ๋ณ€๊ฒฝ. ์ƒˆ๋กญ๊ฒŒ ์‚ฌ์šฉ๋  Image ์ปดํฌ๋„ŒํŠธ๋“ค์ด next/image
  • next/link child๋กœ ๋”์ด์ƒ <a> ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ. ๊ธฐ์กด์— ์‚ฌ์šฉํ•˜๋˜ next/link์—๋Š” legacyBehavior ์†์„ฑ์„ ์ถ”๊ฐ€ํ•˜์—ฌ ๊ธฐ์กด ๊ธฐ๋Šฅ๋“ค์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜ <a>ํƒœ๊ทธ๋ฅผ ์‚ญ์ œํ•˜์—ฌ ์ƒˆ๋กœ์šด next/link๋กœ ์—…๊ทธ๋ ˆ์ด๋“œ
  • Routes๋Š” User-Agent๊ฐ€ ๋ด‡์ผ๊ฒฝ์šฐ ๋”์ด์ƒ prefetched๋˜์ง€ ์•Š์Œ
  • next.config.js์— deprecatedย target ์˜ต์…˜์ด ์ œ๊ฑฐ
  • ์ง€์›๋˜๋Š” ๋ธŒ๋ผ์šฐ์ € ๋ณ€๊ฒฝ (Internet Explorer๋Š” ์ง€์›ํ•˜์ง€ ์•Š์Œ)
    • Chrome 64+
    • Edge 79+
    • Firefox 67+
    • Opera 51+
    • Safari 12+
profile
์–ธ์ œ๋‚˜ ์ƒˆ๋กœ์šด ๋„์ „์„ ๊ฟˆ๊พธ๋Š” ๊ฐœ๋ฐœ์ž

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