React에서 Zustand Store와 Context Provider 함께 사용할 때 발생하는 불필요한 리렌더링 문제 해결하기
React에서 Zustand Store와 Context Provider 함께 사용할 때 발생하는 불필요한 리렌더링 문제 해결하기 문제 상황 호텔 검색 기능을 구현하면서 다음과 같은 문제가 발생했습니다: 이 코드에서 발생한 문제: 1. Provider가 리렌더링될 때마다 새로운 stor…
React에서 Zustand Store와 Context Provider 함께 사용할 때 발생하는 불필요한 리렌더링 문제 해결하기
문제 상황
호텔 검색 기능을 구현하면서 다음과 같은 문제가 발생했습니다:
// SearchProvider.tsx
export function SearchProvider({ children }: { children: ReactNode }) {
const store = createSearchStore(); // 매 리렌더링마다 새로운 store 생성
const searchParams = useSearchParams();
const { isLoggedIn } = useAuthStore();
const fetcher = useCallback(() => {
store.getState().fetchResults({ ...searchParams, isLoggedIn });
}, [searchParams, isLoggedIn, store]);
useEffect(() => {
if (searchParams) {
fetcher();
}
}, [searchParams, fetcher]);
return (
<SearchContext.Provider value={store}>
{children}
</SearchContext.Provider>
);
}
이 코드에서 발생한 문제:
-
Provider가 리렌더링될 때마다 새로운 store 인스턴스가 생성됨
-
store가 의존성 배열에 포함되어 있어 불필요한 fetcher 호출 발생
-
결과적으로 초기 렌더링 시 3번의 API 호출이 발생
원인 분석
Store 생성 위치의 문제
-
컴포넌트 내부에서 store를 생성하면 리렌더링마다 새로운 인스턴스가 생성됨
-
이는 Zustand store의 싱글톤 특성을 해치는 패턴
의존성 배열의 문제
-
store가 의존성 배열에 포함되어 있어 store 참조가 변경될 때마다 useEffect가 실행됨
-
이는 불필요한 API 호출을 유발
해결 방법
1. Store를 컴포넌트 외부로 이동
// searchStore.ts
export const createSearchStore = () => {
return createStore<SearchStore>()(
immer((set, get) => ({
// store 구현
}))
);
};
// SearchProvider.tsx
const store = createSearchStore(); // 모듈 레벨에서 한 번만 생성
export function SearchProvider({ children }: { children: ReactNode }) {
const searchParams = useSearchParams();
const { isLoggedIn } = useAuthStore();
const fetcher = useCallback(() => {
store.getState().fetchResults({ ...searchParams, isLoggedIn });
}, [searchParams, isLoggedIn]); // store 의존성 제거
useEffect(() => {
if (searchParams) {
fetcher();
}
}, [searchParams, fetcher]);
return (
<SearchContext.Provider value={store}>
{children}
</SearchContext.Provider>
);
}
2. 의존성 배열 최적화
-
store를 의존성 배열에서 제거
-
실제로 필요한 의존성만 포함 (searchParams, isLoggedIn)
개선 효과
성능 향상
-
불필요한 store 재생성 방지
-
불필요한 API 호출 감소
코드 품질 향상
-
Zustand store의 싱글톤 특성 유지
-
더 예측 가능한 렌더링 동작
메모리 사용 최적화
- 단일 store 인스턴스로 메모리 사용량 감소
주의사항
Provider의 생명주기
-
Provider는 일반적으로 애플리케이션의 최상위 레벨에서 한 번만 마운트됨
-
하지만 React Strict Mode나 개발 환경에서는 컴포넌트가 두 번 마운트될 수 있음
Store 초기화 시점
-
모듈 레벨에서 store를 생성하면 애플리케이션 시작 시 한 번만 초기화됨
-
이는 대부분의 경우 바람직한 동작이지만, 특정 상황에서는 동적 초기화가 필요할 수 있음
결론
Zustand store와 Context Provider를 함께 사용할 때는 store를 컴포넌트 외부에서 생성하고, 의존성 배열을 최적화하는 것이 중요합니다. 이는 불필요한 리렌더링을 방지하고 애플리케이션의 성능을 향상시키는 데 도움이 됩니다.