next.js 기초 입문 28주차 – Next.js 13/14 업데이트 주요 내용 분석

📚
제28주차 학습 목표

Next.js 13/14 업데이트 주요 내용

최신 Next.js 버전의 주요 변경 사항과 새로운 기능을 정리합니다.

Next.js 기초 입문 28회차: Next.js 13/14 업데이트 주요 내용

안녕하세요! Next.js 기초 입문 과정의 28번째 학습 시간입니다. 이번 회차에서는 Next.js의 최신 버전인 13과 14에서 도입된 주요 업데이트 내용과 새로운 기능들을 심층적으로 살펴보겠습니다. Next.js는 빠르게 발전하는 프레임워크이므로, 최신 변경 사항을 이해하는 것은 효율적인 개발에 필수적입니다.

 

 

 

특히 App Router와 서버 컴포넌트의 개념은 Next.js 개발 패러다임에 큰 변화를 가져왔으므로, 이 부분을 집중적으로 다룰 예정입니다.

📌 이번 회차 학습 목표

  • Next.js 13에서 도입된 App Router의 개념과 사용법을 이해할 수 있습니다.
  • 서버 컴포넌트(Server Components)와 클라이언트 컴포넌트(Client Components)의 차이점 및 활용 전략을 설명할 수 있습니다.
  • Next.js 13/14에서 변경된 데이터 페칭(Data Fetching) 방식을 이해하고 적용할 수 있습니다.
  • Turbopack과 같은 성능 개선 도구의 도입 배경과 이점을 파악할 수 있습니다.
  • 최신 Next.js 환경에서 애플리케이션을 효율적으로 개발하기 위한 지식을 습득합니다.

📝 개념 설명

1. Next.js 13의 핵심: App Router 도입

Next.js 13의 가장 큰 변화는 App Router의 도입입니다. 기존의 Pages Router 방식은 파일 시스템 기반 라우팅을 제공했지만, App Router는 React Server Components를 기반으로 하여 더 유연하고 성능 최적화된 라우팅 시스템을 제공합니다. App Router는 app 디렉토리 내에서 동작하며, 레이아웃, 로딩 UI, 에러 바운더리 등 복잡한 UI 패턴을 쉽게 구현할 수 있도록 돕습니다.

  • app 디렉토리: 모든 라우팅 및 UI 로직이 이 디렉토리 내에서 관리됩니다.
  • 레이아웃(Layouts): 여러 페이지에서 공유되는 UI를 정의합니다. layout.js 파일을 통해 정의되며, 중첩 레이아웃을 지원합니다.
  • 서버 컴포넌트(Server Components) 기본: app 디렉토리 내의 모든 컴포넌트는 기본적으로 서버 컴포넌트입니다.
  • 스트리밍(Streaming): 서버에서 클라이언트로 UI를 점진적으로 렌더링하여 사용자 경험을 개선합니다.

2. 서버 컴포넌트와 클라이언트 컴포넌트

Next.js 13/14의 핵심 개념 중 하나는 서버 컴포넌트(Server Components)클라이언트 컴포넌트(Client Components)의 명확한 구분입니다. 이는 React 18에서 도입된 새로운 패러다임을 Next.js에 적용한 결과입니다.

  • 서버 컴포넌트 (Server Components):
    • 서버에서만 렌더링됩니다.
    • 데이터 페칭, 데이터베이스 접근 등 서버 측 로직을 수행하는 데 적합합니다.
    • 번들 크기에 포함되지 않아 클라이언트 측 JavaScript 번들 크기를 줄여줍니다.
    • useState, useEffect와 같은 React Hook을 사용할 수 없습니다.
    • 기본적으로 app 디렉토리 내의 모든 컴포넌트는 서버 컴포넌트입니다.
  • 클라이언트 컴포넌트 (Client Components):
    • 클라이언트(브라우저)에서 렌더링됩니다.
    • 사용자 인터랙션(클릭, 입력 등), 상태 관리(useState), 브라우저 API 접근(localStorage) 등이 필요한 경우 사용합니다.
    • 파일 상단에 'use client'; 지시어를 명시하여 서버 컴포넌트와 구분합니다.
    • 기존 React 컴포넌트와 동일하게 동작합니다.
⚖️ 서버 vs 클라이언트 컴포넌트
항목서버 컴포넌트클라이언트 컴포넌트
렌더링 위치서버클라이언트 (브라우저)
주요 용도데이터 페칭, DB 접근, API 호출사용자 인터랙션, 상태 관리, 브라우저 API
JavaScript 번들포함되지 않음포함됨
React Hooks사용 불가 (useState, useEffect 등)사용 가능
선언 방식기본값 (app 디렉토리)'use client'; 지시어 필요

3. 새로운 데이터 페칭 방식

Next.js 13/14에서는 데이터 페칭 방식에도 큰 변화가 있었습니다. 특히 서버 컴포넌트의 도입으로 인해 서버에서 직접 데이터를 가져오는 것이 매우 자연스러워졌습니다.

  • fetch() API 확장: Next.js는 기본 fetch() API를 확장하여 자동으로 요청을 캐싱하고 재검증(revalidation)하는 기능을 제공합니다. 이는 ISR (Incremental Static Regeneration)과 유사한 동작을 서버 컴포넌트에서 직접 구현할 수 있게 합니다.
  • 캐싱(Caching): fetch() 요청은 기본적으로 서버에 캐시됩니다. cache: 'no-store' 옵션을 통해 캐싱을 비활성화하거나, next: { revalidate: 60 } 옵션을 통해 일정 시간 후 데이터를 재검증하도록 설정할 수 있습니다.
  • 서버 액션(Server Actions) (Next.js 14): Next.js 14에서 정식 도입된 Server Actions는 클라이언트에서 서버 함수를 직접 호출할 수 있게 하여, 폼 제출과 같은 데이터 변경 작업을 더욱 간편하고 안전하게 처리할 수 있도록 합니다. 이는 API 라우트를 별도로 만들 필요 없이 컴포넌트 내에서 직접 서버 로직을 정의할 수 있게 해줍니다.

4. 성능 개선: Turbopack 도입

Next.js 13부터는 개발 서버의 속도를 획기적으로 개선하기 위해 Turbopack이 도입되었습니다. Turbopack은 Rust로 작성된 새로운 번들러로, 기존 Webpack 대비 훨씬 빠른 개발 서버 시작 시간과 HMR(Hot Module Replacement) 속도를 제공합니다. 이는 대규모 프로젝트에서 개발 생산성을 크게 향상시키는 데 기여합니다.

5. Next.js 14의 주요 개선 사항

Next.js 14는 Next.js 13의 App Router와 서버 컴포넌트 개념을 더욱 안정화하고 개선하는 데 중점을 두었습니다.

  • Server Actions 안정화: 위에서 언급했듯이, Server Actions가 안정화되어 프로덕션 환경에서 사용하기에 적합해졌습니다.
  • 부분적인 프리렌더링(Partial Prerendering) (실험적): 정적 콘텐츠와 동적 콘텐츠를 분리하여 더 빠르게 페이지를 로드하는 새로운 최적화 기법입니다.
  • 메타데이터 API 개선: generateMetadata 함수를 통해 동적으로 메타데이터를 생성하는 기능이 더욱 강력해졌습니다.
📌 Next.js 13/14 핵심 포인트
🚀
App Router
React Server Components 기반의 새로운 라우팅 시스템. app 디렉토리 사용.
⚙️
서버 컴포넌트
서버에서 렌더링되며, 데이터 페칭 및 DB 접근에 최적화. JS 번들 크기 감소.
Turbopack
Rust 기반의 고성능 번들러로 개발 서버 시작 및 HMR 속도 대폭 개선.
🔄
Server Actions
클라이언트에서 서버 함수 직접 호출 가능. 폼 제출 등 데이터 변경 작업 간소화.

💡 예제 & 실습

Next.js 13/14의 App Router와 서버/클라이언트 컴포넌트 사용법을 간단한 예제를 통해 살펴보겠습니다.

예제 1: App Router 기본 구조 및 서버 컴포넌트

새로운 Next.js 프로젝트를 생성하고 app 디렉토리 내에 페이지를 만들어봅니다.

설치 및 프로젝트 생성:
npx create-next-app@latest my-next-app
cd my-next-app

설치 시 App Router 사용 여부를 묻는 질문에 ‘Yes’를 선택합니다.

app/page.js 파일은 루트 경로(/)에 해당하는 페이지입니다. 기본적으로 서버 컴포넌트입니다.

// app/page.js

// 이 컴포넌트는 서버에서 렌더링됩니다.
// 데이터베이스 접근이나 API 호출과 같은 서버 측 작업을 수행하기 좋습니다.

async function getPosts() {
  // 실제 API 호출 대신 가상 데이터를 반환합니다.
  // 서버 컴포넌트에서는 직접 fetch를 사용할 수 있으며, 자동으로 캐싱됩니다.
  const res = await fetch('https://jsonplaceholder.typicode.com/posts', { cache: 'no-store' }); // 캐싱 비활성화 예시
  if (!res.ok) {
    throw new Error('Failed to fetch posts');
  }
  return res.json();
}

export default async function HomePage() {
  const posts = await getPosts(); // 서버에서 데이터 페칭

  return (
    <div>
      <h1>환영합니다! Next.js 13/14 App Router 예제</h1>
      <h2>서버 컴포넌트에서 가져온 게시물 목록</h2>
      <ul>
        {posts.slice(0, 5).map(post => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
}
해설:
  • app/page.js는 루트 페이지를 나타냅니다.
  • getPosts 함수는 fetch를 사용하여 데이터를 가져옵니다. 이 함수는 서버에서 실행됩니다.
  • HomePage 컴포넌트는 async 함수로 정의되어 await getPosts()를 통해 데이터를 비동기적으로 가져올 수 있습니다. 이는 서버 컴포넌트의 강력한 기능 중 하나입니다.
  • { cache: 'no-store' } 옵션은 이 특정 요청을 캐시하지 않도록 지시합니다. 기본적으로는 캐싱됩니다.

예제 2: 클라이언트 컴포넌트 사용하기

사용자 인터랙션이 필요한 버튼 컴포넌트를 클라이언트 컴포넌트로 만들어 봅니다.

// app/components/CounterButton.js
'use client'; // 이 파일은 클라이언트 컴포넌트임을 명시합니다.

import { useState } from 'react';

export default function CounterButton() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>현재 카운트: {count}</p>
      <button onClick={() => setCount(count + 1)}>증가</button>
      <button onClick={() => setCount(count - 1)}>감소</button>
    </div>
  );
}

이제 이 클라이언트 컴포넌트를 app/page.js (서버 컴포넌트)에서 사용해봅니다.

// app/page.js (수정된 내용)

import CounterButton from './components/CounterButton'; // 클라이언트 컴포넌트 임포트

async function getPosts() {
  const res = await fetch('https://jsonplaceholder.typicode.com/posts', { next: { revalidate: 3600 } }); // 1시간마다 재검증 예시
  if (!res.ok) {
    throw new Error('Failed to fetch posts');
  }
  return res.json();
}

export default async function HomePage() {
  const posts = await getPosts();

  return (
    <div>
      <h1>환영합니다! Next.js 13/14 App Router 예제</h1>
      <h2>서버 컴포넌트에서 가져온 게시물 목록</h2>
      <ul>
        {posts.slice(0, 5).map(post => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>

      <h2>클라이언트 컴포넌트 예제</h2>
      <CounterButton /> {/* 서버 컴포넌트 내에서 클라이언트 컴포넌트 사용 */}
    </div>
  );
}
해설:
  • CounterButton.js 파일 상단에 'use client'; 지시어를 추가하여 클라이언트 컴포넌트임을 명시했습니다.
  • 이 컴포넌트에서는 useState 훅을 사용하여 상태를 관리하고, 버튼 클릭 시 상태를 업데이트하는 사용자 인터랙션을 구현했습니다.
  • app/page.js (서버 컴포넌트)에서 CounterButton을 임포트하여 사용할 수 있습니다. Next.js는 빌드 시 이를 적절히 처리하여 클라이언트 측에서 렌더링되도록 합니다.
  • fetch 옵션에 { next: { revalidate: 3600 } }을 추가하여 1시간마다 데이터를 재검증하도록 설정했습니다. 이는 ISR과 유사한 동작을 서버 컴포넌트에서 구현하는 방법입니다.

⚠️ 자주 틀리는 것 / 주의사항

  • 서버 컴포넌트에서 useState, useEffect 사용 금지: 서버 컴포넌트는 클라이언트 측 상태나 라이프사이클 훅을 사용할 수 없습니다. 이들을 사용하려면 반드시 'use client';를 선언한 클라이언트 컴포넌트로 전환해야 합니다.
  • 클라이언트 컴포넌트의 과도한 사용: 모든 컴포넌트에 'use client';를 붙이는 것은 Next.js 13/14의 이점을 충분히 활용하지 못하는 것입니다. 가능한 한 서버 컴포넌트를 기본으로 사용하고, 클라이언트 측 인터랙션이 필요한 최소한의 부분에만 클라이언트 컴포넌트를 사용해야 합니다.
  • fetch() 캐싱 이해: Next.js의 fetch()는 기본적으로 캐싱됩니다. 데이터가 항상 최신이어야 한다면 { cache: 'no-store' } 또는 { next: { revalidate: 0 } } 옵션을 명시하거나, revalidate 시간을 적절히 설정해야 합니다.
  • 환경 변수 사용 시 주의: 서버 컴포넌트에서는 process.env.MY_SECRET_KEY와 같이 서버 전용 환경 변수를 직접 사용할 수 있습니다. 하지만 클라이언트 컴포넌트에서 사용하려면 NEXT_PUBLIC_ 접두사를 붙여야 합니다 (예: process.env.NEXT_PUBLIC_API_URL).
  • app 디렉토리와 pages 디렉토리 혼용: Next.js 13/14는 app 디렉토리와 pages 디렉토리를 동시에 사용할 수 있도록 지원하지만, 새로운 프로젝트에서는 app 디렉토리를 중심으로 개발하는 것이 권장됩니다. 혼용 시 라우팅 우선순위 등 복잡성이 증가할 수 있습니다.
✅ 이번 회차 핵심 정리
  • Next.js 13/14의 가장 큰 변화는 App Router 도입과 서버 컴포넌트 기반의 개발 패러다임 전환입니다.
  • 서버 컴포넌트는 서버에서 렌더링되어 데이터 페칭에 유리하며, 클라이언트 측 JS 번들 크기를 줄입니다. 클라이언트 컴포넌트'use client'; 지시어를 통해 선언하며, 사용자 인터랙션과 상태 관리에 사용됩니다.
  • Next.js는 fetch() API를 확장하여 강력한 캐싱 및 재검증 기능을 제공합니다. Next.js 14에서는 Server Actions가 안정화되어 서버 측 데이터 변경 작업을 간소화합니다.
  • Turbopack은 Rust 기반의 번들러로 개발 서버의 성능을 크게 향상시킵니다.
  • 새로운 버전에서는 서버 컴포넌트를 기본으로 활용하고, 클라이언트 컴포넌트는 필요한 최소한의 경우에만 사용하는 것이 중요합니다.

🔗 다음 회차 예고

이번 회차에서는 Next.js 13/14의 주요 업데이트 내용을 살펴보며 App Router와 서버/클라이언트 컴포넌트의 개념을 익혔습니다. 다음 29회차에서는 ‘Next.js에서 데이터 페칭 심화’를 주제로, 서버 컴포넌트에서의 다양한 데이터 페칭 전략, 캐싱 옵션, 그리고 Server Actions를 활용한 데이터 변경 방법에 대해 더욱 깊이 있게 다룰 예정입니다. 이번 회차에서 배운 내용을 바탕으로 다음 회차에서 더욱 효율적인 데이터 관리 기법을 학습해 봅시다.

댓글 남기기

Wordpress Social Share Plugin powered by Ultimatelysocial
Copy link
URL has been copied successfully!
THREADS
RSS
error: 저작권 콘텐츠보호를 부탁드립니다.