Next.js
강의

#3 - Next.js 16 App Router 업계 표준 설계 및 실제 가이드

중년개발자
중년개발자

@loxo

17일 전

22

Next.js 16 App Router 업계 표준 설계 및 실제 가이드

본 문서는 Next.js 16 버전을 기준으로 App Router의 핵심 사상과 설계 철학, 그리고 실무에서 가장 많이 사용되는 폴더 구조 및 URL 설계 패턴을 정의합니다.


1. 설계 철학 및 사상 (Philosophy & Design Principles)

Next.js App Router의 핵심 정신은 **"물리적 폴더 구조가 곧 애플리케이션의 인터페이스(URL)가 된다"**는 것입니다.

1.1 Folders as URLs (폴더 기반 라우팅)

  • 모든 라우트 세그먼트는 폴더로 정의됩니다.
  • page.js 또는 route.js가 포함된 폴더만이 공개적으로 접근 가능한 URL이 됩니다.
  • 철학: 구조를 눈으로 보는 것만으로도 서비스의 URL 맵을 이해할 수 있어야 합니다.

1.2 Safe Colocation (안전한 병치)

  • page.js 외의 다른 파일(컴포넌트, 스타일, 테스트 등)을 자유롭게 라우트 폴더 내부에 둘 수 있습니다.
  • 장점: 관련 코드를 한곳에 모음으로써 유지보수성이 극대화됩니다.

1.3 Server-First & Static-First

  • 기본적으로 모든 컴포넌트는 Server Component입니다.
  • Next.js 16의 use cache 디렉티브를 활용하여 동적 데이터 조차도 효율적으로 캐싱하고 정적 페이지의 성능을 낼 수 있도록 설계합니다.
  • 주의 (Dynamic Content): use cache는 정적 최적화에 강력하지만, 실시간성이 중요한 게시판이나 SEO가 빈번하게 변하는 페이지에서는 캐시 갱신(Revalidation) 관리가 복잡해질 수 있습니다.

2. 라우터 설계 프로세스 (Design Process)

무작정 폴더를 만들기 전에 다음 순서대로 정의해야 합니다.

  1. 도메인 엔티티(Entity) 정의: 서비스의 핵심 데이터 단위(예: User, Post, Order)를 도메인별로 구분합니다.
  2. 사용자 저니(User Journey) 매핑: 사용자가 처음 진입하여 목표를 달성하기까지의 경로를 정의합니다.
  3. URL 스키마 설계: 기계가 아닌 사람이 읽을 수 있는(Semantic) URL 구조를 설계합니다.
  4. 레이아웃(Layout) 계층 구조 정의: 어느 영역이 유지되고 어느 영역이 변경되는지 구분하여 layout.tsx 위치를 결정합니다.
  5. 성능 전략 수립: use cache 디렉티브를 통한 세밀한 데이터 캐싱과 dynamic segment config(force-static, force-dynamic 등)를 조합하여 정적/동적 렌더링 전략을 수립합니다.

2.1 설계 성숙도 모델 (Design Maturity Model)

실무에서 전문가들은 라우터를 설계할 때 다음과 같은 사고 과정을 거칩니다.


3. URL 설계 전략 (URL Design Strategy)

3.1 의미 있는 경로 (Semantic Paths)

  • /dashboard/settings (O)
  • /d/s (X)
  • 명확한 영문 단어를 사용하며, 명사 위주로 구성합니다.

3.2 계층적 구조

  • 리스트: /products
  • 상세: /products/[id]
  • 관리/수정: /products/[id]/edit
  • 사용자 행동이 URL의 마지막 세그먼트로 오도록 설계하는 것이 관례입니다.

3.3 Dynamic Route & Slug (Mapping with @PathVariable)

Next.js의 동적 라우팅(Slug)은 백엔드 개발자에게 익숙한 **Spring Boot의 @PathVariable**과 개념적으로 1:1 대응됩니다.

  • Next.js: app/users/[id]/page.tsxid라는 슬롯(Slug) 정의
  • Spring Boot: @GetMapping("/users/{id}"){id}라는 경로 변수 정의
실무 매핑 예시
구분UI (Next.js)API (Spring Boot)설명
Path/users/[id]/api/v1/users/{id}특정 리소스 식별
Codeparams.id@PathVariable Long id변수 추출 및 사용

핵심 개념: 프론트엔드의 폴더 구조([id])가 백엔드의 API 엔드포인트 설계({id})와 동일한 언어(도메인 모델)를 공유하도록 설계하는 것이 풀스택 아키텍처의 핵심입니다.


4. 업계 표준 폴더 구조 (Standard Folder Structure)

4.1 Route Groups (group)

URL에 영향을 주지 않고 폴더를 그룹화할 때 사용합니다.

  • (auth): login, signup 등을 묶어 별도의 레이아웃 적용.
  • (main): 서비스 메인 영역과 대시보드 영역의 레이아웃 분리.

4.2 Private Folders _folder

라우팅 시스템에서 완전히 제외되는 폴더로, Next.js 공식 문서에서 제안하는 표준 관례입니다.

  • 컨벤션: 폴더명 앞에 언더바_를 붙입니다 (예: _components, _utils).
  • 용도: 특정 라우트 세그먼트 내에서만 사용되는 컴포넌트, 유틸리티, 테스트 파일 등을 안전하게 병치(Colocation)할 때 사용합니다.
  • 장점: 해당 폴더와 그 하위의 모든 파일은 라우팅 대상에서 제외되므로, 실수로 page.js를 추가하더라도 URL로 노출되지 않는 보안 효과와 코드 구조상의 명확성을 제공합니다.

4.3 표준 예시

text
src/app/ ├── (auth)/ # 인증 관련 (URL 미포함) │ ├── layout.tsx # 인증 전용 배경 처리 │ ├── login/ │ └── signup/ ├── (dashboard)/ # 실제 서비스 영역 │ ├── layout.tsx # 사이드바/헤더 공유 │ ├── _components/ # 대시보드 전용 컴포넌트 │ ├── users/ │ │ ├── [id]/ │ │ │ ├── page.tsx │ │ │ └── _components/ │ │ └── page.tsx ├── layout.tsx # Root Layout (html, body 포함) └── page.tsx # 메인 랜딩 페이지

5. 실무 핵심 패턴 (Practical Patterns)

5.1 다중 루트 레이아웃 (Multiple Root Layouts)

단일 루트 레이아웃(app/layout.tsx)만으로는 랜딩 페이지와 관리자 대시보드처럼 **완전히 다른 HTML 구조나 스타일(Body 클래스, 폰트, 서드파티 스크립트 등)**을 처리하기 어렵습니다. 이때 다중 루트 레이아웃 패턴을 사용합니다.

설계 패턴 및 구조

최상위의 layout.tsx를 삭제하고, 각 Route Group 안에 독립적인 루트 레이아웃을 정의합니다.

text
src/app/ ├── (marketing)/ # 랜딩 페이지, 퍼블릭 영역 │ ├── layout.tsx # 전용 root layout (GTM, 랜딩 전용 폰트) │ ├── page.tsx # / (홈 경로) │ └── about/ └── (app)/ # 실제 서비스, 대시보드 영역 ├── layout.tsx # 전용 root layout (사이드바, 대시보드 테마) ├── dashboard/ └── profile/
주의사항 및 실무 팁
  1. 필수 태그: 각 루트 레이아웃(layout.tsx)은 반드시 <html><body> 태그를 포함해야 합니다.
  2. 전체 페이지 새로고침 (Full Page Reload): 서로 다른 루트 레이아웃을 가진 경로 간(예: /about/dashboard) 이동 시에는 React 상태가 유지되지 않고 브라우저가 전체 페이지를 새로고침합니다. 이는 의도된 동작이며, 무거운 대시보드 리소스를 마케팅 페이지에서 분리할 수 있는 장점이기도 합니다.
  3. 공통 설정: 양쪽에서 공통으로 쓰이는 메타데이터나 설정은 _lib 폴더나 별도의 설정 파일을 통해 공유하여 코드 중복을 방지합니다.

5.2 고급 모달 패턴 (Parallel & Intercepting Routes)

인스타그램처럼 서비스 피드 내에서 게시물을 클릭했을 때 URL은 해당 게시물 주소로 바뀌면서 기존 화면 위에 모달이 뜨는 패턴은 App Router의 가장 강력한 기능 중 하나입니다. 이 패턴을 이해하기 위해서는 세 가지 핵심 개념을 알아야 합니다.

1) Parallel Routes (병렬 라우트) : @slot

동일한 레이아웃 내에서 여러 페이지를 동시에 렌더링할 때 사용합니다. 폴더명 앞에 @를 붙여 슬롯을 정의하며, 라우트 폴더(app/)의 layout.tsx에서 이 슬롯들을 Props로 전달받습니다.

2) Intercepting Routes (가로채기 라우트) : (.)계열

사용자가 특정 URL로 향할 때, 그 경로를 가로채서 현재 화면 위에 다른 컴포넌트를 보여주는 기법입니다.

  • (.): 동일한 수준의 세그먼트 가로채기
  • (..) : 한 단계 위의 세그먼트 가로채기
3) default.js : 새로고침 대응의 핵심

이 부분이 처음 사용자들에게 가장 혼란스러운 지점입니다.

  • Soft Navigation (클릭 이동): Next.js가 메모리에 현재 상태를 유지하므로 문제없이 모달이 뜹니다.
  • Hard Navigation (새로고침): 브라우저가 새로고침되면 Next.js는 병렬 슬롯(@modal 등)의 이전 상태를 잃어버립니다. 이때 현재 URL과 일치하는 페이지가 없는 슬롯에 무엇을 보여줄지 결정하는 파일이 바로 default.js입니다. (일반적으로 null을 반환하여 아무것도 보여주지 않게 설정합니다.)
실무 표준 구조 예시 (사진 갤러리 모달)
text
src/app/ ├── @modal/ # 병렬 슬롯 정의 │ ├── (.)photos/[id]/ # URL이 /photos/1일 때 가로채서 보여줄 모달 페이지 │ │ └── page.tsx │ └── default.tsx # 새로고침 시 모달 슬롯을 비워두기 위한 설정 (return null) ├── photos/[id]/ # 실제 사진 페이지 (직접 접속 시 보여줄 전체 화면) │ └── page.tsx ├── layout.tsx # { children, modal }: { children: ReactNode, modal: ReactNode } └── page.tsx # 피드 리스트

이 구조를 통해 사용자가 피드에서 사진을 클릭하면 @modal/(.)photos/[id]가 활성화되어 현재 화면 위에 모달로 뜨고, 해당 URL로 직접 접속하거나 새로고침하면 photos/[id]의 전체 페이지가 렌더링됩니다.

4) 모달 오픈 방법 (Triggering)

모달을 열기 위해서는 반드시 **클라이언트 사이드 내비게이션(Client-side Navigation)**을 이용해야 합니다.

  • <Link> 컴포넌트: 가장 권장되는 방법입니다. 사용자가 링크를 클릭하면 Next.js가 URL 변화를 감지하고 가로채기 라우트를 활성화합니다.
  • router.push(): 프로그래밍 방식으로 모달을 열 때 사용합니다. useRouter 훅을 통해 호출하며, 이 역시 클라이언트 사이드 이동이므로 라우트 가로채기가 정상 작동합니다.
tsx
// 표준적인 모달 오픈 방식 import Link from 'next/link' import { useRouter } from 'next/navigation' export default function FeedItem({ id }) { const router = useRouter() return ( <div> {/* 방식 1: Link 사용 (권장) */} <Link href={`/photos/${id}`}>사진 보기 (모달)</Link> {/* 방식 2: router.push 사용 */} <button onClick={() => router.push(`/photos/${id}`)}> 상세 보기 </button> </div> ) }

중요: 만약 브라우저 주소창에 직접 URL을 입력하고 엔터를 치거나 window.location.href로 이동하면 Hard Navigation이 발생하여 모달이 아닌 전체 페이지가 뜨게 됩니다. 즉, **"앱 내부에서의 부드러운 이동"**일 때만 모달 패턴이 활성화된다는 점이 핵심입니다.

5.3 Next.js 16 : after()을 활용한 비차단(Non-blocking) 작업

Next.js 16에서 도입된 after() API는 응답이 사용자에게 전달된 에 실행되어야 하는 작업을 처리하는 표준입니다.

  • 용도: 로그 기록, 분석 데이터 전송, 캐시 무효화 등.
  • 장점: 사용자가 UI 응답을 받기까지 기다릴 필요가 없으므로 체감 성능이 극대화됩니다.
tsx
import { after } from 'next/server'; export async function POST(request: Request) { // 1. 핵심 비즈니스 로직 수행 (예: 댓글 쓰기) await db.saveComment(...); // 2. 응답 후 비동기 작업 스케줄링 after(async () => { await logAnalytics('comment_created'); await sendNotification(authorId); }); return Response.json({ success: true }); }

5.3 Next.js 16 : use cache 전략

  • 기존: force-static, revalidate 위주의 캐싱.
  • Next.js 16: 함수 단위로 use cache 디렉티브를 선언하여 데이터 페칭의 세밀한 캐싱 제어가 가능합니다. 라우터 수준에서 데이터 의존성을 분리하여 성능을 최적화합니다.
  • 실무 팁 (Hybrid Caching): 고빈도 수정이 일어나는 게시판 데이터의 경우, Next.js 단의 use cache에만 의존하기보다 백엔드(Spring Boot Cache + Redis) 영역에서 데이터를 캐싱하는 것을 추천합니다. 이를 통해 여러 Next.js 인스턴스 간 데이터 일관성을 보장하고, 프런트엔드에서는 필요한 순간에만 동적으로 데이터를 페칭하여 SEO와 실시간성을 동시에 확보할 수 있습니다.
  • Next.js 표준 부합성: 이는 Next.js 공식 문서에서 제안하는 BFF(Backend for Frontend) 아키텍처와 일치합니다. Next.js는 UI 렌더링에 집중하고, 복잡한 데이터 정합성 관리와 원천 데이터 캐싱은 전용 백엔드 레이어에 위임하는 것이 대규모 서비스의 업계 표준입니다.

6. 결론 및 요약

Next.js 16의 App Router는 **"가독성 있는 URL"**과 **"유연한 코드 구조(Colocation)"**를 동시에 잡는 것을 목표로 합니다.

  1. (group)을 적극 활용하여 도메인별 레이아웃을 분리하세요.
  2. _folder를 사용해 공유 로직과 라우트를 명확히 분리하세요.
  3. URL 설계는 사용자 경험(UX)과 검색 엔진 최적화(SEO)를 고려하여 명사 위주 세그먼트로 구성하세요.

목차

#App Router#웹 개발#프론트엔드#라우팅#next.js 16

댓글 0

Ctrl + Enter를 눌러 등록할 수 있습니다
※ AI 다듬기는 내용을 정제하는 보조 기능이며, 최종 내용은 사용자가 확인해야 합니다.