서버 운영
배포 전 Server Component 동작 검증
Next.js App Router에서 Server Component가 실제로 올바르게 동작하는지 배포 전에 확인하는 방법.
Next.js 13+ App Router에서 Server Component는 서버에서만 실행되어야 한다. 하지만 클라이언트 코드가 섞여 들어가거나, 브라우저 API를 사용하면 문제가 생긴다.
로컬에서 프로덕션 빌드 실행
로컬 개발 환경에서는 에러를 놓칠 수 있다. 실제 프로덕션 빌드를 해본다:
npm run build
npm run start
# 또는 프로덕션처럼 빌드된 버전으로 로컬 테스트
개발 중에는 에러를 자세히 보여주지만, 프로덕션은 더 엄격하다.
Server Component vs Client Component
각 컴포넌트가 어디서 실행되어야 하는지 명확히 한다:
// Server Component (명시 안 해도 기본값)
export default function Page() {
const data = await fetch('...'); // 서버에서만 가능
return <div>{data}</div>;
}
// Client Component (명시 필요)
'use client';
export default function Button() {
const [count, setCount] = useState(0); // 클라이언트에서만 가능
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
Server Component에서 useState를 쓰거나, Client Component에서 fetch 없이 서버 데이터를 쓰려고 하면 에러가 난다.
브라우저 API 확인
Server Component에서 window, document, localStorage 같은 브라우저 API를 사용했나?
// 에러 발생
export default function Header() {
const isDark = localStorage.getItem('theme') === 'dark';
return <header>{isDark ? '🌙' : '☀️'}</header>;
}
// 수정: Client Component로 분리
'use client';
export default function Header() {
const isDark = localStorage.getItem('theme') === 'dark';
return <header>{isDark ? '🌙' : '☀️'}</header>;
}
서버에는 window 객체가 없으므로 런타임 에러가 난다.
useEffect와 훅
Server Component에서는 어떤 React 훅도 쓸 수 없다. useEffect, useContext, useState 등:
// 에러
export default function Timer() {
useEffect(() => {
console.log('mounted');
}, []);
return <div>Timer</div>;
}
// 수정: 'use client' 추가
'use client';
export default function Timer() {
useEffect(() => {
console.log('mounted');
}, []);
return <div>Timer</div>;
}
모든 훅 사용은 Client Component에서만 가능하다.
이벤트 핸들러
Server Component에서 onClick, onChange 같은 이벤트 핸들러를 직접 정의할 수 없다.
// 에러
export default function Form() {
return <button onClick={() => console.log('clicked')}>Click</button>;
}
// 수정
'use client';
export default function Form() {
return <button onClick={() => console.log('clicked')}>Click</button>;
}
이벤트가 필요하면 Client Component로 만든다.
서버 액션 사용
Server Component와 Client Component가 협력해야 한다면, Server Action을 쓴다:
// app/actions.ts
'use server';
export async function submitForm(data: FormData) {
await saveToDB(data);
return { success: true };
}
// app/page.tsx (Client Component)
'use client';
import { submitForm } from './actions';
export default function Form() {
return (
<form action={submitForm}>
<input name="name" />
<button>Submit</button>
</form>
);
}
Server Action을 통해 안전하게 서버 로직을 실행할 수 있다.
빌드 경고 확인
빌드 시 경고가 나는가?
npm run build
# ⚠️ Server Component ... uses browser API
경고는 무시하면 운영에서 버그가 된다. 모든 경고를 읽고 수정한다.
테스트 환경 점검
배포 전에 실제와 같은 환경에서 한 번 더 테스트한다:
- 프로덕션 빌드
- 실제 데이터베이스 (또는 스테이징)
- 실제 환경 변수
이 정도면 대부분의 Server Component 문제를 사전에 방지할 수 있다.