ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Next.js 13 업데이트 내용 정리
    개발 블로깅/Next.js 2022. 10. 28. 01:17

     

     

     

    얼추 1년 만에 또 Next.js의 Major 버전이 업데이트 되었다. 🙌
    거두절미하고 바로 어떤 내용이 추가되었는지 확인해보자.

     

    주요 내용 목차

     

    Next.js 13 사용해보기

     

    npm i next@latest react@latest react-dom@latest eslint-config-next@latest

     

    app/Directory (beta)

    사실 이번 next 13의 메인 내용으로 볼 수도 있겠다. 뒤에 나오는 내용들 중 절반이 여기 app 디렉토리와 연관이 되어 있다.

    현재 Next.js에서는 폴더 디렉토리 방식에 따라 각 기능들이 라우팅 되도록, 파일 시스템 기반 라우팅 형식으로 되어있다. (대표적인 예로, 모든 페이지의 최상단 레벨의 컴포넌트는 'pages/' 하위에 위치하는 것)

    이 형식이 우리에게 좋은 이유가, 우리가 react-router-dom 같은 것을 따로 쓰면서 별도 라우팅 코드를 세팅하지 않아도, 그냥 그 디렉토리 하위로 파일을 생성하면 그 파일이 그대로 라우팅 세팅이 되기 때문이다.

     

    The app/ directory can be incrementally adopted from your existing pages/ directory.

     

     

    이번에 라우팅 방식을 조금 더 향상시킬 수 있는 app/ 이라는 디렉토리가 생겼다. 요거는  Layouts RFC에 대한 커뮤니티 피드백을 통해 나온 결과라고 하는데, 나중에 어떤 내용인지 한번 읽어봐야겠다.

    아직 app/ 디렉토리가 크게 필요를 느끼지 못할 수도 있어서, 아직은 베타 버전으로 지원이 되고 있으며, 현재는 pages 디렉토리와 함께 공존할 수 있지만, 점진적으로 app/만 사용되는 것으로 채택될 수 있다고 한다.

    그러면 이러한 app 디렉토리에서 우리가 무엇을 할 수가 있을까?
    아래에서 계속 확인해보자.

     

    Layouts

    app/ 디렉토리 안에서 복잡한 레이아웃 배치를 현재 navigation 처리 상태를 유지하면서 작업할 수 있으며, 리소스가 큰 리렌더링을 피하고 라우팅 패턴을 더 확대시킬 수 있다. 추가로  각 라우팅 위치에 컴포넌트, 스타일, 테스트 관련 코드를 같이 놓을 수 있다고 한다.

     

     

    더 쉽게 설명하자면, 원래 페이지 별 혹은 컴포넌트가 사라졌다가 다시 나타나는 등 컴포넌트 변경이 있으면 리렌더링을 시도하는데, Layouts을 쓰게 되면 이러한 컴포넌트 변경이 있어도 최대한 렌더링 된 상태를 유지하여 리렌더링으로 인한 성능 감소를 줄일 수 있다.

    살짝 예시 코드를 봐보자.

     

    import { ReactNode } from 'react';
    import DataNav from './DataNav';
    
    export default function Layout({ children }: { children: ReactNode }) {
      const ids = [{ id: '1' }, { id: '2' }, { id: '3' }, { id: '4' }];
      if (!ids) return null;
    
      return (
        <div className="space-y-9">
          <div className="flex items-center justify-between">
            <DataNav ids={ids} />
            <div className="rounded-lg bg-zinc-700 px-3 py-1 text-sm font-medium tabular-nums text-zinc-100">
              Last Rendered: {new Date().toLocaleTimeString()}
            </div>
          </div>
    
          <div>{children}</div> // <- 여기가 실제 컨텐츠 
        </div>
      );
    }

     

    전체적인 컨텐츠 영역은 {children}를 통해서 배치하며, 동일한 라우팅 경로 내에서 일어나는 컴포넌트 변경 부분은 컴포넌트가 바뀌어도 최대한 렌더링이 일어나지 않고 유지되는 식이다.

    따라서 페이지 단위로 화면 내 컴포넌트가 수시로 바뀌어도 Layout 내에서는 리렌더링 비용을 최대한 줄여 성능을 향상시킬 수 있게 된다.

    위 Layout은 app 디렉토리 내에서 layout.tsx 파일로 생성할 수 있다.

     

    layout.tsx 파일뿐 아니라, 이와 같은 Special Files이 여러 가지가 있는데, 아래에서 확인해볼 수 있다.

     

    Routing: Fundamentals | Next.js

    ...

    beta.nextjs.org

     

     

    Server Components

    이번에 next 13에서 Server Component 아키텍처를 소개한다. 

    Server Components를 사용하면, 클라이언트로 보내는 자바스크립트 양을 줄일 수 있어 빠르게 페이지 로딩이 가능하다고 설명한다.

    런타임이 로드될 때, 캐시가 가능하고 사이즈를 예측할 수 있어서, 애플리케이션이 점차 커져도 런타임이 증가하지 않는다고 한다. 추가로 런타임이 async 하게 로딩되고, 서버로부터 Hydration 된 HTML이 클라이언트 쪽으로 점진적으로 향상되도록 제공된다고 한다.

     

    (이번에 Next.js 13으로 올라가면서 렌더링 방식이 꽤나 많이 바뀐 것 같다.)

     

    우선 app 디렉토리에서 돌아가는 모든 컴포넌트는 기본적으로 Server Component 방식이다. 따라서 별도의 서버 쪽 설정 없이 바로 Server Component를 활용하여 성능을 올릴 수 있다고 한다.

    뭔가 여기서 설명하는 것으론 정확히 어떤 건지 잘 모르겠긴 한데, 쉽게 말해서 그냥 컴포넌트를 통해 나오는 여러 성능적인 요소(번들 청크파일, 로딩 등)이 서버 쪽 리소스를 활용해서 좀 더 최적화된 상태로 사용할 수 있다는 내용인 듯하다.

     

    Rendering: Server & Client Components | Next.js

    Learn how use Server and Client Components in your Next.js application.

    beta.nextjs.org

     

     

    Streaming

    이것도 app/ 디렉토리 내에서 이루어지는 기능인데, 서버 사이드 단에서 컴포넌트를 점진적으로 렌더링 한 뒤 스트리밍 방식으로 클라이언트에게 전달하는 방식이다. 

    기존에는 서버 사이드 렌더링을 할 때, 화면에 보여줄 데이터들을 백엔드 API를 통해 fetch를 해서 가져올 때까지 기다려야 했다.
    이러한 문제를 Streaming을 통해 해결할 수 있다.

    고정적인 레이아웃 부분은 data fetch가 필요 없기 때문에 먼저 렌더링한 뒤 클라이언트로 보낼 수 있고, 이후 다른 부분은 data fetch가 끝나면 그 이후에 별도로 렌더링을 한 뒤 클라이언트 단으로 보내주게 된다.

    추가로 data fetch가 필요로 한 부분은 가져오기 전까지는 알아서 로딩 상태로 표시가 된다.

     

    Data Fetching

    이번에 app/ 에서 React Suspense 기반으로 구현된 새로운 data fetch 하는 방식이다.

    기존에는 getStaticProps와 getServerSideProps 함수를 별도로 export 하면서 서버 사이드 단에서 돌아가는 로직을 구현을 했다면, 이제는 별도로 함수로 export 하지 않고도 서버 사이드 로직을 구현할 수 있다.

    react에서 제공하는 use hook을 통해 아래처럼 구현하면 된다.

    // app/page.js
    import { use } from 'react';
    
    async function getData() {
      const res = await fetch('...');
      const name: string = await res.json();
      return name;
    }
    
    export default function Page() {
      // This value is fully typed
      // The return value is *not* serialized
      // so you can return Date, Map, Set, etc.
      const name = use(getData());
    
      return '...';
    }

     

    이 방법을 통해 컴포넌트 레벨에서 유연하게 fetch, cache, revalidate data를 할 수 있다고 소개한다.

     

    이러한 이점을 fetch 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 } });

     

     

    여기까지가 이번에 새롭게 소개된 app 디렉토리에서 할 수 있는 내용들이었다.

    뭔가 기존의 pages 디렉토리로 하기에는 이미 내부적으로 너무 복잡한 상태여서 새로운 렌더링 방식 적용에 한계가 있어서 app 디렉토리 라는 것을 새로 만든 듯한 느낌이다. 

    위에서 소개한, app 디렉토리에서 할 수 있는 여러 기능들을 직접 이것저것 해볼 수 있는 playGround 코드도 별도로 제공하고 있다.

     

    App Playground 공식 홈페이지

     

    Next.js 13 App Playground – Vercel

    Explore the new app directory in Next.js 13.

    vercel.com

     

    App Playground Repository

     

    GitHub - vercel/app-playground: https://app-dir.vercel.app/

    https://app-dir.vercel.app/. Contribute to vercel/app-playground development by creating an account on GitHub.

    github.com

     

     

    Turbopack..!!! (alpha)

    Rust 기반의 새로운 번들러다. (미쳤다 미쳤어..!)

    기존에 프론트엔드계에서 거의 필수 모듈인 webpack임에도 자바스크립트 기반의 툴로써 한계에 다다르게 되었다고 말하며, 이러한 한계를 대체할 Rust 기반의 번들러인 Turbopack을 소개한다.

    • 700x faster updates than Webpack
    • 10x faster updates than Vite
    • 4x faster cold starts than Webpack

    Turbopack is our Rust-based successor to Webpack, with 700x faster HMR for large applications.

     

    Turbopack은 Server Components, TypeScript, JSX, CSS 등 많은 부분들을 바로 지원한다.

    Turbopack에 여러 가지 기능들이 있는데, 아직 Alpha 버전이어서 아직은 많은 지원이 되지 않는 상태인데, 우선 next 13 버전에서 "$ next dev --turbo" 명령어를 통해서 바로 turbopack을 통해 서비스를 돌려볼 수 있다.

    $ next dev --turbo

     

     

     

     

    Turbopack - The successor to Webpack

    Turbopack is an incremental bundler optimized for JavaScript and TypeScript, written in Rust.

    turbo.build

     

    next/image

    Core Web Vitals에 최적화된 이미지 컴포넌트를 소개하고 있다.
    (근데 요거는 next 12 버전이랑 차이가 없는 것 같은데..?)

    • Ships less client-side JavaScript
    • Easier to style and configure
    • More accessible requiring alt tags by default
    • Aligns with the Web platform
    • Faster because native lazy loading doesn't require hydration

     

    음.. 진짜 12 버전의 이미지 컴포넌트랑 차이가 없는데..?

     

    Upgrading next/image to Next.js 13

    예전 버전의 이미지 컴포넌트는 'next/legacy/image'로 변경이 되면서, 이전에 사용하던 next/image 컴포넌트들을 'next/legacy/image'로 자동으로 변경해주는 codemod를 제공한다고 한다.

    codemod 사용법은 아래와 같다.

    // ./pages 디렉토리에 있는 코드들을 변경 시
    $ npx @next/codemod next-image-to-legacy-image ./pages

     

    솔직히 어떤 게 바뀐 건지 모르겠다. 내부 성능적으로 변경점이 있나 보다..🙏

     

    @next/font

    next 13 에서는 새로운 글꼴 시스템을 제공한다.

    • 자동으로 최적화된 기본 글꼴 및 커스텀 글꼴 제공.
    • 프라이버시와 퍼포먼스 향상을 위해 외부 네트워크 요청 방식을 제거.
    • 모든 글꼴 파일을 위한 자동 셀프 호스팅 방식.
    • css의 size-adjust 프로퍼티를 이용한 Layout shift 요소 제거. (..오...쩐다)

     

    이번에 새로운 폰트 시스템은 Google Font를 편하게 지원한다. CSS와 폰트 파일을 빌드 타임에 다운로드하고 나머지 static asset파일들과 함께 셀프 호스팅 된다. 따라서 더 이상 폰트 사용을 위해 브라우저에서 Google로 폰트 요청을 하지 않아도 된다.

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

     

    커스텀 폰트 역시 자동 셀프 호스팅, 캐싱, 폰트 파일 preloading을 포함해서 지원된다.

    import localFont from '@next/font/local';
    
    const myFont = localFont({ src: './my-font.woff2' });
    
    <html className={myFont.className}>

     

    커스텀 폰트 역시나 grate performance를 유지하면서 Laout shift를 일으키지 않으며 유저 경험을 최적화시킬 수 있다.

     

    next/link

    기존에 next 12 버전까지는 next/link를 쓰기 위해서 <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 13부터는 <a> 태그를 제외하고 사용할 수 있다.

    import Link from 'next/link'
    
    // Next.js 13: `<Link>` always renders `<a>`
    <Link href="/about">
      About
    </Link>

     

    next/link 역시 기존에 사용하던 next/link 코드를 next 13 버전에 맞게 자동으로 수정할 수 있도록 codemod를 제공한다.
    사용법은 아래와 같다.

    // ./pages 디렉토리에 있는 코드들을 변경할 경우 
    $ npx @next/codemod new-link ./pages

     

    OG Image Generation

    OpenGraph를 위한 Social card도 제공한다..(완전 제대로 SEO 전용 프레임워크로 거듭나고 있구나..)

    참고로 Open Graph는, 특정 사이트가 다른 사이트에 링크를 걸었을 때, 링크 걸린 사이트를 표시하는 방식을 어떻게 할지 정보를 가지는 그래프라고 설명할 수 있겠다.

     

    예시로, 아래에 이런 것이다.

    현재 티스토리에서 링크를 걸었을때 미리보기로 표시되는 방식

     

    일반적으로 사용되는 Static social Card는 에러 발생도 쉽고 느리고 유지관리가 어려웠고, dynamic social cards는 비용이 비싸고 개인화 + 동작 계산이 되어야 해서 매우 어려운 문제가 있었다.

    이러한 dynamic socail cards를 @vercel/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>
        ),
      );
    }

     

     

    Middleware API Updates

    next 12 버전에서 소개되었던 Middleware가 여러 피드백을 통해 개선된 점을 소개한다.

    아래와 같이 쉽게 request의 header 값을 세팅할 수 있다.

    // middleware.ts
    import { NextResponse } from 'next/server';
    import type { NextRequest } from 'next/server';
    
    export function middleware(request: NextRequest) {
      // Clone the request headers and set a new header `x-version`
      const requestHeaders = new Headers(request.headers);
      requestHeaders.set('x-version', '13');
    
      // You can also set request headers in NextResponse.rewrite
      const response = NextResponse.next({
        request: {
          // New request headers
          headers: requestHeaders,
        },
      });
    
      // Set a new response header `x-version`
      response.headers.set('x-version', '13');
      return response;
    }

     

    또한, Auth 인증 실패 등으로 리다이렉트가 필요할 때, 더 이상 rewrite, redirect를 쓰지 않고, 바로 response 선언 부분에서 Json 형태로 Response를 처리할 수 있다.

    // middleware.ts
    import { NextRequest, NextResponse } from 'next/server';
    import { isAuthenticated } from '@lib/auth';
    
    // Limit the middleware to paths starting with `/api/`
    export const config = {
      matcher: '/api/:function*',
    };
    
    export function middleware(request: NextRequest) {
      // Call our authentication function to check the request
      if (!isAuthenticated(request)) {
        // Respond with JSON indicating an error message
        return NextResponse.json(
          {
            success: false,
            message: 'Auth failed',
          },
          {
            status: 401,
          },
        );
      }
    }

     

    현재는 Middleware로부터 response 보내는 부분은 experimental 단계에서 next.config.js에서 experimental.allowMiddlewareResponseBody 설정을 별도로 해주어야 한다.

    const nextConfig = {
      reactStrictMode: true, // Recommended for the `pages` directory, default in `app`.
      swcMinify: true,
      experimental: {
        allowMiddlewareResponseBody: true
      },
    };
    
    module.exports = nextConfig;

     

    Breaking Changes

    • 최소 React 버전 18.2.0부터 지원
    • 최소 Node 버전 14.0.0부터 지원
    • next 12 버전에 소개했던 Rust 기반의 컴파일러 swcMinify 기본 설정이 false에서 true로 변경.
    • next/image 가 next/legacy/image로 변경.
    • next/link 에서 <a> 태그 중첩하던 방식으로 제거.
    • Routes가 User-Agent가 bot일 때는 더 이상 prefetch 하지 않음.
    • next.config.js 옵션 중 deprecated 되었던 target 옵션이 완전히 제거됨.
    • supported browser 변경. Internet Explorer는 완전히 Drop. Browserslist target이 아래와 같음.
      • Chrome 64+
      • Edge 79+
      • Firefox 67+
      • Opera 51+
      • Safari 12+

     

     

    이번에도 Major 버전 업데이트 내용이 굉장하다..(사랑한다..)
    지금 당장 Next 13으로 업그레이드를 하러 가보자!

    $ npm install next react@latest react-dom@latest eslint-config-next
    # or
    $ yarn add next react@latest react-dom@latest eslint-config-next
    # or
    $ pnpm update next react@latest react-dom@latest eslint-config-next

     

     

    Reference

     

    Next.js 13

    Next.js 13 introduces layouts, React Server Components, and streaming in the app directory, as well as Turbopack, an improved image component, and the brand new font component.

    nextjs.org

     

    반응형

    댓글

Designed by Tistory.