Next.js

최민준·2026년 9월 7일

Next.js

Next.js란?

Next.js는 풀스택 웹 애플리케이션을 만들기 위한 React 기반의 프레임워크입니다.

주요 특징

  1. 서버 사이드 렌더링(SSR): 서버에서 미리 HTML을 만들어 브라우저에 전달하여 검색 엔진 최적화(SEO)와 초기 로딩 속도에서 유리합니다.

  2. 다양한 렌더링 방식: 요청마다 서버에서 실행하는 SSR 이외에도 정적 사이트 생성(SSG) 등 프로젝트에 맞는 방식을 선택할 수 있습니다.

  3. 자동 파일 기반 라우팅: 폴더와 파일 구조에 맞춰서 자동으로 페이지 경로가 설정되기 때문에 편리합니다.

  4. 성능 최적화: 자동으로 코드를 분할해주고 이미지 최적화 등의 기능이 기본적으로 포함되어 있습니다.

React와 Next.js의 차이점

React는 화면을 만들기 위한 UI 라이브러리이고, Next.js는 이를 기반으로 SSR과 라우팅 기능을 더해 만든 풀스택 프레임워크입니다.

핵심 차이점

  1. 정의: React는 UI 라이브러리라 화면의 부품만 제공하고, Next.js는 전체적인 구조와 규칙을 잡아줍니다.

  2. 렌더링 방식: React는 주로 브라우저에서 화면을 그리는 CSR(Client Side Rendering)을 사용하지만, Next.js에서는 서버에서 미리 HTML을 만드는 SSR, SSG 등을 지원합니다.

  3. 라우팅: React는 별도의 라이브러리가 필요하지만, Next.js는 폴더 구조에 따라서 자동으로 주소가 연결되는 파일 기반 라우팅을 제공합니다.

  4. 검색 엔진 최적화: React는 일반적인 CSR 환경에서는 초기 HTML에 콘텐츠가 충분히 포함되지 않을 수 있어 SEO를 추가로 고려해야 하지만, Next.js에서는 SSR이나 SSG 등을 활용하여 검색 엔진이 콘텐츠를 이해하기 유리한 HTML을 제공할 수 있습니다.

App Router

App Router란?

App Router는 Next.js의 파일 시스템 기반 라우팅 및 렌더링 아키텍처입니다. 기존의 pages 디렉터리 기반 라우터를 대체하며, 폴더 구조와 특수 파일(page.js, layout.js 등)을 통해서 웹 애플리케이션의 URL 경로와 UI를 직관적으로 구성합니다.

주요 특징

  1. 파일 시스템 라우팅: app 폴더 하위에 만드는 폴더 구조가 그대로 URL 경로(세그먼트)가 됩니다.

  2. React 서버 컴포넌트 (RSC) 기본 지원: 별도의 설정 없이 컴포넌트가 기본적으로 서버 컴포넌트로 동작하여, 클라이언트로 전송되는 JS 번들 크기를 줄이고 초기 로딩 속도를 높이는 데 도움을 줍니다.

  3. 중첩 레이아웃: layout.js 파일을 통해서 공통 레이아웃을 쉽게 유지하고, 페이지 전환 시 레이아웃을 유지한 채 필요한 부분을 리렌더링할 수 있습니다.

  4. 스트리밍 및 Suspense: React Suspense를 활용해 서버에서 준비된 UI를 점진적으로 클라이언트에 전송하여 UX를 개선할 수 있습니다.

  5. 서버 액션: API 엔드포인트를 따로 만들지 않고도 서버에서 실행되는 함수를 통해 폼 제출이나 데이터 변경을 처리할 수 있습니다.

기존 Pages Router와의 차이점

App Router와 Pages Router는 모두 파일 시스템을 기반으로 URL을 매핑한다는 공통점이 있지만, 디렉터리 구조와 지원하는 렌더링 방식에서 차이가 있습니다.

주요 차이점

  1. 디렉터리 및 라우팅 구조: Pages Router는 pages 디렉터리 안의 파일 이름(about.js 등)이 그대로 URL 경로(/about)가 됩니다. App Router는 app 디렉터리를 사용하며, 폴더 이름이 경로가 되고 화면에 렌더링될 파일은 page.js라는 이름을 가져야 합니다.

  2. 기본 렌더링 및 컴포넌트: Pages Router에서는 페이지 단위로 SSR이나 SSG 등을 설정합니다. App Router는 기본적으로 서버 컴포넌트를 사용하며, 필요한 부분만 클라이언트 컴포넌트로 구성할 수 있습니다.

  3. 레이아웃 및 특수 파일: Pages Router에서 전역 설정은 _app.js와 _document.js를 통해 관리합니다. App Router에서는 폴더별로 layout.js, loading.js, error.js 등의 특수 파일을 지원하여 중첩된 레이아웃과 로딩 상태를 직관적으로 처리할 수 있습니다.

  4. 데이터 패칭 방식: Pages Router에서는 getServerSideProps, getStaticProps와 같은 특수 함수를 사용합니다. App Router에서는 서버 컴포넌트 내부에서 표준 async/await와 fetch를 직접 사용해서 데이터를 불러올 수 있습니다.

Server Component / Client Component

Server Component란?

Server Components란 서버에서 실행되는 React 컴포넌트입니다. 서버에서 데이터를 가져오거나 렌더링한 결과를 클라이언트로 전달할 수 있습니다.

핵심 특징

  1. 서버 전용 실행: 데이터베이스나 파일 시스템에 직접 접근할 수 있지만, useState나 useEffect와 같은 클라이언트 컴포넌트 전용 Hook이나 이벤트 핸들러는 사용할 수 없습니다.

  2. 번들 크기 감소: 서버 컴포넌트의 코드는 클라이언트로 전송되는 JavaScript 번들에 포함되지 않기 때문에 클라이언트에서 실행되는 JavaScript의 양을 줄일 수 있습니다.

  3. 자동 스트리밍: 서버에서 처리된 결과를 RSC Payload 등의 형태로 클라이언트에 전달하여 필요한 UI를 점진적으로 보여줄 수 있습니다.

SSR과의 차이점

  • SSR: 서버에서 요청에 따라 HTML을 생성하여 브라우저에 전달하는 렌더링 방식입니다.
  • Server Component: 컴포넌트 단위로 서버에서 실행될 부분과 클라이언트에서 실행될 부분을 구분할 수 있는 React의 컴포넌트 모델입니다.

즉, SSR과 Server Component는 서로 같은 개념이 아니라 서로 다른 관점의 개념입니다. SSR은 HTML을 어디에서 생성할 것인지에 대한 렌더링 방식이고, Server Component는 컴포넌트를 어디에서 실행할 것인지에 대한 구분 방식입니다.

Client Component란?

Client Component는 클라이언트에서 JavaScript를 통해 실행되며, 사용자의 클릭 및 입력 등 즉각적인 상호작용(인터랙션)을 처리하는 Next.js 및 React의 UI 구성 요소입니다.

주요 특징

  1. 상호작용 처리: 버튼 클릭, 폼 입력, 스크롤 등 사용자의 행동에 반응합니다.

  2. 상태 및 효과 관리: useState, useEffect와 같은 React Hook을 사용할 수 있습니다.

  3. 브라우저 API 사용: window, localStorage 등 브라우저 전용 기능을 사용할 수 있습니다.

  4. 선언 방식: 파일 최상단에 'use client' 지시어를 작성해 지정합니다.

사용하는 경우

  1. 사용자가 버튼을 눌렀을 때 숫자가 올라가는 카운터 기능

  2. 입력 폼에서 실시간으로 글자를 적고 유효성을 검사하는 기능

  3. Modal 팝업창 열기 및 닫기 또는 다크모드 토글 버튼

예를 들어 다음과 같이 작성할 수 있습니다.

'use client';

import { useState } from 'react';

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

  return (
    <button onClick={() => setCount(count + 1)}>
      {count}
    </button>
  );
}

Server Component와 Client Component의 차이점

Server Component와 Client Component는 각각 서버와 클라이언트에서 실행되는 컴포넌트를 구분하여 사용하는 방식이라는 대표적인 차이가 있습니다.

  1. 선언 방식: Server Component는 별도로 표시하지 않고, Client Component는 최상단에 'use client'를 작성해 선언합니다.

  2. 주요 역할: Server Component는 데이터 조회, 정적 콘텐츠 및 서버에서 처리할 수 있는 UI의 역할을 하고 Client Component는 사용자 상호작용(클릭, 입력 등)의 역할을 합니다.

  3. Hook 사용: Server Component에서는 useState, useEffect와 같은 클라이언트 컴포넌트 전용 Hook을 사용할 수 없지만, Client Component에서는 사용할 수 있습니다.

  4. 브라우저 API: Server Component는 window, localStorage 등의 브라우저 API를 사용할 수 없지만, Client Component에서는 사용할 수 있습니다.

  5. 데이터 접근: Server Component에서는 서버에서 데이터베이스나 파일 시스템 등에 직접 접근할 수 있지만, Client Component에서는 직접 접근하지 않고 API 등을 통해 서버와 통신해야 합니다.

  6. 번들 크기: Server Component의 코드는 클라이언트로 전송되는 JS 번들에 포함되지 않지만, Client Component의 코드는 클라이언트에서 실행되므로 번들 크기에 포함됩니다.

Routing / Dynamic Routing

Routing이란?

Next.js에서 Routing은 URL 경로에 따라 어떤 UI를 보여줄지 결정하고 페이지를 이동하는 기능입니다.

App Router에서는 폴더와 파일의 구조를 기반으로 URL 경로가 결정됩니다.

<Link>를 이용한 페이지 이동

Next.js에서는 next/link의 <Link> 컴포넌트를 이용해 페이지를 이동할 수 있습니다.

import Link from 'next/link';

export default function Home() {
  return (
    <Link href="/posts">
      게시글 보기
    </Link>
  );
}

useRouter를 이용한 페이지 이동

사용자의 동작에 따라 프로그래밍 방식으로 페이지를 이동해야 할 때는 useRouter를 사용할 수 있습니다.

'use client';

import { useRouter } from 'next/navigation';

export default function Button() {
  const router = useRouter();

  return (
    <button onClick={() => router.push('/posts')}>
      게시글 보기
    </button>
  );
}

Dynamic Routing이란?

Dynamic Routing은 URL의 일부를 동적으로 변경하거나 값에 따라 다른 페이지를 보여줄 때 사용하는 방식입니다.

동적 세그먼트

폴더 이름을 [slug]와 같이 작성하면 동적 세그먼트가 됩니다. 이 경우 다음과 같은 URL을 처리할 수 있습니다. 동적 세그먼트의 값은 params를 통해 가져올 수 있습니다.

export default async function PostPage({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;

  return <h1>게시글 번호: {slug}</h1>;
}

/posts/1로 접속하면 slug에는 "1"이 전달됩니다.

Catch-all Segment

여러 개의 경로를 하나의 페이지에서 처리해야 하는 경우 [...slug]를 사용할 수 있습니다. 이 경우 다음과 같은 경로를 처리할 수 있습니다. slug는 배열 형태로 전달됩니다.

export default async function DocsPage({
  params,
}: {
  params: Promise<{ slug: string[] }>;
}) {
  const { slug } = await params;

  return <p>{slug.join(' / ')}</p>;
}

searchParams

URL의 Query String을 사용해야 할 때는 searchParams를 사용할 수 있습니다.

예를 들어

/posts?keyword=nextjs

와 같이 URL이 있다면, searchParams를 통해 keyword 값을 가져올 수 있습니다.

export default async function PostsPage({
  searchParams,
}: {
  searchParams: Promise<{ keyword?: string }>;
}) {
  const { keyword } = await searchParams;

  return <h1>검색어: {keyword}</h1>;
}

params가 URL 경로의 동적 세그먼트 값을 다룬다면, searchParams는 URL의 Query String을 다룬다는 차이가 있습니다.

Layout

Layout이란?

Layout은 여러 페이지 간에 공통으로 사용되는 UI 구조를 정의하는 컴포넌트입니다.

주요 특징

  1. 공통 UI 재사용: 헤더, 푸터, 사이드바처럼 모든 페이지나 특정 그룹의 페이지에 반복되는 요소를 한 번만 설정합니다.

  2. 상태 유지: 페이지를 이동할 때 공통 레이아웃은 유지되고, 내부 콘텐츠만 변경되므로 자연스러운 페이지 전환이 가능합니다.

  3. 중첩 가능: 폴더 구조를 기반으로 상위 레이아웃 안에 하위 레이아웃을 중첩해 적용할 수 있습니다.

  4. 서버 컴포넌트 기본 작동: 기본적으로 서버 컴포넌트로 동작해 서버에서 데이터를 처리하는 작업에 활용할 수 있습니다.

  5. 루트 레이아웃: 최상위 app 폴더에 필수로 존재해야 하며, <html>과 <body> 태그를 포함해야 합니다.

예를 들어 다음과 같이 작성할 수 있습니다.

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="ko">
      <body>
        <header>Header</header>
        {children}
        <footer>Footer</footer>
      </body>
    </html>
  );
}

Data Fetching

Data Fetching이란?

Data Fetching은 웹 애플리케이션이 서버, 외부 API 또는 DB에서 필요한 데이터를 가져오거나 읽는 과정입니다.

주요 특징

  1. 요청과 응답: 프론트엔드에서 서버나 외부 API에 데이터를 요청하고 응답으로 데이터를 받아오는 작업입니다.

  2. 비동기 처리: 데이터가 도착할 때까지 기다리는 동안 다른 작업을 멈추지 않도록 비동기 방식으로 이루어집니다.

Next.js에서의 Data Fetching

Next.js의 App Router에서는 서버 컴포넌트에서 async/await와 fetch를 사용해 데이터를 직접 가져올 수 있습니다.

export default async function PostsPage() {
  const response = await fetch(
    'https://jsonplaceholder.typicode.com/posts'
  );

  const posts = await response.json();

  return (
    <div>
      {posts.map((post) => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.body}</p>
        </article>
      ))}
    </div>
  );
}

이 방식에서는 서버 컴포넌트가 서버에서 데이터를 가져온 후 그 결과를 이용해 UI를 렌더링합니다.

Fetch 캐싱과 revalidate

Next.js에서는 fetch에 캐싱 및 재검증 옵션을 설정할 수 있습니다.

const response = await fetch(
  'https://jsonplaceholder.typicode.com/posts',
  {
    next: {
      revalidate: 60,
    },
  }
);

revalidate를 사용하면 데이터를 일정 시간 동안 재사용하고, 설정한 시간이 지난 후 새로운 데이터로 다시 검증하도록 구성할 수 있습니다.

Server Actions

Server Actions는 서버에서 실행되는 함수를 정의하고 클라이언트의 동작과 연결할 수 있는 기능입니다. 주로 폼 제출이나 데이터 생성, 수정, 삭제와 같은 서버 작업에 사용할 수 있습니다.

'use server';

export async function createPost(formData: FormData) {
  const title = formData.get('title');

  console.log(title);
}

이처럼 Server Actions를 이용하면 서버에서 실행되어야 하는 데이터 변경 로직을 구성할 수 있습니다.

사용하는 경우

  1. 사용자 프로필 화면에 보여줄 닉네임과 프로필 사진 불러오기

  2. 게시판에 띄울 글 목록이나 쇼핑몰의 상품 리스트 가져오기

  3. 날씨 정보 API에서 실시간 날씨 데이터 조회하기

Loading / Error

Loading이란?

loading은 페이지나 콘텐츠가 서버에서 불러와지는 동안 사용자에게 즉시 로딩 화면을 보여주는 특수 파일입니다.

주요 특징

  1. React Suspense 자동 적용: 내부적으로 page.js와 하위 컴포넌트를 React Suspense 경계로 감싸 로딩 UI를 표시할 수 있도록 합니다.

  2. 즉시 반응하는 UI: 사용자가 다른 페이지로 이동할 때 로딩 UI를 빠르게 보여줘서 앱이 멈춘 것이 아님을 알려줄 수 있습니다.

  3. 스트리밍: 서버에서 데이터가 준비되는 순서대로 콘텐츠를 스트리밍해 클라이언트에 보내고, 로딩 화면을 실제 콘텐츠로 교체할 수 있습니다.

  4. 레이아웃 유지: 메인 콘텐츠가 로딩되는 동안에도 공통 레이아웃은 유지됩니다.

사용 방법

라우트 폴더에 loading.js 파일을 생성합니다.

스피너, 스켈레톤 UI, 단순 텍스트 등의 로딩 컴포넌트를 작성한 후 export default합니다.

예를 들어 다음과 같이 작성할 수 있습니다.

export default function Loading() {
  return <p>불러오는 중입니다...</p>;
}

Error란?

Next.js에서는 앱 구동 중 예외나 런타임 오류가 발생했을 때 전체 화면이 깨지는 것을 막고, 사용자에게 대체 UI를 보여주며 앱의 안정성을 유지하는 방식으로 Error를 처리합니다.

주요 특징

  1. 파일 규칙: 라우트 세그먼트 내에 이 파일을 만들면, Next.js가 해당 영역의 하위 컴포넌트에서 발생한 오류를 처리할 수 있는 에러 경계를 구성합니다.

  2. 자동 프로퍼티 전달: 에러 컴포넌트는 에러 정보가 담긴 error 객체와 복구를 시도하는 reset() 함수를 props로 전달받습니다.

  3. 클라이언트 컴포넌트 필수: 에러 처리 파일은 반드시 상단에 'use client' 지시문을 작성해야 합니다.

  4. 계층적 에러 전파: 특정 경로에 에러 파일이 없으면 가장 가까운 상위 경로의 에러 처리 영역으로 오류가 전파될 수 있습니다.

  5. global-error.js: 루트 레이아웃 자체에서 발생하는 치명적인 오류를 처리하기 위해 전역 에러 파일을 설정할 수 있습니다.

예를 들어 다음과 같이 작성할 수 있습니다.

'use client';

export default function ErrorPage({
  error,
  reset,
}: {
  error: Error & { digest?: string };
  reset: () => void;
}) {
  return (
    <div>
      <p>문제가 발생했습니다.</p>
      <button onClick={() => reset()}>
        다시 시도
      </button>
    </div>
  );
}

Metadata / SEO

Metadata란?

Metadata는 웹 페이지의 제목, 설명, 오픈 그래프(OG) 이미지 등 HTML의 <head> 요소에 들어갈 정보를 정의해 SEO와 웹 공유 기능을 향상시키는 내장 API입니다.

주요 특징 및 종류

  1. Static Metadata: 고정된 값의 metadata 객체를 선언해 사용합니다.

  2. Dynamic Metadata: generateMetadata 함수를 사용해 DB나 외부 API의 데이터를 불러와 페이지별로 맞춤형 메타 태그를 생성합니다.

  3. 상속 및 병합: layout.js와 page.js에서 각각 정의할 수 있으며, 상위 레이아웃의 메타데이터가 하위 페이지로 상속되거나 필요한 경우 덮어씌워집니다.

설정 예시

import type { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'My Website',
  description: 'Next.js를 이용해 만든 웹 사이트입니다.',
};

SEO란?

SEO는 구글이나 네이버 같은 검색 엔진이 웹사이트의 콘텐츠를 쉽게 읽고 이해할 수 있도록 웹사이트를 최적화하는 작업입니다.

주요 개념 및 핵심 기능

  1. SSR/SSG: 서버에서 완성된 HTML을 제공하는 방식은 검색 엔진이 페이지의 콘텐츠를 이해하는 데 도움을 줄 수 있습니다.

  2. Metadata 설정: 웹 페이지의 제목, 설명, OG 태그 등을 설정해 검색 결과나 공유 시 표시되는 정보를 관리합니다.

  3. 자동 최적화 및 도구: App Router의 metadata 객체나 generateMetadata 등을 활용해 페이지별로 고유한 Metadata를 설정할 수 있습니다.

profile
GSM 10기 Front-End

0개의 댓글