Post

React에서 Zustand Store와 Context Provider 함께 사용할 때 발생하는 불필요한 리렌더링 문제 해결하기

React에서 Zustand Store와 Context Provider 함께 사용할 때 발생하는 불필요한 리렌더링 문제 해결하기 문제 상황 호텔 검색 기능을 구현하면서 다음과 같은 문제가 발생했습니다: 이 코드에서 발생한 문제: 1. Provider가 리렌더링될 때마다 새로운 stor…

2025-06-02·ReactcontextproviderTypeScriptZustand

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>
  );
}

이 코드에서 발생한 문제:

  1. Provider가 리렌더링될 때마다 새로운 store 인스턴스가 생성됨

  2. store가 의존성 배열에 포함되어 있어 불필요한 fetcher 호출 발생

  3. 결과적으로 초기 렌더링 시 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를 컴포넌트 외부에서 생성하고, 의존성 배열을 최적화하는 것이 중요합니다. 이는 불필요한 리렌더링을 방지하고 애플리케이션의 성능을 향상시키는 데 도움이 됩니다.