React 18 Server Component 실험 중 마주친 hydration 이슈
문제 상황
Next.js 13의 app 디렉토리를 실험적으로 도입하면서 Server Component를 적용했다. 배포 후 콘솔에 hydration mismatch 경고가 반복적으로 발생했다.
// app/posts/[id]/page.tsx
export default function PostPage({ post }) {
return (
<article>
<time>{new Date(post.createdAt).toLocaleDateString()}</time>
<h1>{post.title}</h1>
</article>
);
}
서버에서는 UTC 기준으로 날짜를 렌더링하고, 클라이언트는 사용자 로컬 timezone을 적용해서 불일치가 발생했다.
해결 과정
- 서버에서 직렬화 처리: Date 객체를 ISO string으로 변환해 props로 전달
- 클라이언트 전용 컴포넌트 분리: 날짜 포맷팅은 'use client' 지시어를 사용한 별도 컴포넌트로 분리
// components/FormattedDate.tsx
'use client';
export default function FormattedDate({ isoString }) {
const [formatted, setFormatted] = useState('');
useEffect(() => {
setFormatted(new Date(isoString).toLocaleDateString());
}, [isoString]);
return <time suppressHydrationWarning>{formatted}</time>;
}
결론
Server Component는 강력하지만 서버/클라이언트 경계에서 데이터 직렬화에 주의가 필요하다. Date, Function, Symbol 같은 non-serializable 값은 반드시 문자열이나 JSON으로 변환해야 한다. ChatGPT가 출시된 지 2주밖에 안 됐는데, 이런 새로운 패러다임의 디버깅 자료는 아직 부족해서 공식 문서에 많이 의존하고 있다.