Frontend

Next.js 13 버전을 알아보자 - Turbopack, Layouts, Server Components...

mechaniccoder 2022. 10. 29. 14:17

Next.js conf에서 13버전이 공개됐습니다. 프런트엔드 개발자로서 이번 conf를 보고 대격변이라고 생각이 들 정도로 많은 기능들과 제품들을 소개했는데요. 어떤 것들이 있었는지 Next.js beta 공식문서를 하나씩 읽어보면서 알아보겠습니다.

 

Routing

Next.js 13에서는 새로운 file-system routing이 추가됐습니다. 기존에는 pages/ 디렉토리를 사용했었는데 이제는 app/ 을 사용하시면 됩니다. 점진적으로 도입하기 위해서 pages/ 디렉토리를 여전히 사용할 수도 있습니다.

Next.js new file-system routing
Next.js new file-system routing

app에 생성한 component는 기본적으로 React Server Component이지만 Client Component로도 생성할 수 있습니다. React Server Component와 Client Component에 대해서는 뒤쪽에서 자세히 다뤄보겠습니다.

 

Route Groups

app/ 디렉토리내에 routes를 그룹화할때 사용하는 기능입니다. 소괄호로 이름을 정해 디렉토리로 생성하면 되고, 실제 route에는 영향을 주지 않습니다.

 

Special Files

routes에서 사용할 수 있는 파일들이 추가로 생겼습니다. 기존 Next.js 공식문서에는 layout에 코드를 아래와 같이 작성해줬었습니다.

// pages/index.js

import Layout from '../components/layout'
import NestedLayout from '../components/nested-layout'

export default function Page() {
  return {
    /** Your content */
  }
}

Page.getLayout = function getLayout(page) {
  return (
    <Layout>
      <NestedLayout>{page}</NestedLayout>
    </Layout>
  )
}

Next.js 13에서는 layout, loading, error, template, head 각각의 파일로 정의할 수 있습니다.

 

page.tsx

  • page.tsx는 페이지 컴포넌트를 정의합니다. 기존에 페이지 컴포넌트에서 작성하던 코드는 이 파일에 들어갑니다.

Layout.tsx

  • layout.tsx는 여러 페이지에서 공유되는 UI를 정의합니다. layout은 state를 유지하며, 리렌더링 되지 않고 nested 될 수도 있습니다. app/ 디렉토리 최상위 경로에 정의한 layout.tsx는 Root Layout이고 이 파일은 반드시 필요합니다.
  • Root Layout은 <html>, <body> 태그를 반드시 포함해야 합니다. 기존에 _app.tsx와 _document.tsx를 대체합니다.
  • Nested Layout은 app/ 디렉토리내의 자식 디렉토리 경로에 layout.tsx를 정의하면 됩니다.

Template.tsx

  • Template은 Layout과 비슷한 역할을 하지만 state가 보존되는 Layout과는 달리 항상 새로운 children을 생성합니다.
  • Template의 usecase에 대해서 잘 인지하고 사용해야할 것 같네요.

Head.tsx

  • next/head를 대체하는 파일입니다.

Loading.tsx

  • page의 contents를 받아오기 까지 loading UI를 그려줍니다. page를 suspense로 감싸고 fallback에 이 파일에서 정의한 UI가 들어갑니다.

Error.tsx

  • page에서 error가 발생할 경우, 이 파일에서 정의한 UI를 그려줍니다. react-error-boundary 역할을 합니다.

<Link> Component

이제 더 이상 <Link> 컴포넌트안에 <a> 태그를 쓰지 않아도 됩니다. 읽기 매우 편하네요.

import Link from 'next/link';

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

useRouter() Hook

더 이상 next/router가 아닌 next/navigation에서 import해야합니다.

 

Rendering

Next.js 13버전 부터는 렌더링에 대한 새로운 mental model을 학습해야 합니다.

 

Server and Client component

컴포넌트를 어떤 환경에서 렌더링시킬지 정해줄 수가 있습니다. 기본적으로 app/ 디렉토리에 있는 컴포넌트는 Server Component입니다. 아래 그림과 같이 tree에 Server Component와 Client Compnent가 있는 것을 확인할 수 있습니다.

 

Static Rendering

Static Rendering에서 Server, Client Component는 build 시점에 prerendering됩니다. Client Component는 HTML, JSON을 생성하고 서버에서 이를 캐싱하여 hydrate를 위해 client로 이 파일들을 전송합니다.

Server Component의 경우 서버에서 컴포넌트를 렌더링 시킨 후 HTML을 포함한 payload를 생성하여 javascript 없이 client로 전송합니다. javascript가 없다는 말이 이해가 안돼서 직접 build를 돌려서 테스트를 해봤을때 Server Component에서는 React Hook, 인터랙션을 위한 Event Handler를 사용할 수가 없더군요. Server Component는 정말로 그려주기 위한 역할을 하는 것 같네요.

Dynamic Rendering

Staic Redering외 Dynamic Redering은 request 시점에 server에서 렌더링 됩니다. 기존의 getStaticProps, getServerSideProps로 대응된다고 생각하면 될 것 같네요.

 

Server Component

Server Compoent의 장점은 뭘까요? 기존의 Client Component의 경우 사이즈가 큰 dependency가 bundling에 포함되어 초기 로딩 속도를 느리게 하는 단점이 있었습니다. Server Component의 경우 이 dependency를 서버에만 들고 있고 이를 사용해 컴포넌트를 렌더링하기 때문에 bundling에 포함되지 않아 초기 로딩 속도를 개선할 수 있는 장점이 있습니다.

이 뿐만 아니라 sensitive data를 클라이언트에 노출시키지 않아도 되고, 직접 DB에 접근해서 데이터를 받아올 수도 있겠네요. Server Component와 Client Component를 각각 언제 사용해야하는지에 대한 기준을 확인해보세요.

 

Server component에서 Client Component로 Props 전달

Server Component에서 Client Component로 props를 전달할때 serializable한 값만 보낼 수 있습니다. Redux Toolkit을 사용해보신 분들은 아시겠지만 serializable은 JSON.stringfy할 수 있는 값을 의미합니다. (array, object, string, number ...)

 

Data Fetching

Next.js 13에서는 data fetching하는 방식도 업데이트 됐습니다. async/await과 Server Component를 사용하는 방식인데 코드를 확인해보시죠.

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();
}

export default async function Page() {
  const name = await getData();

  return '...';
}

 

Client Component에서는 data fetching에 React Query, SWR 같은 라이브러리를 사용했는데요. 이제는 React의 use hook을 사용하면됩니다.

"use client";

import { use } from 'react';

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();
}

export default async function Page() {
  const name = use(getData());

  return '...';
}

Data Fetching의 loading, error 상태를 위에서 언급한 loading.tsx, error.tsx 파일이 처리하기 때문에 data가 정상적으로 들어왔을때에 대한 코드만 신경쓰면 됩니다. 코드가 훨씬 간결해진 것 같네요!

 

마지막으로 turbopack에 대해 간단히 알아보겠습니다.

 

Turbopack

Next.js 13에서는 새로운 bundler인 turbopack을 소개했습니다. Rust로 만들어졌기 때문에 webpack, vite.js보다 뛰어난 성능을 보여준다고 소개되고 있네요. 아직은 Next.js에서만 사용되고 있지만 로드맵을 봤을때 webpack의 역할을 하도록 큰 그림을 그리고 있는 것 같습니다. 눈여겨 지켜봐야겠습니다.

 

References