Redux부터 Tanstack Query까지 (1)

WOODIE·2025년 2월 20일

#React

목록 보기
2/3
post-thumbnail

Redux

Redux 는 리액트 어플리케이션에서 전역 상태 관리를 위해 사용되는 라이브러리이다. 기본적인 개념은 어플리케이션의 모든 상태를 하나의 중앙 저장소에서 관리 하는 것이고, 상태를 변경할 때에는 Reducer 를 통해 Action 을 처리하는 방식으로 동작한다.

✅ prop-drilling 방식

<App> (상태를 가지고 있음)
 ├── <Parent> (props 전달)
 │    ├── <Child> (props 전달)
 │    │    ├── <GrandChild> (드디어 props 사용)

⚠️ 단점

  • setState 를 사용하기 위해 엄마 컴포넌트에서는 사용하지도 않는 props 를 전달해야 함
  • 컴포넌트가 많아질수록 비효율적이고 재사용성이 떨어진다.

✅ Context API 방식

React 가 기본적으로 제공하는 전역 상태 관리 기능!
Redux 없이도 상태를 전역에서 관리할 수 있게 해준다.

📌 Context API의 동작 과정

  • Context 객체 생성 (createContext())
  • Provider로 감싸서 전역 상태 제공 (Context.Provider)
  • useContext()를 사용하여 전역 상태 접근

예시) CountContext.jsx

// CountContext.jsx (Context 생성)
import { createContext, useContext, useState } from "react";

// 1. Context 생성
const CountContext = createContext();

// 2. Provider 정의
export function CountProvider({ children }) {
  const [count, setCount] = useState(0);

  return (
    <CountContext.Provider value={{ count, setCount }}>
      {children}
    </CountContext.Provider>
  );
}

// 3. useContext() 를 사용할 수 있도록 헬퍼 함수 생성
// useContext(CountContext)를 직접 사용해도 됨!
export function useCount() {
  return useContext(CountContext);
}

App.jsx

// App.jsx
import { CountProvider } from "./CountContext"; // import 해와서
import GrandChild from "./GrandChild";

function App() {
  return (
    <CountProvider> // 감싸주면 끝!
      <GrandChild />
    </CountProvider>
  );
}

export default App;

GrandChild.jsx

// GrandChild.jsx
import { useCount } from "./CountContext"; // 헬퍼 함수 호출

function GrandChild() {
  const { count, setCount } = useCount();

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increase</button>
    </div>
  );
}

export default GrandChild;

⚠️ 단점

  • 상태가 많아지면 Context 파일이 많아지고, 관리가 어려워진다.
  • Provider가 많아질 경우 중첩(<Provider>...<Provider>...</Provider>) 구조가 복잡해진다.
  • useContext() 를 계속해서 호출해야 함

✅ Redux Toolkit (RTK) 방식

Redux 의 개념은 아직 정확히 이해를 못했기 때문에 RTK 로 바로 패스.
기존 Redux 의 복잡한 설정을 단순화해준다. 데이터 상태 관리의 흐름이 대략 이렇다, 라는 과정만 알아보기!

🪄 이디액타페써스!!!!!!

📌 RTK 의 데이터 흐름

  1. 사용자가 UI 에서 버튼을 클릭(이벤트 발생)
  2. Dispatch 함수 실행 (Action type, Payload)
  3. Action 실행
  4. Reducer 에서 상태 변경
  5. Store 가 새로운 상태 저장
  6. userSelector 로 변경된 상태를 가져옴
  7. 화면이 리렌더링됨

그럼 이제 이 과정을 단순화해주는 RTK 에 대해 알아보자.

📌 RTK 의 특징

  • 코드가 훨씬 단순해짐 (자동으로 불필요한 반복 코드 제거)
  • 비동기 상태 관리가 편리함 (createAsyncThunk 활용)
  • Context API보다 더 확장성이 좋음 (대규모 프로젝트에서 효과적)

1. createSlice()

State + Reducer + Action 을 한 번에 만들 수 있는 메서드

기존의 Redux 에서는 action , reducer , action creator 를 각각 만들어서 관리했어야 했는데, RTK 에서는 createSlice() 로 한 파일에서 한 번에 관리할 수 있다.

import { createSlice } from "@reduxjs/toolkit"; // 설치해서 import

// 초기 상태 정의
const initialState = {
  value: 0,
};

// createSlice()로 Slice 생성
const counterSlice = createSlice({
  name: "counter", // Slice의 이름
  initialState, // 초기 상태
  reducers: {
    increment: (state) => {
      state.value += 1; // 상태 증가
    },
    decrement: (state) => {
      state.value -= 1; // 상태 감소
    },
    increaseByAmount: (state, action) => {
      state.value += action.payload; // action에서 전달된 값만큼 증가
    },
  },
});

// 액션 & 리듀서 export
export const { increment, decrement, increaseByAmount } = counterSlice.actions;
export default counterSlice.reducer;

switch-casereducer 함수를 만들지 않고, 위 방식으로 직접 state 를 변경하는 방식을 적용할 수 있다!

2. Store (configureStore)

configureStore() 를 사용하면 여러 개의 reducer 함수를 쉽게 합칠 수 있다.

import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./counterSlice"; // 만든 slice의 reducer 가져오기

const store = configureStore({
  reducer: {
    counter: counterReducer, // 여러 reducer를 등록 가능
  },
});

export default store;

3. Redux 사용하기

useSelectoruseDispatch 를 사용해서 상태를 읽고 업데이트할 수 있다.

import React from "react";
import { useSelector, useDispatch } from "react-redux";
import { increment, decrement, increaseByAmount } from "./counterSlice";

const Counter = () => {
  const count = useSelector((state) => state.counter.value); // 상태 가져오기
  const dispatch = useDispatch(); // dispatch 함수 가져오기

  return (
    <div>
      <h1>Counter: {count}</h1>
      <button onClick={() => dispatch(increment())}>+</button>
      <button onClick={() => dispatch(decrement())}>-</button>
      <button onClick={() => dispatch(increaseByAmount(5))}>+5</button>
    </div>
  );
};

export default Counter;

이렇게 더 간단하게 자동화와 내장 기능이 있는 RTK 를,,,계속 복습해봐야 할 것 같은데, React Query 배울 생각을 하니, 쉬운 거 쓰다가 어떻게 다시 돌아오고 그러지, 싶어 막막하기도 하다. 하지만 둘을 같이 쓰는 경우도 있을테니 하루 빨리 익숙해져야만,,,

0개의 댓글