Next.js
React 상태 관리가 꼬였을 때 따라갈 흐름
화면이 예상과 다르게 렌더링되면 상태 변화를 시간 순서대로 추적하는 게 가장 빠르다.
React에서 상태 관리가 꼬이면 "왜 이 값이 이럴까" 하면서 한참을 헤맨다. 하지만 체계적으로 추적하면 원인을 빠르게 찾을 수 있다.
먼저 화면에 보이는 값을 메모한다
문제를 재현했을 때 실제로 보이는 값들을 모두 적어둔다:
- 예상: 상품 카운트가 5
- 실제: 상품 카운트가 0
- 예상: "로딩 중" 텍스트가 사라짐
- 실제: "로딩 중" 텍스트가 계속 보임
이 차이들이 모두 같은 원인에서 비롯됐을 가능성이 높다.
React DevTools Profiler로 렌더링을 기록한다
DevTools → Profiler에서 기록을 시작한 후 문제 행동을 재현한다.
기록된 렌더링 목록에서:
- 어떤 컴포넌트가 렌더링됐는가
- 몇 번 렌더링됐는가
- 렌더링 이유가 뭔가 (props 변경, state 변경, 부모 리렌더링)
상태가 바뀔 때마다 렌더링이 실행되는데, 예상과 다른 시점에 렌더링되면 상태 관리에 버그가 있는 거다.
각 컴포넌트의 props와 state를 점검한다
React DevTools의 Components 탭에서 의심 컴포넌트를 클릭하면:
- 현재 props 값
- 현재 state 값
을 볼 수 있다. 이들이 예상과 다르면 부모에서 전달하는 값이 잘못된 거다.
예를 들어 isLoading이 true여야 하는데 false라면, useState의 초기값 설정이나 setState 호출이 제대로 안 된 것이다.
useEffect 의존성을 확인한다
상태 변화 후 데이터를 가져오는 useEffect가 있다면 의존성 배열을 본다:
// 문제: userId가 바뀌어도 데이터를 다시 가져오지 않음
useEffect(() => {
fetchUser(userId) // userId 사용
}, []) // 빈 배열 - 한 번만 실행
// 해결: userId가 바뀔 때마다 실행
useEffect(() => {
fetchUser(userId)
}, [userId]) // userId 추가
의존성 배열을 잘못 설정하면 상태가 바뀌었는데 UI가 업데이트 안 된다.
상태 업데이트가 실제로 일어나는지 console.log로 확인한다
function MyComponent() {
const [count, setCount] = useState(0)
const handleClick = () => {
console.log('Before:', count) // 업데이트 전 값
setCount(count + 1)
console.log('After:', count) // 아직 old 값
}
useEffect(() => {
console.log('Updated:', count) // 실제 업데이트됨
}, [count])
}
콘솔에 로그가 나타나지 않으면 setState 함수 자체가 호출되지 않은 거다. 이벤트 핸들러 연결을 다시 확인해야 한다.
Context나 전역 상태 라이브러리를 의심한다
Redux, Zustand, Recoil 같은 상태 관리 라이브러리를 사용한다면:
- 실제로 action이 dispatch 되는가
- reducer에서 상태가 제대로 변경되는가
- store에 구독한 컴포넌트가 업데이트를 받는가
이런 라이브러리의 DevTools 확장을 사용하면 모든 상태 변화 기록을 볼 수 있다.
마지막으로 배포 환경을 테스트한다
로컬에서는 되는데 배포 후엔 안 될 수 있다:
npm run build
npm run start
production 빌드에서는 source map이 없어서 에러를 추적하기 어렵다. 에러 모니터링 서비스 (Sentry 등)를 설정해두면 배포 환경의 버그를 빠르게 발견할 수 있다.