Next.js는 파일 기반 라우팅 시스템을 사용합니다. 즉, 폴더 구조가 곧 URL 경로가 되며, 개발자가 별도로 라우팅을 설정할 필요가 없이, pages/
폴더에 파일을 생성하는 것만으로도 직관적이고 효율적인 라우팅이 자동으로 제공합니다.
URL | 파일 경로 |
---|---|
/ | /pages/index.tsx |
/about | /pages/about.tsx |
/contact | /pages/contact.tsx |
Next.js의 라우팅 시스템은 여러 기능을 통해 확장될 수 있으며, 다음과 같이 구분됩니다.
pages/
폴더 기반으로 동작
데이터 패칭 방식: getServerSideProps
, getStaticProps
app/
폴더 기반으로 동작
리액트 서버 컴포넌트 및 서버액션을 지원하고, 더 강력한 레이아웃 및 상태 관리 기능을 제공
파일 이름을 대괄호로 감싸면 해당 부분이 동적으로 변할 수 있습니다.
파일: /pages/post/[id].tsx
URL 예시: /post/1
, /post/2
, /post/title
Catch-All-Routes는 Next.js에서 동적 라우팅을 확장하여 여러 개의 경로 세그먼트를 한 번에 처리할 수 있도록 해주는 기능입니다.
[…param]
)하나 이상의 경로 매개변수가 반드시 필요하며, 해당 값이 없으면 404 에러를 발생합니다.
예시: pages/blog/[…slug].tsx
→ /blog/hello/world
는 가능하지만, 루트 경로인 /blog/
는 404 오류 발생합니다.
[[…param]]
)경로 매개변수가 없어도 실행되며, 루트 경로에서도 동일한 컴포넌트를 활용할 수 있습니다.
예시: pages/blog/[[…param]].tsx
→ blog
도 정상적으로 처리 가능
Next.js의 API Routes는 서버에서 실행되는 API 엔드포인트를 쉽게 만들 수 있는 기능입니다.
일반 서버 뿐만 아니라 Vercel과 같은 서버리스 환경에서도 동작할 수 있기 때문에, Next.js 프로젝트 안에서도 백엔드 기능을 구현할 수 있습니다.
Next.js 에서 api 폴더 내부에 파일을 생성하면, 해당 파일이 자동으로 API 엔드포인트가 됩니다.
API Route 내부의 함수는 요청과 응답 객체를 받아 HTTP 요청을 처리합니다.
Next.js의 API Routes는 일반 서버 환경과 서버리스 환경 모두에서 작동할 수 있도록 설계된 기능입니다. 다만, 배포되는 환경이 서버리스 환경이라면 요청할 때만 실행되고, 일반 서버 환경이라면 지속적인 실행을 한다는 점에서 동작 방식이 다릅니다.
Next.js가 Vercel, AWS Lambda, Cloud Functions 같은 서버리스 플랫폼에서 실행될 경우 다음과 같이 동작합니다.
⭐️ 서버리스 환경에서는 상태가 유지되지 않는 이유
서버리스는 요청이 들어올 때마다 새로운 인스턴스를 생성하고, 요청이 끝나면 생성했던 인스턴스를 종료합니다. 즉, 서버가 항상 실행되고 있는 것이 아니라, 요청이 있을 때만 실행되기 때문에 메모리에 상태를 저장해도 다음 요청에는 새로운 실행 환경이 할당되기 때문에 상태가 유지되지 않습니다.
Next.js가 Node.js 서버에서 실행될 경우 다음과 같이 동작합니다.
저는 그동안 클라이언트에서 상태를 다루는 걸로 알고 있었지만, 사실 서버에서도 서버를 유지하는 경우가 있습니다.
여기서 말하는 상태는 글로벌 변수나 캐시같이 어떤 요청과 다음 요청 사이에 유지되는 데이터를 의미합니다. 서버리스 환경에서는 이런 상태를 유지할 수 없고, 일반적인 서버 환경에서는 상태를 유지할 수 있습니다.
서버가 계속 실행 중이라면 counter 변수의 값은 유지되기 때문에, 요청이 여러 번 들어와도 이전의 counter 값이 유지되면서 증가합니다.
서버가 재시작되거나, 서버리스 환경에서는 매번 새로운 인스턴스에서 실행되기 때문에 counter 값은 초기화됩니다.
// Node.js Express 서버
let counter = 0;
app.get('/increase', (req, res) => {
counter++;
res.send(`Counter: ${counter}`);
});
src/pages/엔드포인트.ts
export default
로 정의함// src/pages/api/hello.ts
export default function handler(req, res){
res.status(200).json({ message: ‘Page Router 예시’ });
}
src/app/api/엔드포인트/route.ts
export function GET()
) 구조 사용// src/app/api/hello/route.ts
export async function GET(req) {
const body = { message: ‘App Router 예시’ });
const options = { headers: { ‘Content-Type’: ‘application/json’ }, };
return new Response(JSON.stringfy(body, options);
}
Next.js 미들웨어는 요청이 최종적으로 페이지나 API 핸들러에 전달되기 전에 실행되는 코드입니다.
요청이 전달되기 전에 실행되는 점을 활용하면 인증 체크, 페이지 리다이렉션, 캐시 정책 설정 등의 작업을 수행하는 데 유용합니다.
인증: 쿠키나 헤더를 확인하여 인증된 사용자만 특정 페이지에 접근하도록 제한
리다이렉션: 특정 경로에 접근하는 사용자를 다른 페이지로 이동
캐시 제어: 응답 헤더를 수정해서 캐시 정책을 제어
// 루트경로에 위치한 middleware.ts
import { NextResponse } from ‘next/server’;
import type { NextRequest } from ‘next/server’;
// auth-token 쿠키를 확인하고, 로그인되지 않은 유저는 자동으로 / 경로로 리다이렉트됩니다.
export function middleware(req: NextRequest) {
const isLogin = req.cookies.get('auth-token’);
if(!isLogin) return NextResponse.redirect(new URL(‘/‘, req.url);
// HTTP를 HTTPS로 강제 변환
if(req.nextUrl.protocol === ‘http:’) return NextResponse.redirect(req.url.replace(‘http:’, ‘https:’));
const response = NextResponse.next();
const pathname = req.nextUrl.pathname;
if(pathname.startsWith(‘/no-cache’)) {
// 브라우저와 프록시에서 캐싱되지 않도록 설정
response.headers.set(‘Cache-Control’,’no-store, no-cache, must-revalidate, proxy-revalidate’);
}
if(pathname.startsWith(‘/static’)) {
// 1년 (=31536000초) 동안 캐싱되며 변경되지 않음(immutable)으로 설정
response.headers.set(‘Cache-Control’, ‘public, max-age=31536000, immutable’);
}
if(pathname.startsWith(‘/api/secure’)) {
const token = req.headers.get(‘Authorization’);
// 올바른 토큰이 없으면 401 응답을 반환함
if(!token || token !== ‘Bearer token-name’) return new Response(‘Unauthorized’, { status: 401 });
}
return response;
}
// 특졀 경로에서만 미들웨어 실행하도록 설정: /home 페이지에 접근하면 미들웨어가 실행됩니다.
export const config = {
matcher: [‘/home’],
};