useRouter import 위치
// pages router 시절
import { useRouter } from 'next/router';// app router
import { useRouter } from 'next/navigation';React · Next.js 노트
app router 마이그레이션, 서버 컴포넌트, 하이드레이션 에러, 데이터 패칭 비교 등 실무에서 자주 부딪히는 함정을 before/after 비교 중심으로 정리했다.
Before / After 비교
잘못 쓰기 쉬운 패턴(before)과 권장 패턴(after)을 나란히 정리했다.
// pages router 시절
import { useRouter } from 'next/router';// app router
import { useRouter } from 'next/navigation';// Server Component에서 onClick 사용 (불가)
export default function Page() {
return <button onClick={() => {}}>클릭</button>;
}// Client Component로 분리
'use client';
export default function Button() {
return <button onClick={() => {}}>클릭</button>;
}useEffect(() => {
fetchData(userId);
}, []); // userId 변경에 반응 안 함useEffect(() => {
fetchData(userId);
}, [userId]); // userId 변경 시 재실행자주 만나는 함정
symptom
`Warning: Prop 'className' did not match.` — 서버와 클라이언트 렌더링 결과가 달라 발생.
fix
서버에서 실행되면 다른 값을 반환하는 코드(날짜, 랜덤값, window 객체 참조 등)를 `useEffect`로 이동시킨다.
symptom
리스트 항목이 올바르게 재렌더되지 않거나 경고 메시지 출력.
fix
각 항목의 고유 ID를 `key`로 사용한다. 배열 인덱스를 `key`로 쓰면 항목 순서 변경 시 버그가 생길 수 있다.
symptom
서버 컴포넌트에서 `useState`, `useEffect` 호출 시 에러 발생.
fix
`'use client'` 지시어를 파일 상단에 추가하거나, 해당 훅을 클라이언트 컴포넌트로 분리한다.
symptom
Next.js 14에서 동일한 URL을 여러 번 fetch해도 같은 결과가 나오거나, 최신 데이터가 안 오는 경우.
fix
`cache: 'no-store'` 또는 `next: { revalidate: N }`을 명시적으로 설정한다. 기본 캐시 동작을 의존하지 않는다.
symptom
컴포넌트 언마운트 후에도 타이머, 구독, fetch 등이 계속 실행되어 메모리 누수 또는 상태 업데이트 경고 발생.
fix
`useEffect` 반환 함수에서 `clearInterval`, `subscription.unsubscribe()`, `AbortController.abort()` 등을 호출한다.
관련 데브로그 글
React · Next.js 자주 묻는 것들
pages router도 Next.js 14에서 여전히 지원된다. 신규 프로젝트라면 app router 기준으로 시작하는 것이 낫다. 기존 프로젝트는 팀 상황에 따라 판단이 필요하다. 두 라우터를 혼용하는 것은 가능하지만 복잡도가 올라간다.