[React] Redux Toolkit 사용방법(feat. Context API)

seyun·2025년 11월 3일

react

목록 보기
11/12
post-thumbnail

Redux란?

  • 자바스크립트 기반 앱에서, 상태를 효율적으로 관리하기 위한 라이브러리(전역 상태 관리 툴)
  • 중앙 저장소(store)에 앱 전체 상태를 보관함

Redux Toolkit

  • 기존의 Redux의 복잡한 설정과 반복적인 부분을 줄여주기 위해 만든, Redux 유지/개선용 라이브러리
    • configureStore, createSlice, createAsyncThunk 등의 쉬운 API 제공
    • Immer 라이브러리 내장 → state 직접 변경해도 불변성을 유지해줌
    • thunk 등의 비동기 로직 기본 내장
  • 설치 방법
    npm install @reduxjs/toolkit react-redux

사용 방법

1. createSlice

  • 서로 관련 있는 상태(state)와 액션(action)을 하나로 묶은 조각을 생성함
  • 구성 요소
    1. 슬라이스 이름(action type의 이름)
    2. 초기 상태 값(일반적으로 객체값)
    3. 리듀서(상태 변경 함수를 작성) → 자동으로 액션 생성자가 됨
  • 예시
    // src/stores/cartSlice.js
    import { createSlice } from '@reduxjs/toolkit';
    
    const initialState = {
      totalNum: 0,
      totalPrice: 0,
      cartList: [],
    };
    
    export const cartSlice = createSlice({
      // (1) 슬라이스 이름
      name: 'cart',
      // (2) 초기 상태 값
      initialState: initialState,
      // (3) 리듀서
      reducers: {
        addToCart: (state, action) => {
          const itemToAdd = action.payload;
          const existingItem = state.cartList.find(
            (cartItem) => cartItem.id === itemToAdd.id
          );
          if (existingItem) {
            existingItem.quantity += 1;
          } else {
            state.cartList.push({ ...action.payload, quantity: 1 });
          }
          state.totalNum += 1;
          state.totalPrice += itemToAdd.price;
        },
        clearCart: () => initialState,
        // clearCart: (state) => {
    	  //   state = initialState; // 정상 작동하지 않음!
        // }
        // ...리듀서 함수들
    );
    
    // export. 모든 액션 생성자 함수 (컴포넌트에서 dispatch 하기 위해)
    export const {
      addToCart,
      clearCart,
    } = cartSlice.actions;
    
    // default export. 전체 리듀서 함수 (스토어에 등록하기 위함)
    export default cartSlice.reducer;
  • 위의 예시처럼, state값을 직접 변경해도, Immer 라이브러리가 자동적으로 새로운 객체를 만들어 불변성을 유지시켜줌
  • state 객체 자체를 변경하는 경우, clearCart 처럼, state 값에 새로운 값을 대입하는 것이 아니라, 반환 값으로 객체를 반환해야함
  • 액션 생성자 함수 및 전체 리듀서 함수를 export해서 사용

2. configureStore

  • 전역 데이터 저장소 (스토어)생성 함수
  • 리액트 앱의 모든 상태를 한 곳에 저장하고 관리
  • 모든 컴포넌트들이 스토어의 데이터를 공유함
  • 각 상태는 슬라이스로 나누어 관리
  • 예시
    // src/stores/store.js
    import { configureStore } from '@reduxjs/toolkit';
    import cartReducer from './cartSlice';
    
    export const store = configureStore({
      reducer: {
        cart: cartReducer,
        // user: userReducer, ... 처럼 확장해서 사용
      },
    });
  • 위의 예시처럼, 각 슬라이스에서 정의한 전체 리듀서 함수를 가져와서 store에 등록

3.

  • react-redux에서 제공하는 컴포넌트인, <Provider>로 최상위에서 감싸서 사용
  • 이 때, <Provider>의 props로 위에서 등록한 store을 전달하면, 하위 컴포넌트에서 접근 가능
  • 예시
    // main.jsx
    import { createRoot } from 'react-dom/client';
    import { Provider } from 'react-redux';
    import { store } from './stores/store.js';
    import App from './App.jsx';
    
    createRoot(document.getElementById('root')).render(
      <Provider store={store}>
        <App />
      </Provider>
    );
    

4. useDispatch (상태 변경)

  • 상태 변경을 하기 위해, 액션을 디스패치(전달) 함
  • react-redux에서 제공하는 useDispatch() 훅 사용
  • cartSlice.js에서 정의한 액션 생성자를 import해서, action 객체를 생성하여 사용
  • 예시
    // src/features/product/components/productItem/ProductItem.jsx
    import { useDispatch } from 'react-redux';
    import { addToCart } from '../../../../stores/cartSlice.js';
    
    function ProductItem({ product }) {
      const dispatch = useDispatch();
    
      const handleClickAdd = (item) => {
    	  // addToCart 함수 === 액션 생성자 함수
    	  // 생성 결과: { type: 'cart/addToCart', payload: item객체 }
        dispatch(addToCart(item));
      };
      
      return (
    	  // ...
    	  <button
          className="product-add-button"
          onClick={() => handleClickAdd(product)}
        >
          장바구니에 추가
        </button>
    	  // ...
      );
    }

5. useSelector (상태 읽기)

  • Redux store을 읽기 위해 react-reduxuseSelector() 훅 사용
  • useSelector()를 통해 상태 트리의 데이터를 읽어올 수 있음
  • 예시
    // src/features/cart/Cart.jsx
    import CartItem from './components/cartItem/CartItem';
    import { useDispatch, useSelector } from 'react-redux';
    import { clearCart } from '../../stores/cartSlice';
    
    function Cart() {
    	// store.js에서 등록한 이름으로 불러옴
      const cart = useSelector((state) => state.cart);
      // cart = { totalNum : 0, totalPrice: 0, cartList: [] }
    
      // ...
      return (
    	  // ...
    	  <div>
    	   {cart.cartList.map((item) => (
            <CartItem key={item.id} item={item} />
         ))}
        </div>
    	  // ...
      );
    }

Context API와의 차이점

Context API는 “전역 상태 관리 도구로 볼 수 없다”라는 이야기를 들어왔는데, 정확히 어떤 차이점이 있는지 알아보자.

  1. Context API (+useReducer)
    • Context API에서는 state의 일부 만을 사용한다고 해도 전체 state를 구독해야함
    • Context의 상태 중에서, 특정 컴포넌트가 state.a 만 사용한다고 가정할 때, state.b가 변경되어도 해당 컴포넌트는 리렌더링 됨 (불필요한 리렌더링)
  2. Redux Toolkit(전역 상태 관리 도구)
    • const a = useSelector((state) => state.user.a) 처럼 선택적 구독이 가능함
    • 따라서 state.user.b가 변경되어도 해당 컴포넌트는 리렌더링이 되지 않음

따라서 정리하자면,

상태의 범위가 작거나 지역적으로 상태를 관리하고 싶다면, 가벼운 설정이 가능한 Context API + useReducer를,

상태의 범위가 크고 전역적으로 상태를 관리하고자 한다면 Redux Toolkit 등의 전역 상태 관리 도구를 채택하여 사용하는 것이 적절한 방식이다.

profile
프론트엔드 개발자 준비 중

0개의 댓글