Next.js는 React 기반의 정적 및 동적 웹 애플리케이션을 구축할 수 있도록 설계된 프레임워크입니다. 이 글에서는 Next.js의 핵심 기능 중 하나인 서버사이드 렌더링(SSR)과 API 라우팅에 대해 심도 깊게 살펴봅니다. 프론트엔드 개발의 새로운 패러다임을 이끄는 기술인 만큼, 이론적인 배경부터 실무에 적용 가능한 예제까지 폭넓게 다룰 예정입니다.
목차
- 프론트엔드의 경계를 넘다 – React에서 Next.js로
- React와 Next.js의 관계
- 서버사이드 렌더링(SSR)이란?
- Next.js에서 SSR 구현하기
- Next.js API 라우팅이란?
- Next.js API Routes 구현 실습
- SSR과 API 라우팅의 통합 활용
- 보안, 인증, 미들웨어의 활용
- Next.js의 최신 기능 활용
- 서버사이드 렌더링과 API 라우팅으로 완성하는 모던 웹
1. 프론트엔드의 경계를 넘다 – React에서 Next.js로
웹 개발의 패러다임은 끊임없이 진화해왔습니다. 초기의 정적 HTML 페이지에서부터 동적 SPA(Single Page Application)로의 전환, 그리고 최근에는 사용자 경험과 성능을 모두 만족시키기 위한 SSR(Server-Side Rendering) 기반의 아키텍처로 발전하고 있습니다. 이러한 흐름 속에서 React는 사용자 인터페이스 구축에 최적화된 도구로 각광받았지만, 그 자체만으로는 SEO와 초기 로딩 속도 등 다양한 문제를 완벽히 해결할 수 없습니다.
바로 이 지점에서 등장한 것이 Next.js입니다. React 위에 구축된 프레임워크로서, Next.js는 CSR(Client-Side Rendering)의 한계를 넘어 SSR과 SSG(Static Site Generation), 그리고 API 라우팅 기능까지 내장하고 있어 프론트엔드와 백엔드의 경계를 자연스럽게 넘나드는 개발이 가능합니다.
이번 글에서는 Next.js가 왜 현대 웹 개발에서 중요한 도구로 자리 잡았는지를 설명하고, 특히 그 중에서도 핵심 기능인 SSR과 API 라우팅 기능을 중심으로 실무에서 어떻게 활용할 수 있는지를 구체적으로 다룰 것입니다. 단순한 기능 소개에 그치지 않고, 실제로 개발자가 구현할 수 있도록 코드 예시와 함께 설명하므로, React 경험이 있는 개발자라면 누구나 다음 단계로 나아갈 수 있는 기반을 마련할 수 있을 것입니다.
2. React와 Next.js의 관계
React는 Facebook(현 Meta)이 개발한 UI 라이브러리로, 컴포넌트 기반의 사용자 인터페이스를 구축하는 데 강력한 도구입니다. 그러나 React 자체는 클라이언트 사이드 렌더링(CSR)에 초점을 맞추고 있으며, 페이지 라우팅, 서버 사이드 렌더링, API 통신 등은 개발자가 별도로 구성해야 하는 단점이 있습니다. 이러한 이유로, 복잡한 애플리케이션을 개발하려면 여러 라이브러리를 조합하여 인프라를 직접 구축해야 하는 부담이 존재합니다.
Next.js는 이러한 React의 한계를 보완하기 위해 만들어진 프레임워크로, React의 기능 위에 서버 사이드 렌더링, 정적 사이트 생성, 파일 기반 라우팅, API 라우팅 등 다양한 기능을 기본으로 제공합니다. 결과적으로 Next.js는 React의 가능성을 확장하는 프레임워크라고 할 수 있으며, 프론트엔드 개발자들이 백엔드까지 통합적으로 설계할 수 있는 환경을 제공합니다.
두 기술의 관계를 정리하면 다음과 같습니다:
- React: UI 구성에 초점을 맞춘 라이브러리
- Next.js: React 기반의 웹 애플리케이션을 위한 풀스택 프레임워크
Next.js는 React의 장점을 그대로 유지하면서, 서버 측 렌더링(SSR)을 통한 SEO 향상, 빠른 페이지 전환, 클라이언트와 서버의 경계를 허무는 API 라우팅 등으로 개발자 경험(DX)과 사용자 경험(UX)을 모두 향상시키는 데에 큰 기여를 하고 있습니다.
특히 Next.js의 파일 기반 라우팅 시스템은 복잡한 라우터 설정 없이도 디렉토리 구조만으로 페이지와 API를 구성할 수 있게 하여, 생산성과 유지보수 측면에서 React 단독 사용 시보다 훨씬 우수한 결과를 만들어냅니다.
이제부터는 이러한 Next.js의 핵심 기능 중 하나인 서버사이드 렌더링(SSR)에 대해 좀 더 깊이 있게 살펴보겠습니다.
3. 서버사이드 렌더링(SSR)이란?
서버사이드 렌더링(Server-Side Rendering, SSR)은 말 그대로 브라우저가 아닌 서버에서 HTML을 생성하여 클라이언트에 전달하는 방식입니다. 이는 React와 같은 라이브러리의 기본 동작 방식인 CSR(Client-Side Rendering)과는 대조되는 개념으로, SEO 최적화와 초기 로딩 속도 개선에 큰 강점을 가지고 있습니다.
CSR 방식은 사용자의 브라우저에서 자바스크립트가 실행된 이후에 콘텐츠가 렌더링되기 때문에, 검색 엔진이 페이지의 내용을 정확하게 인식하지 못하는 경우가 많습니다. 반면 SSR은 서버가 완성된 HTML을 먼저 반환하기 때문에, 사용자와 검색 엔진 모두에게 더 빠르고 완전한 페이지를 제공합니다.
CSR vs SSR vs SSG vs ISR
Next.js에서는 SSR 외에도 다양한 렌더링 방식을 지원합니다. 각 방식의 차이점을 표로 정리하면 다음과 같습니다.
렌더링 방식 | 설명 | 사용 예시 |
---|---|---|
CSR (Client-Side Rendering) | 브라우저에서 모든 콘텐츠를 렌더링 | 로그인이 필요한 대시보드, SPA |
SSR (Server-Side Rendering) | 요청 시 서버에서 HTML을 생성 | 블로그, 뉴스 사이트, SEO가 중요한 페이지 |
SSG (Static Site Generation) | 빌드 시 HTML을 미리 생성 | 문서 페이지, 마케팅 랜딩 페이지 |
ISR (Incremental Static Regeneration) | 정적 생성 + 실시간 업데이트 | 자주 갱신되는 콘텐츠 페이지 |
이 중에서도 SSR은 동적 데이터가 필요하고 SEO 또한 중요한 페이지에서 가장 강력한 렌더링 전략입니다. 사용자 요청마다 서버에서 데이터를 가져와 최신 상태의 콘텐츠를 제공하기 때문에, 데이터 일관성과 사용자 경험을 동시에 보장할 수 있습니다.
Next.js에서 SSR을 사용할 때의 장점
- SEO 최적화: 검색 엔진 크롤러가 완성된 HTML을 쉽게 읽을 수 있음
- 빠른 초기 로딩: 첫 화면이 빠르게 로드되어 사용자 경험 향상
- 최신 데이터 반영: 매 요청마다 최신 데이터를 렌더링
- 보안 강화: 클라이언트가 아닌 서버에서 민감한 데이터 처리 가능
다음 섹션에서는 실제로 Next.js에서 SSR을 구현하는 방법과 관련 API인 getServerSideProps
에 대해 구체적으로 살펴보겠습니다.
4. Next.js에서 SSR 구현하기
Next.js에서는 SSR을 매우 간단하게 구현할 수 있도록 getServerSideProps
라는 특별한 함수를 제공합니다. 이 함수는 특정 페이지 컴포넌트에서만 사용할 수 있으며, 사용자가 페이지를 요청할 때마다 서버 측에서 실행되어, 데이터를 가져온 뒤 해당 페이지에 전달합니다.
getServerSideProps의 기본 구조
아래는 getServerSideProps
를 사용하는 기본적인 예제입니다.
export async function getServerSideProps(context) {
const res = await fetch('https://api.example.com/data');
const data = await res.json();
return {
props: {
data,
},
};
}
export default function Page({ data }) {
return (
<div>
<h1>서버사이드 렌더링 예제</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
이 구조에서 중요한 포인트는 다음과 같습니다:
- getServerSideProps는 페이지가 요청될 때마다 호출되며, 서버에서 실행됩니다.
- API 요청이나 데이터베이스 쿼리 등 서버에서만 가능한 작업을 수행할 수 있습니다.
- 리턴된
props
는 컴포넌트에 전달되어 렌더링 시 사용됩니다.
SSR을 사용해야 할 시점
SSR은 모든 페이지에 무조건 사용하는 것이 아니라, 실시간 데이터가 필요하거나 사용자 맞춤 정보가 포함되어야 하는 페이지에 사용해야 합니다. 예를 들어:
- 로그인한 사용자에게 개인화된 콘텐츠를 제공할 때
- 검색 엔진 최적화가 중요한 뉴스, 블로그, 쇼핑몰 페이지
- API 호출 시마다 최신 데이터를 기반으로 렌더링할 필요가 있을 때
주의사항
SSR은 매 요청마다 서버에서 렌더링이 일어나므로, 서버 부하와 응답 시간에 주의해야 합니다. 불필요한 API 호출을 줄이고, 캐싱 전략을 적절히 사용하는 것이 중요합니다.
실제 적용 예: 뉴스 콘텐츠 SSR
예를 들어, 실시간 뉴스 콘텐츠를 SSR로 구성하면 사용자에게 최신 기사를 빠르게 전달할 수 있으며, 검색 엔진에서도 정확히 인식할 수 있습니다.
export async function getServerSideProps() {
const res = await fetch('https://newsapi.org/v2/top-headlines?country=kr');
const data = await res.json();
return {
props: {
headlines: data.articles,
},
};
}
export default function News({ headlines }) {
return (
<div>
<h1>오늘의 주요 뉴스</h1>
<ul>
{headlines.map((article, index) => (
<li key={index}>{article.title}</li>
))}
</ul>
</div>
);
}
이제 SSR의 개념과 Next.js에서의 구현 방식에 대해 충분히 이해하셨을 것입니다. 다음으로는 Next.js의 또 다른 강력한 기능, API 라우팅에 대해 알아보겠습니다.
5. Next.js API 라우팅이란?
Next.js는 단순히 정적 웹사이트나 서버사이드 렌더링 페이지를 제공하는 것에 그치지 않습니다. 또 하나의 강력한 기능은 바로 API 라우팅 기능입니다. 이는 Next.js 프로젝트 내에서 별도의 백엔드 서버 없이도 API 엔드포인트를 정의하고, 그 안에서 서버 로직을 구현할 수 있도록 해줍니다.
React로만 개발할 경우에는 데이터를 가져오기 위해 외부 API 서버를 별도로 구성하거나 Node.js, Express 등의 백엔드를 함께 운용해야 합니다. 그러나 Next.js는 /pages/api
디렉토리 하위에 파일을 생성하는 것만으로도 즉시 API 라우트를 만들 수 있어, 프론트엔드와 백엔드를 하나의 프로젝트에서 통합적으로 관리할 수 있게 합니다.
API 라우팅의 구조
Next.js에서 API 라우트는 Next.js 서버에 의해 실행되는 Node.js 함수입니다. 아래는 API 라우트의 가장 기본적인 형태입니다.
// /pages/api/hello.js
export default function handler(req, res) {
res.status(200).json({ message: '안녕하세요, Next.js API입니다!' });
}
위 예제는 /api/hello
경로로 요청이 들어왔을 때 JSON
데이터를 반환합니다. 이 API는 서버에서 실행되며, CORS나 인증, DB 연동 등의 서버 로직을 자유롭게 구현할 수 있습니다.
HTTP 메서드에 따른 분기 처리
단일 API 라우트 파일에서 GET, POST, PUT, DELETE 등 다양한 메서드를 처리할 수 있습니다.
// /pages/api/user.js
export default function handler(req, res) {
if (req.method === 'GET') {
res.status(200).json({ name: '홍길동' });
} else if (req.method === 'POST') {
const { name } = req.body;
res.status(201).json({ message: `${name}님이 등록되었습니다.` });
} else {
res.status(405).end(); // Method Not Allowed
}
}
이처럼 단일 엔드포인트에서도 메서드에 따라 다양한 기능을 처리할 수 있어, 마이크로 API 아키텍처를 손쉽게 구현할 수 있습니다.
Next.js API 라우팅 vs 전통적인 백엔드
Next.js의 API 라우팅은 전통적인 Node.js + Express 백엔드와 비교할 때 다음과 같은 장점이 있습니다.
- 통합된 개발 환경: 프론트엔드와 백엔드를 하나의 프로젝트에서 관리
- 파일 기반 라우팅: 디렉토리 구조에 따라 API 경로 자동 생성
- 배포 간편화: Vercel 등에서 자동으로 서버리스 함수로 배포 가능
- 서버리스 친화적: 요청이 있을 때만 함수가 실행되어 효율적인 리소스 사용 가능
다만, 장기적인 백엔드 아키텍처가 필요한 프로젝트에서는 별도의 백엔드 서버와의 통합도 고려할 수 있으며, Next.js API는 프론트 중심의 경량 백엔드 또는 BFF(Backend for Frontend)로서 활용하는 것이 가장 이상적입니다.
이제 실제로 API 라우트를 구현하고, 클라이언트 측에서 이를 호출하는 방식에 대해 실습 예제를 통해 살펴보겠습니다.
6. Next.js API Routes 구현 실습
이번 섹션에서는 Next.js의 API 라우팅 기능을 실제로 구현해보며 구조와 동작 방식을 익혀보겠습니다. 실습을 통해 GET과 POST 요청을 처리하는 간단한 API를 만들고, 클라이언트 컴포넌트에서 이 API를 호출하여 데이터를 주고받는 과정을 함께 다뤄보겠습니다.
프로젝트 구조 이해
Next.js 프로젝트 내에서 API 라우트는 /pages/api
폴더에 생성하며, 이곳의 파일 이름이 그대로 URL 경로가 됩니다.
my-next-app/
├── pages/
│ ├── index.js
│ └── api/
│ └── greeting.js
위 구조에서 greeting.js
파일은 /api/greeting
경로로 접근할 수 있는 API 엔드포인트가 됩니다.
API 라우트 구현: 간단한 인사 메시지
아래는 GET과 POST 요청을 각각 처리하는 API 라우트의 예제입니다.
// /pages/api/greeting.js
export default function handler(req, res) {
if (req.method === 'GET') {
res.status(200).json({ message: '안녕하세요! Next.js API 라우트입니다.' });
} else if (req.method === 'POST') {
const { name } = req.body;
res.status(200).json({ message: `${name}님, 환영합니다!` });
} else {
res.status(405).json({ error: '허용되지 않은 메서드입니다.' });
}
}
이제 클라이언트 측 페이지에서 이 API를 호출하여 데이터를 받아보겠습니다.
클라이언트에서 API 호출하기
fetch
또는 axios
를 사용하여 클라이언트 컴포넌트에서 Next.js API 라우트를 호출할 수 있습니다. 다음은 기본적인 POST 요청 예제입니다.
// /pages/api-test.js
import { useState } from 'react';
export default function ApiTest() {
const [name, setName] = useState('');
const [response, setResponse] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
const res = await fetch('/api/greeting', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ name }),
});
const data = await res.json();
setResponse(data.message);
};
return (
<div>
<h2>API 테스트</h2>
<form onSubmit={handleSubmit}>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="이름 입력"
/>
<button type="submit">보내기</button>
</form>
<p>응답: {response}</p>
</div>
);
}
실행 결과
사용자가 이름을 입력하고 버튼을 클릭하면, Next.js의 API 라우트로 POST 요청이 전송되고, 서버에서는 해당 이름을 포함한 환영 메시지를 응답으로 반환합니다. 클라이언트는 이 메시지를 화면에 출력하여 즉시 사용자 피드백을 제공합니다.
요약
/pages/api
디렉토리 구조로 간단하게 API 라우트를 구성할 수 있습니다.- 단일 파일에서 다양한 HTTP 메서드 처리를 통해 유연한 백엔드 로직 작성이 가능합니다.
- 클라이언트에서는 기존 fetch 또는 axios를 사용해 Next.js API와 연동할 수 있습니다.
이처럼 Next.js의 API 라우팅은 프로젝트 규모와 관계없이 빠르게 서버 기능을 구현하고 프론트와 매끄럽게 통합할 수 있는 매우 유용한 기능입니다.
다음 섹션에서는 이렇게 구현한 SSR과 API 라우팅을 어떻게 통합하여 더욱 강력한 웹 애플리케이션을 만들 수 있는지 알아보겠습니다.
7. SSR과 API 라우팅의 통합 활용
지금까지 서버사이드 렌더링(SSR)과 API 라우팅을 각각 개별적으로 살펴보았습니다. 이번 섹션에서는 이 두 기능을 통합하여, SSR 페이지에서 Next.js의 API 라우트를 호출해 데이터를 가져오는 패턴을 알아보겠습니다. 이 방식은 프론트엔드와 백엔드를 같은 프로젝트 내에서 깔끔하게 분리하면서도, 밀접하게 연동할 수 있어 실제 프로젝트에서 매우 자주 활용됩니다.
SSR과 API를 통합하는 이유
SSR 환경에서는 직접 외부 API를 호출하는 방식도 사용 가능하지만, 내부 API 라우트를 호출하면 다음과 같은 장점이 있습니다:
- 비즈니스 로직의 재사용: 클라이언트와 SSR 모두 동일한 API 엔드포인트를 활용 가능
- 보안성 향상: 민감한 로직을 클라이언트가 아닌 서버 내부 API에서 처리
- 유지보수 편의성: API 기능을 한 곳에 집중시켜 코드 관리가 용이
실전 예제: SSR에서 내부 API 호출하기
예를 들어, 내부 API /api/products
에서 상품 목록을 반환하고, 이를 SSR로 렌더링하는 페이지를 구현할 수 있습니다.
// /pages/api/products.js
export default function handler(req, res) {
const products = [
{ id: 1, name: '노트북', price: 1200000 },
{ id: 2, name: '무선 마우스', price: 30000 },
];
res.status(200).json(products);
}
이제 위 API를 SSR 페이지에서 호출해 보겠습니다.
// /pages/products.js
export async function getServerSideProps() {
const res = await fetch('http://localhost:3000/api/products');
const products = await res.json();
return {
props: {
products,
},
};
}
export default function ProductsPage({ products }) {
return (
<div>
<h1>상품 목록</h1>
<ul>
{products.map((item) => (
<li key={item.id}>
{item.name} - {item.price.toLocaleString()}원
</li>
))}
</ul>
</div>
);
}
주의할 점
SSR에서 내부 API 라우트를 호출할 때는, 서버 환경에서의 경로 설정에 유의해야 합니다. 특히 도메인 이름을 명시적으로 기입해야 하며, 개발 환경에서는 보통 http://localhost:3000
을 사용하지만, 배포 시에는 절대 경로 또는 환경변수를 활용하는 것이 권장됩니다.
외부 API와의 차이
외부 API와 직접 통신하는 것보다 내부 API를 거치는 방식은 다음과 같은 차이가 있습니다:
구분 | 외부 API 호출 | 내부 API 호출 |
---|---|---|
성능 | 네트워크 경로가 길어 상대적으로 느릴 수 있음 | 같은 서버 내 호출이므로 빠름 |
보안 | 민감 정보 노출 위험 존재 | 서버 내부 처리 가능, 보안 유리 |
유지보수 | 각 위치에서 API 호출 방식 개별 관리 | 하나의 API에서 일관된 비즈니스 로직 관리 |
이처럼 SSR과 API 라우팅을 통합하면, Next.js의 진정한 강점을 발휘할 수 있습니다. 서버에서 데이터를 가져오면서도 프론트엔드-백엔드 간의 경계를 효과적으로 관리할 수 있기 때문입니다.
다음 섹션에서는 실제 현업에서 많이 사용하는 인증, 보안, 그리고 미들웨어 처리 방식에 대해 자세히 살펴보겠습니다.
8. 보안, 인증, 미들웨어의 활용
Next.js의 API 라우팅은 강력하지만, 실제 서비스 환경에서는 단순한 요청 처리 외에도 보안과 인증, 그리고 공통 로직을 관리하기 위한 미들웨어 구현이 필수적입니다. 이 섹션에서는 실무에서 반드시 고려해야 할 보안 요소와, API를 안전하게 보호하는 방법, 그리고 인증 흐름을 처리하는 전략을 다룹니다.
1. 인증(Authentication) 처리: JWT
가장 널리 사용되는 인증 방식 중 하나는 JWT (JSON Web Token)입니다. 사용자 로그인 시 토큰을 발급하고, 이후 요청에서 해당 토큰을 헤더에 담아 서버가 사용자를 식별하는 방식입니다.
// /pages/api/protected.js
import jwt from 'jsonwebtoken';
export default function handler(req, res) {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ message: '인증 토큰이 없습니다.' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
res.status(200).json({ message: `${decoded.name}님, 접근이 허용되었습니다.` });
} catch (err) {
res.status(403).json({ message: '유효하지 않은 토큰입니다.' });
}
}
위 예제에서 서버는 Authorization 헤더를 통해 전달된 JWT를 검증하고, 유효할 경우 사용자에게 응답을 반환합니다. 이 방식은 API의 무단 접근을 차단하고, 사용자의 신원을 안전하게 관리할 수 있는 기반을 제공합니다.
2. CORS 설정: 외부 접근 통제
Next.js API 라우트는 서버 함수로 실행되기 때문에, 다른 도메인에서 요청할 경우 CORS (Cross-Origin Resource Sharing) 정책에 따라 접근이 차단될 수 있습니다. 이 경우 CORS 설정을 명시적으로 추가해야 합니다.
// /pages/api/with-cors.js
export default function handler(req, res) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
if (req.method === 'OPTIONS') {
return res.status(200).end();
}
res.status(200).json({ message: 'CORS 설정 완료' });
}
실제 서비스에서는 *
대신 신뢰할 수 있는 도메인만 허용하는 것이 바람직합니다.
3. 미들웨어: 공통 로직의 중앙 집중화
API 요청마다 공통적으로 실행해야 하는 로직(예: 인증 검사, 로깅, 사용자 권한 체크 등)은 각 API 라우트에 중복해서 작성하는 대신, 미들웨어로 분리하여 관리하는 것이 효율적입니다.
// /lib/middleware/auth.js
import jwt from 'jsonwebtoken';
export function authMiddleware(handler) {
return async (req, res) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) return res.status(401).json({ message: '토큰이 없습니다.' });
try {
req.user = jwt.verify(token, process.env.JWT_SECRET);
return handler(req, res);
} catch {
return res.status(403).json({ message: '인증 실패' });
}
};
}
// /pages/api/secure-data.js
import { authMiddleware } from '../../lib/middleware/auth';
function handler(req, res) {
res.status(200).json({ message: `안녕하세요, ${req.user.name}님` });
}
export default authMiddleware(handler);
이 방식은 관심사의 분리(Separation of Concerns) 원칙에 따라, 각 기능을 독립적이고 재사용 가능하게 만들어 유지보수성과 확장성을 크게 향상시킵니다.
보안 체크리스트 요약
- JWT 기반 인증 토큰을 활용한 사용자 검증
- 민감 정보는 서버에서만 처리하고 클라이언트에는 최소한으로 전달
- CORS 설정을 통해 외부 접근 제어
- 미들웨어로 공통 로직을 모듈화하여 재사용
이처럼 보안과 인증 체계를 견고하게 구성하면, Next.js 기반의 프로젝트도 백엔드 수준의 안전성과 유연성을 확보할 수 있습니다.
다음은 Next.js의 최신 기능들을 활용하여 서버 렌더링을 더욱 발전시키는 방법에 대해 살펴보겠습니다.
9. Next.js의 최신 기능 활용
Next.js는 빠르게 발전하는 프레임워크입니다. 전통적인 pages
디렉토리 기반의 라우팅 외에도, 최근에는 App Router, React Server Components, Edge Functions 등의 혁신적인 기능들이 도입되며 서버 중심 아키텍처의 방향을 더욱 명확히 하고 있습니다. 이번 섹션에서는 이러한 최신 기능들을 간략히 소개하고, SSR 및 API 라우팅과 어떻게 연계할 수 있는지 살펴보겠습니다.
1. App Router와 파일 시스템 기반의 진화
Next.js 13부터 도입된 App Router는 기존의 pages
폴더 대신 app
폴더를 사용하여 라우트를 구성합니다. 이 구조는 더 명확한 계층화와 서버 컴포넌트 기반 렌더링을 자연스럽게 도입할 수 있게 합니다.
my-app/
├── app/
│ ├── page.tsx // 루트 페이지
│ ├── layout.tsx // 공통 레이아웃
│ └── about/
│ └── page.tsx // /about 경로
App Router를 사용하면 페이지를 서버 컴포넌트로 구성할 수 있으며, 기본적으로 서버에서 렌더링되는 페이지가 됩니다. 클라이언트 전용 로직이 필요한 경우에만 명시적으로 'use client'
를 선언하면 됩니다.
2. React Server Components (RSC)
React Server Components는 서버에서만 실행되는 컴포넌트로, 클라이언트 번들 크기를 줄이고, 데이터 fetching 로직을 클라이언트에서 제거할 수 있습니다. Next.js는 이 기능을 App Router와 함께 완벽히 지원하며, SSR의 성능과 유연성을 한층 끌어올릴 수 있습니다.
// app/page.tsx
import ProductList from './components/ProductList';
export default function HomePage() {
return (
<main>
<h1>서버에서 렌더링된 상품 목록</h1>
<ProductList />
</main>
);
}
// components/ProductList.tsx
export default async function ProductList() {
const res = await fetch('https://api.example.com/products', { cache: 'no-store' });
const products = await res.json();
return (
<ul>
{products.map(p => (
<li key={p.id}>{p.name}</li>
))}
</ul>
);
}
위 예제는 ProductList
컴포넌트가 서버에서 실행되어 데이터를 가져오고 렌더링까지 마친 뒤 클라이언트에 전달되는 구조입니다.
3. Edge Functions와의 비교
API Routes
는 Node.js 환경에서 실행되지만, Edge Functions는 전 세계 엣지 네트워크에서 실행되어 훨씬 빠른 응답성과 짧은 지연 시간을 자랑합니다. Next.js는 Vercel과 같은 플랫폼을 통해 손쉽게 엣지 실행 환경을 구성할 수 있습니다.
구분 | API Routes | Edge Functions |
---|---|---|
실행 환경 | Node.js 서버 | 글로벌 CDN 엣지 |
지연 시간 | 상대적으로 느림 | 매우 짧음 |
기능 제한 | 제한 없음 | 파일 시스템 접근 불가, 일부 라이브러리 제한 |
결론적으로, SSR이나 API 기능을 구성할 때 요구되는 성능과 범위에 따라 API Routes 또는 Edge Functions를 선택적으로 활용하는 전략이 필요합니다.
이제 우리는 Next.js의 전통적인 렌더링 방식부터 최신 App Router 및 서버 컴포넌트까지 폭넓은 기능들을 이해하게 되었습니다. 다음 마지막 섹션에서는 지금까지의 내용을 정리하고, 실제 프로젝트에 적용할 때 고려할 점들을 간결하게 되짚어보겠습니다.
10. 결론: 서버사이드 렌더링과 API 라우팅으로 완성하는 모던 웹
Next.js는 단순한 프론트엔드 프레임워크를 넘어서, 모던 웹 개발의 전체 흐름을 통합적으로 이끄는 강력한 도구입니다. 특히 서버사이드 렌더링(SSR)과 API 라우팅 기능은 프론트엔드와 백엔드의 경계를 허물며, 개발자에게 더 큰 자유와 유연성을 제공합니다.
이번 포스팅에서는 다음과 같은 핵심 요소들을 살펴보았습니다:
- React의 SPA 방식의 한계를 극복하기 위한 SSR의 필요성과 작동 원리
- Next.js의
getServerSideProps
를 통한 동적 렌더링 구현 방식 - 파일 기반의 API 라우팅과 이를 활용한 클라이언트-서버 간 통신
- SSR과 API 라우팅의 통합을 통한 실무 패턴 정립
- 보안과 인증, 미들웨어를 통한 API 보호 전략
- App Router, React Server Components, Edge Functions 등 최신 기능의 흐름
이러한 기능들을 유기적으로 조합하면, SEO에 강하고, 성능이 우수하며, 유지보수성과 확장성이 뛰어난 웹 애플리케이션을 구축할 수 있습니다. 특히 서버사이드 렌더링과 API 라우팅의 조합은 마치 별도의 백엔드 없이도 강력한 서버 기능을 구현하는 ‘프론트 중심 풀스택 개발’의 이상적인 형태라 할 수 있습니다.
지금 이 순간에도 수많은 개발자들이 Next.js를 통해 보다 효율적이고 탄탄한 웹 애플리케이션을 구현하고 있습니다. 중요한 것은 기술 자체가 아니라, 그 기술을 언제, 왜, 어떻게 선택하고 활용할 것인가에 대한 깊이 있는 이해입니다.
Next.js를 마스터한다는 것은 곧, 모던 웹 개발의 가장 실용적인 해답을 손에 넣는 것과 다름없습니다. 오늘 이 글이 여러분의 웹 개발 여정에 있어 의미 있는 이정표가 되기를 바랍니다.
더 나은 웹을 위한 여정, 그 중심에 Next.js가 있습니다.