Next.js App Router에서 getServerSideProps 대체 및 데이터 페칭법
- 원인: App Router는 서버 컴포넌트에서 직접 async/await를 쓸 수 있어
getServerSideProps/getStaticProps가 아예 없어짐 - 해결: 빌드 시 캐싱(SSG) → 기본
fetch/ 매 요청 최신화(SSR) →{ cache: 'no-store' }/ 주기 갱신(ISR) →{ next: { revalidate: N } } - 주의: getServerSideProps 안에 있던 인증 체크 로직은 이제 컴포넌트 함수 내부나 미들웨어로 옮겨야 함
const res = await fetch(url, { cache: 'no-store' }); // SSR과 동일하게 매번 재요청
↓ 왜 이런 에러가 나는지 원리 보기
Next.js 13 이상에서 App Router가 표준이 되면서, Pages Router에서 쓰던 getServerSideProps와 getStaticProps가 더 이상 동작하지 않습니다. 두 함수가 사라진 설계적 이유와, 이를 대체하는 데이터 패칭 방식을 정리했습니다.
왜 getServerSideProps와 getStaticProps가 사라졌을까
과거 Next.js는 페이지 컴포넌트 내부에서만 특정 정적 함수(getServerSideProps 등)를 실행하여 데이터를 미리 받아온 뒤, 이를 컴포넌트의 props로 넘겨주는 복잡한 직렬화 구조를 가졌습니다. 이로 인해 컴포넌트 간 깊이가 깊어지면 'Props Drilling(프로퍼티 대물림)'이 심해져 코드 관리가 몹시 까다로웠습니다.
App Router 환경에서는 서버 컴포넌트(Server Component)가 표준이 됨에 따라 컴포넌트 내에서 직접 비동기 함수(async/await)를 작성할 수 있게 되었습니다. 즉, 더 이상 페이지 최상단에서 무겁게 데이터를 모아 Props로 내려보낼 필요가 없어졌기 때문에 이전의 정적 함수들을 과감히 제거한 것입니다. 특히 마이그레이션 초기에는 getServerSideProps 안에 있던 인증 체크 로직을 어디로 옮겨야 할지 몰라 헤매는 경우가 많습니다.
Pages Router 시절에는 페이지 단위로 렌더링하며 정적 함수가 외부에서 데이터를 받아와 Props로 주입하는 구조였습니다. App Router에서는 컴포넌트 단위로 독립적으로 렌더링되며, 컴포넌트 내부에서 직접 비동기 fetch 통신과 캐싱을 제어할 수 있게 바뀌었습니다.
App Router 환경에서의 3가지 대체 데이터 패칭법
1. getStaticProps (정적 생성 - SSG) 대체법
한 번 빌드할 때 데이터를 가져와 평생 정적 페이지로 서비스하는 방식입니다. App Router에서는 기본 fetch 호출을 보내면 자동으로 빌드 시 캐싱되어 동일하게 작동합니다.
버전 참고: 아래 설명은 Next.js 14 기준입니다. Next.js 15부터는 fetch의 기본 캐싱 정책이 no-store로 바뀌어서, 명시적으로 { cache: 'force-cache' }를 지정해야 이 예시와 같은 SSG 동작이 됩니다. 프로젝트의 Next.js 버전을 먼저 확인하세요.
// 서버 컴포넌트 내에서 직접 async/await 사용
export default async function ProductPage() {
// 기본 fetch는 { cache: 'force-cache' }로 동작 (SSG 방식과 동일)
const res = await fetch('https://api.example.com/products');
const products = await res.json();
return (
<ul>
{products.map((p: any) => <li key={p.id}>{p.name}</li>)}
</ul>
);
}
2. getServerSideProps (실시간 서버 렌더링 - SSR) 대체법
사용자가 페이지를 요청할 때마다 서버가 매번 새로운 데이터를 패칭하여 화면을 그려주는 방식입니다. fetch API의 캐싱 옵션을 no-store로 설정하여 구현합니다.
export default async function UserProfilePage() {
// cache: 'no-store' 설정을 통해 매 요청 시 새로 패칭 (SSR 방식과 동일)
const res = await fetch('https://api.example.com/users/profile', {
cache: 'no-store',
});
const profile = await res.json();
return <div>반갑습니다, {profile.name}님!</div>;
}
3. ISR (Incremental Static Regeneration - 일정 주기 갱신) 대체법
기본적으로 정적 페이지를 유지하되, 지정된 시간(초)이 지나면 백그라운드에서 데이터를 조용히 새로고침해 주는 방식입니다. next.revalidate 옵션을 부여합니다.
export default async function NewsPage() {
// 60초마다 한 번씩 백그라운드에서 데이터를 최신화 (ISR 방식과 동일)
const res = await fetch('https://api.example.com/news', {
next: { revalidate: 60 },
});
const newsList = await res.json();
return <div>실시간 뉴스 업데이트 건수: {newsList.length}</div>;
}
Pages Router로 짜여있던 사내 대시보드를 App Router로 옮길 때, getServerSideProps 안에 있던 인증 체크 로직을 어디로 옮겨야 할지 몰라서 한동안 페이지 컴포넌트마다 똑같은 리다이렉트 코드를 복붙했습니다. 나중에 알고 보니 그 검증은 미들웨어로 한 번에 처리하는 게 정석이었고, 페이지 쪽 코드는 순수하게 데이터 패칭만 남기니 훨씬 깔끔해졌습니다.
요약: Pages Router와 App Router 비교표
과거 지식과 현재 설계 방식을 매칭해서 정리했습니다.
| 과거 함수 (Pages Router) | 최신 fetch 옵션 (App Router) | 렌더링 패러다임 |
|---|---|---|
| getStaticProps | fetch(url, { cache: 'force-cache' }) |
정적 사이트 생성 (SSG) |
| getServerSideProps | fetch(url, { cache: 'no-store' }) |
서버 사이드 렌더링 (SSR) |
| revalidate 옵션 포함 | fetch(url, { next: { revalidate: 60 } }) |
정적 점진적 갱신 (ISR) |
Next.js의 캐싱 메커니즘과 데이터 패칭 방식에 대한 자세한 내용은 공식 문서에서 확인할 수 있습니다.
fetch 캐싱 옵션도 결국 자바스크립트의 비동기 처리 흐름(Promise, async/await)을 알아야 정확히 와닿습니다. 기초가 헷갈리신다면 [호마다의 IT 개발 입문 블로그] 의 비동기 처리 기초 글도 참고해보세요.
댓글
댓글 쓰기