TIL - 20250909

juni·2025년 9월 9일

TIL

목록 보기
120/315

0909 React 성능 최적화와 Zustand 상태 관리


✅ 1. React 렌더링 최적화 기법

  • React는 부모 컴포넌트가 리렌더링되면 기본적으로 모든 자식 컴포넌트도 리렌더링합니다. 하지만 자식 컴포넌트가 받는 props가 변경되지 않았다면, 이 리렌더링은 불필요한 연산 낭비입니다. 이를 방지하기 위한 여러 최적화 기법이 있습니다.

➕ 1-1. React.memo: Props 기반의 리렌더링 방지

  • memo는 고차 컴포넌트(HOC)로, 컴포넌트를 감싸서 props가 변경되었을 때만 리렌더링하도록 만듭니다.

  • React는 이전 props와 현재 props를 얕게(shallowly) 비교하여 변경 여부를 판단합니다.

  • 사용 사례: props가 자주 변경되지 않는 무거운 UI 컴포넌트(e.g., 차트, 복잡한 목록 아이템)에 적합합니다.

    import { memo } from 'react';
    
    const MyComponent = memo(function MyComponent(props) {
      // 이 컴포넌트는 props가 변경될 때만 리렌더링됩니다.
      console.log('MyComponent RENDERED');
      return <div>{props.text}</div>;
    });

➕ 1-2. useCallback: 함수 재생성 방지

  • 문제점: JavaScript에서 함수는 객체이므로, 컴포넌트가 리렌더링될 때마다 내부에 선언된 함수는 새로운 메모리 주소를 가진 새로운 함수로 다시 생성됩니다. 이로 인해 memo로 감싼 자식 컴포넌트에 이 함수를 prop으로 전달하면, prop이 변경되었다고 인식하여 memo가 무력화됩니다.

  • useCallback: 함수를 메모이제이션(memoization)하여, 의존성 배열([])의 값이 변경되지 않는 한 함수를 재생성하지 않고 재사용하게 해주는 Hook입니다.

  • 사용법: const memoizedCallback = useCallback(() => { doSomething(a, b); }, [a, b]);

    const App = () => {
      const [count, setCount] = useState(0);
    
      // count가 변경될 때만 함수가 재생성됨
      const handleClick = useCallback(() => {
        console.log('Button clicked!');
      }, []); // 의존성 배열이 비어있으므로, 최초 1회만 생성됨
    
      return <MyButton onClick={handleClick} />; // MyButton은 memo로 감싸져 있음
    };

➕ 1-3. 컴포넌트 분리를 통한 최적화

  • 때로는 복잡한 Hook보다 컴포넌트 구조를 개선하는 것이 더 효과적인 최적화 방법일 수 있습니다.
  • 상태(state)가 변경되는 부분과 그렇지 않은 부분을 별도의 컴포넌트로 분리하면, 상태 변경의 영향을 받는 범위를 최소화하여 불필요한 리렌더링을 자연스럽게 줄일 수 있습니다.

➕ 1-4. key Prop을 이용한 상태 초기화

  • 문제점: 리스트의 순서가 바뀌거나 항목이 추가/삭제될 때, React는 key를 기준으로 컴포넌트를 재사용하며 props만 업데이트합니다. 이로 인해 컴포넌트 내부의 상태(state)는 그대로 유지되어 의도치 않은 동작을 유발할 수 있습니다.
  • 해결책: key prop을 고유하고 안정적인 값으로 설정하면, React는 key가 다른 엘리먼트는 완전히 다른 것으로 인식하여 이전 컴포넌트를 파괴하고 새로운 상태를 가진 새 컴포넌트를 생성합니다. 이를 역으로 이용하여, 특정 컴포넌트의 상태를 의도적으로 초기화하고 싶을 때 key 값을 변경하는 트릭을 사용할 수 있습니다.

✅ 2. Zustand: 간결하고 강력한 전역 상태 관리

  • ZustandContext API의 복잡성과 보일러플레이트(boilerplate)를 줄여주는, 매우 간결하고 직관적인 전역 상태 관리 라이브러리입니다. Redux나 MobX보다 훨씬 배우기 쉽습니다.

➕ 2-1. Store 생성 및 사용

  • Store: 애플리케이션의 상태(state)와 상태를 변경하는 액션(action)을 담고 있는 저장소입니다.

  • create 함수를 사용하여 스토어를 생성하고, 컴포넌트에서는 이 스토어를 Hook처럼 호출하여 상태와 액션을 가져올 수 있습니다.

  • Provider가 필요 없음: Zustand는 Context와 달리, 앱을 <Provider>로 감쌀 필요가 없어 설정이 매우 간단합니다.

    // store/counterStore.js
    import { create } from 'zustand';
    
    // 1. Store 생성
    const useCounterStore = create((set) => ({
      // State
      count: 0,
      // Actions
      increase: () => set((state) => ({ count: state.count + 1 })),
      decrease: () => set((state) => ({ count: state.count - 1 })),
    }));
    
    export default useCounterStore;
    
    // components/Counter.js
    import useCounterStore from '../store/counterStore';
    
    function Counter() {
      // 2. 컴포넌트에서 Hook처럼 호출하여 사용
      const { count, increase, decrease } = useCounterStore();
    
      return (
        <div>
          <span>{count}</span>
          <button onClick={increase}>+</button>
          <button onClick={decrease}>-</button>
        </div>
      );
    }

➕ 2-2. persist 미들웨어: 상태 영속성 유지

  • 문제점: 브라우저를 새로고침하면 모든 상태가 초기화됩니다.

  • persist 미들웨어: Zustand가 제공하는 미들웨어로, 스토어의 상태를 localStorage와 같은 웹 스토리지에 자동으로 저장하고, 페이지 로드 시 다시 불러옵니다.

  • 사용법: 스토어 생성 시 persist 함수로 감싸주기만 하면 됩니다.

    // store/authStore.js
    import { create } from 'zustand';
    import { persist } from 'zustand/middleware';
    
    const useAuthStore = create(
      // persist로 감싸기만 하면 됨
      persist(
        (set) => ({
          isLoggedIn: false,
          login: () => set({ isLoggedIn: true }),
          logout: () => set({ isLoggedIn: false }),
        }),
        {
          name: 'auth-storage', // localStorage에 저장될 키 이름
        }
      )
    );

📌 요약

  • React.memoprops가 변하지 않은 컴포넌트의 불필요한 리렌더링을 막아주는 가장 기본적인 최적화 도구입니다.
  • useCallback은 함수 prop이 계속 재생성되어 memo를 무력화시키는 것을 방지하기 위해 함수 자체를 메모이제이션합니다.
  • 때로는 복잡한 Hook보다 컴포넌트를 논리적으로 분리하는 것이 더 효과적인 최적화 방법일 수 있습니다.
  • ZustandProvider가 필요 없는 매우 간결한 문법으로 전역 상태 관리를 가능하게 합니다.
  • Zustand의 persist 미들웨어를 사용하면, 단 몇 줄의 코드로 로그인 상태와 같은 데이터를 새로고침 후에도 유지할 수 있습니다.

0개의 댓글