1. pages
디렉토리와 app
디렉토리
Next.js 13 버전부터는 새로운 app
디렉토리가 도입되었습니다. 이 디렉토리는 React Server Components를 기반으로 한 서버 사이드 렌더링을 지원합니다. 기존의 pages
디렉토리도 계속해서 사용할 수 있습니다만, 새로운 기능과 향상된 퍼포먼스를 위해 app
디렉토리를 사용하는 것이 권장됩니다.
1.1 app
디렉토리 구조
app
디렉토리 안에서는 파일과 폴더 이름이 라우팅과 직접적으로 연결됩니다.
- 페이지 파일:
page.tsx
또는page.jsx
- 레이아웃 파일:
layout.tsx
또는layout.jsx
- 로딩 상태 파일:
loading.tsx
또는loading.jsx
- 에러 처리 파일:
error.tsx
또는error.jsx
- 헤드 파일:
head.tsx
또는head.jsx
(Next.js 13.2 이전) - 라우트 핸들러:
route.ts
또는route.js
- 미들웨어:
middleware.ts
또는middleware.js
2. page.tsx
2.1 역할
- 각 경로의 기본 엔트리 포인트로서, 해당 경로에 대한 UI를 정의합니다.
- Next.js는
app
디렉토리에서page.tsx
파일을 찾아 해당 경로로 매핑합니다.
2.2 사용 예제
디렉토리 구조 예시:
app/
├── page.tsx
├── about/
│ └── page.tsx
└── blog/
├── page.tsx
└── [slug]/
└── page.tsx
app/page.tsx
// app/page.tsx
export default function HomePage() {
return (
<main>
<h1>홈 페이지</h1>
</main>
);
}
app/about/page.tsx
// app/about/page.tsx
export default function AboutPage() {
return (
<main>
<h1>소개 페이지</h1>
</main>
);
}
동적 라우팅 예제:
app/blog/[slug]/page.tsx
// app/blog/[slug]/page.tsx
import { useParams } from 'next/navigation';
export default function BlogPostPage() {
const params = useParams();
const { slug } = params;
return (
<main>
<h1>블로그 포스트: {slug}</h1>
</main>
);
}
3. route.ts
3.1 역할
- Next.js에서 API 라우트를 정의하는 데 사용됩니다.
- 기존의
pages/api
디렉토리 대신app
디렉토리에서route.ts
파일을 사용하여 API 엔드포인트를 설정합니다. GET
,POST
,PUT
,DELETE
등의 HTTP 메서드를 처리할 수 있습니다.
3.2 사용 예제
디렉토리 구조 예시:
bash
코드 복사
app/
├── api/
│ └── users/
│ └── route.ts
app/api/users/route.ts
ts
코드 복사
// app/api/users/route.ts
import { NextResponse } from 'next/server';
export async function GET() {
const users = await fetchUsersFromDatabase();
return NextResponse.json({ users });
}
export async function POST(request: Request) {
const data = await request.json();
const newUser = await createUserInDatabase(data);
return NextResponse.json({ user: newUser }, { status: 201 });
}
설명:
- GET 메서드: 사용자 목록을 반환합니다.
- POST 메서드: 새로운 사용자를 생성합니다.
NextResponse
를 사용하여 응답을 생성합니다.
4. middleware.js
또는 middleware.ts
4.1 역할
- 전역적인 요청 처리를 위한 미들웨어를 정의합니다.
- 예를 들어, 인증, 로깅, 리다이렉션 등의 작업을 수행할 수 있습니다.
- 모든 경로나 특정 경로에 대해 미들웨어를 적용할 수 있습니다.
4.2 사용 예제
프로젝트 루트에 middleware.ts
파일 생성
ts
코드 복사
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// 예: 인증되지 않은 사용자를 로그인 페이지로 리다이렉트
const isLoggedIn = checkUserAuthentication(request);
if (!isLoggedIn) {
return NextResponse.redirect(new URL('/login', request.url));
}
// 요청을 그대로 통과시킵니다.
return NextResponse.next();
}
// 미들웨어가 적용될 경로 설정
export const config = {
matcher: ['/protected/:path*'],
};
설명:
middleware
함수: 각 요청에 대해 실행되며,NextRequest
객체를 인자로 받습니다.NextResponse
: 응답을 생성하거나 요청을 수정할 때 사용합니다.config.matcher
: 미들웨어가 적용될 경로를 지정합니다.
5. 기타 파일 및 디렉토리
5.1 layout.tsx
- 각 경로의 레이아웃 컴포넌트를 정의합니다.
- 해당 경로의 모든 하위 페이지에 공통으로 적용됩니다.
사용 예제:
tsx
코드 복사
// app/layout.tsx
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="ko">
<body>
<Header />
{children}
<Footer />
</body>
</html>
);
}
5.2 loading.tsx
- 동적 데이터 로딩 중에 표시될 로딩 상태를 정의합니다.
- Suspense를 활용하여 데이터가 로드될 때까지 로딩 UI를 보여줍니다.
사용 예제:
tsx
코드 복사
// app/loading.tsx
export default function Loading() {
return <div>로딩 중...</div>;
}
5.3 error.tsx
- 페이지에서 에러가 발생했을 때 표시될 UI를 정의합니다.
- 에러 복구 로직을 포함할 수 있습니다.
사용 예제:
tsx
코드 복사
// app/error.tsx
'use client'; // 에러 컴포넌트는 클라이언트 컴포넌트여야 합니다.
import { useEffect } from 'react';
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
useEffect(() => {
// 에러 로깅 등
console.error(error);
}, [error]);
return (
<div>
<h2>에러가 발생했습니다!</h2>
<button onClick={() => reset()}>다시 시도</button>
</div>
);
}
5.4 global.css
- 전역 스타일을 정의하는 파일입니다.
app
디렉토리에서layout.tsx
에서 임포트하여 사용합니다.
사용 예제:
tsx
코드 복사
// app/layout.tsx
import './global.css';
export default function RootLayout({ children }: { children: React.ReactNode }) {
// ...
}
6. 동적 라우팅과 중첩 라우팅
6.1 동적 라우팅
[parameter]
형태의 폴더 이름을 사용하여 동적 경로를 정의합니다.
예제:
css
코드 복사
app/
└── blog/
└── [slug]/
└── page.tsx
blog/hello-world
와 같은 경로로 접근하면slug
값이hello-world
로 설정됩니다.
6.2 중첩 라우팅
- 폴더 구조를 통해 라우트를 중첩시킬 수 있습니다.
- 각 폴더에
layout.tsx
를 정의하여 해당 경로의 레이아웃을 지정할 수 있습니다.
예제:
코드 복사
app/
├── dashboard/
│ ├── layout.tsx
│ ├── page.tsx
│ └── settings/
│ ├── layout.tsx
│ └── page.tsx
dashboard
의 레이아웃은app/dashboard/layout.tsx
에서 정의됩니다.dashboard/settings
의 레이아웃은app/dashboard/settings/layout.tsx
에서 정의되며, 상위 레이아웃을 중첩합니다.
7. 폴더 및 파일의 특수 규칙
7.1 app
디렉토리에서의 파일과 폴더
- 폴더: 라우트 경로를 나타냅니다.
page.tsx
: 해당 경로의 페이지 컴포넌트를 정의합니다.layout.tsx
: 해당 경로 및 하위 경로에 적용될 레이아웃을 정의합니다.loading.tsx
: 해당 경로에서 Suspense에 의한 로딩 상태를 정의합니다.error.tsx
: 해당 경로에서 발생한 에러를 처리합니다.template.tsx
: 동적 렌더링을 위한 템플릿을 정의합니다.not-found.tsx
: 404 페이지를 커스터마이징합니다.
7.2 서버 컴포넌트와 클라이언트 컴포넌트
- 기본적으로 모든 컴포넌트는 서버 컴포넌트입니다.
- 클라이언트 측 상태나 이벤트를 사용하려면 컴포넌트 파일 상단에
'use client';
지시어를 추가해야 합니다.
예제:
tsx
코드 복사
// app/components/ClientComponent.tsx
'use client';
import { useState } from 'react';
export default function ClientComponent() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>클릭: {count}</button>;
}
8. API 라우팅 (route.ts
)의 자세한 예제
8.1 동적 API 라우팅
디렉토리 구조:
bash
코드 복사
app/
└── api/
└── posts/
└── [id]/
└── route.ts
app/api/posts/[id]/route.ts
ts
코드 복사
// app/api/posts/[id]/route.ts
import { NextResponse } from 'next/server';
export async function GET(request: Request, { params }: { params: { id: string } }) {
const post = await getPostById(params.id);
if (!post) {
return NextResponse.json({ error: 'Post not found' }, { status: 404 });
}
return NextResponse.json({ post });
}
설명:
params
객체를 통해 동적 라우트의 매개변수에 접근할 수 있습니다.GET
메서드에서 특정 ID의 포스트를 반환합니다.
8.2 요청 본문 및 헤더 처리
ts
코드 복사
// app/api/data/route.ts
import { NextResponse } from 'next/server';
export async function POST(request: Request) {
const contentType = request.headers.get('content-type');
if (contentType !== 'application/json') {
return NextResponse.json({ error: 'Invalid content type' }, { status: 415 });
}
const data = await request.json();
// 데이터 처리 로직
return NextResponse.json({ success: true });
}
9. 미들웨어 (middleware.ts
)의 상세 예제
9.1 다국어 지원을 위한 리다이렉션
ts
코드 복사
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
const PUBLIC_FILE = /\.(.*)$/;
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// 정적 파일 및 API 경로는 제외
if (PUBLIC_FILE.test(pathname) || pathname.startsWith('/api')) {
return NextResponse.next();
}
// 브라우저의 언어 설정에 따라 리다이렉트
const acceptLanguage = request.headers.get('accept-language');
const language = acceptLanguage?.split(',')[0] || 'en';
if (!pathname.startsWith(`/${language}`)) {
return NextResponse.redirect(new URL(`/${language}${pathname}`, request.url));
}
return NextResponse.next();
}
설명:
- 사용자의 브라우저 언어 설정에 따라 해당 언어의 페이지로 리다이렉션합니다.
- 정적 파일과 API 경로는 미들웨어의 적용을 제외합니다.
9.2 인증 미들웨어
ts
코드 복사
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const token = request.cookies.get('token');
if (!token) {
// 로그인 페이지로 리다이렉트
return NextResponse.redirect(new URL('/login', request.url));
}
// 토큰 유효성 검사 로직 추가 가능
return NextResponse.next();
}
export const config = {
matcher: ['/dashboard/:path*', '/settings/:path*'],
};
설명:
/dashboard
및/settings
경로에 대해 인증을 요구합니다.- 쿠키에서 토큰을 가져와 인증 여부를 판단합니다.
10. Next.js의 라우팅 규칙 요약
- 폴더: URL 경로를 정의합니다.
page.tsx
: 해당 경로의 페이지 컴포넌트.layout.tsx
: 해당 경로와 하위 경로에 적용되는 레이아웃.[parameter]
: 동적 라우팅을 위한 폴더 이름.(group)
: URL 경로에 영향을 주지 않는 그룹화 폴더.route.ts
: 해당 경로의 API 엔드포인트를 정의.middleware.ts
: 전역 또는 특정 경로에 대한 미들웨어를 정의.
요약
page.tsx
: 각 경로의 UI를 정의하는 엔트리 포인트입니다.route.ts
: API 라우트를 정의하며, HTTP 메서드를 처리합니다.middleware.ts
: 전역 또는 특정 경로에 대해 요청을 가로채고 처리하는 미들웨어를 정의합니다.- 파일 및 디렉토리의 이름 규칙은 Next.js의 라우팅 시스템과 밀접하게 연결되어 있습니다.
도움이 되셨길 바랍니다! 추가로 궁금한 사항이나 도움이 필요한 부분이 있으시면 언제든지 말