🚀 Next.js + App Router 환경에서 서버 상태 및 URL 기반 라우팅 최적화
Next.js의 App Router가 등장하면서 페이지와 라우팅 관리 방식이 훨씬 유연해졌습니다. 동시에 서버 상태와 클라이언트 상태, 그리고 URL 기반의 라우팅까지 복합적으로 설계하는 일이 많아졌죠. 특히 React Query, SWR 같은 라이브러리와 함께 사용할 때 서버 상태와 URL 파라미터를 어떻게 조화롭게 구성하느냐에 따라 UX와 유지보수성이 크게 달라질 수 있습니다.
이번 글에서는 중급 개발자를 대상으로, App Router 기반 프로젝트에서 "서버 상태 관리"와 "URL 기반 라우팅"을 어떻게 최적화할 수 있는지 실전적으로 정리해봅니다.
1. App Router의 기본 이해와 특징
Next.js의 App Router(폴더 기반 라우팅)는 기존 Pages Router와 달리 파일 시스템 중심의 라우팅이면서도, React Server Components(RSC), 중첩 레이아웃, 로딩/에러 UI 분리, 동적 Segments 등을 지원합니다.
기본 구조 예시:
app/
├── layout.tsx
├── page.tsx
├── loading.tsx
├── error.tsx
├── dashboard/
│ └── page.tsx
└── [userId]/
└── page.tsx
App Router의 도입으로 서버 상태 및 URL 경로 설계 시 다음 요소들이 중요해졌습니다:
- 경로 기반 데이터 페칭 전략 (server component or client component)
- 쿼리 파라미터 ↔️ 상태 연동
- 동적 라우팅과 캐싱, prefetch
2. 서버 상태 관리 전략: React Query와의 조합
✅ 서버 상태란?
- API 요청을 통해 가져오는 데이터 (ex. 게시글 목록, 유저 정보 등)
- 다양한 컴포넌트에서 재사용됨
- 클라이언트 사이드에서도 캐싱, 동기화가 필요
✅ React Query 기본 세팅 (App Router 호환)
// app/providers.tsx
'use client'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
export function Providers({ children }: { children: React.ReactNode }) {
return (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);
}
// app/layout.tsx
import { Providers } from './providers';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return <Providers>{children}</Providers>;
}
이렇게 하면 App Router 기반에서도 useQuery, useMutation, useQueryClient 등을 전역으로 활용할 수 있습니다.
✅ 서버에서 데이터 프리패칭 (SSG or SSR)
App Router는 fetch를 서버 컴포넌트에서 직접 호출하여 데이터 프리패칭을 지원합니다.
// app/dashboard/page.tsx
export default async function DashboardPage() {
const data = await fetch('https://api.example.com/dashboard').then((res) => res.json());
return ;
}
➡️ React Query와 다르게 서버 컴포넌트에서는 fetch를 바로 사용할 수 있으며, 캐시도 자동 관리됩니다.
3. 클라이언트 라우팅 시 URL 상태와 동기화하기
URL에 검색 조건, 필터, 탭 등을 넣는 건 UX 측면에서 유리합니다:
- 브라우저 뒤로가기/앞으로가기 지원
- URL 공유 가능
- 상태 유지 가능 (리프레시해도 동일한 화면)
✅ useSearchParams 활용
'use client'
import { useSearchParams, useRouter } from 'next/navigation';
function FilterComponent() {
const searchParams = useSearchParams();
const router = useRouter();
const handleChange = (value: string) => {
const params = new URLSearchParams(searchParams);
params.set('category', value);
router.push(`?${params.toString()}`);
};
return (
<select onChange={(e) => handleChange(e.target.value)}>
<option value="all">전체</option>
<option value="news">뉴스</option>
</select>
);
}
➡️ 상태와 URL이 동기화됨 → SSR 시에도 동일한 UI 구성 가능
4. 서버 상태와 URL 파라미터 연동
React Query에서 쿼리 키를 URL 파라미터와 연동하면 매우 강력한 UX를 만들 수 있습니다.
const category = useSearchParams().get('category') || 'all';
const { data, isLoading } = useQuery(['posts', category], () =>
fetch(`/api/posts?category=${category}`).then(res => res.json())
);
- category가 바뀌면 자동으로 해당 쿼리가 다시 fetch됨
- 쿼리 캐싱 → 동일 파라미터 요청 시 서버 부하 감소
- 쿼리 무효화, prefetch 등도 쉽게 연결 가능
5. 렌더링 전략 최적화
✅ 어떤 페이지를 Server Component로?
- 초기에 데이터가 꼭 필요한 페이지 → Server Component 사용
- 로그인된 사용자 UI → Client Component 사용 (쿠키, 세션 등 클라이언트 필요)
✅ dynamic()으로 클라이언트 컴포넌트 나누기
const ClientOnly = dynamic(() => import('./ClientOnly'), { ssr: false });
➡️ 렌더링 성능 향상 + CSR 필요 영역만 클라이언트 렌더링
6. 실전 팁
- searchParams는 SSR에서 직접 사용 가능 (params.searchParams)
- URL 변경 시 스크롤 유지 or 이동 제어 필요할 경우 router.replace() 활용
- ReactQueryDevtools는 client 컴포넌트에서만 작동하므로 조건부 로딩 필요
✅ 마무리 요약
Next.js App Router 환경에서는 단순한 컴포넌트 단위 설계뿐 아니라 "서버 상태", "라우팅", "클라이언트 상태"가 유기적으로 연결되어야 진짜 UX 최적화가 가능합니다.
React Query와 App Router의 조합은 다음과 같은 이점을 제공합니다:
- SSR/CSR 혼합 구성 가능
- URL 파라미터와 상태 연동을 통한 UX 개선
- 중복 요청 제거 및 성능 최적화
- 클라이언트 상태와 서버 데이터를 명확하게 분리
➡️ 데이터 흐름이 명확하고, URL에 기반한 라우팅이 자연스러운 Next.js 앱을 만들고 싶다면 이 구조를 꼭 고려해보세요!
'일상이 개발' 카테고리의 다른 글
React 앱 성능 병목 실전 분석: DevTools부터 Profiler까지 완전 정복 (0) | 2025.04.11 |
---|---|
React 앱 성능 향상 필수 전략! 코드 스플리팅과 Lazy Loading 제대로 적용하기 (0) | 2025.04.10 |
React 상태 동기화 이슈 완전 정복: Form ↔ 전역 상태 ↔ URL 쿼리 흐름 정리 (0) | 2025.04.09 |
Next.js + React Query로 서버 상태 완벽 최적화하기: 실전 적용 가이드 (0) | 2025.04.09 |
Next.js와 React Query로 데이터 패칭 최적화하기: SSR, CSR, 캐싱까지 한 번에 잡기 (0) | 2025.04.08 |