Redux 는 리액트 어플리케이션에서 전역 상태 관리를 위해 사용되는 라이브러리이다. 기본적인 개념은 어플리케이션의 모든 상태를 하나의 중앙 저장소에서 관리 하는 것이고, 상태를 변경할 때에는 Reducer 를 통해 Action 을 처리하는 방식으로 동작한다.
<App> (상태를 가지고 있음)
├── <Parent> (props 전달)
│ ├── <Child> (props 전달)
│ │ ├── <GrandChild> (드디어 props 사용)
React 가 기본적으로 제공하는 전역 상태 관리 기능!
Redux 없이도 상태를 전역에서 관리할 수 있게 해준다.
📌 Context API의 동작 과정
- Context 객체 생성 (createContext())
- Provider로 감싸서 전역 상태 제공 (Context.Provider)
- useContext()를 사용하여 전역 상태 접근
// 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
import { CountProvider } from "./CountContext"; // import 해와서
import GrandChild from "./GrandChild";
function App() {
return (
<CountProvider> // 감싸주면 끝!
<GrandChild />
</CountProvider>
);
}
export default App;
// 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;
(<Provider>...<Provider>...</Provider>) 구조가 복잡해진다.useContext() 를 계속해서 호출해야 함Redux 의 개념은 아직 정확히 이해를 못했기 때문에 RTK 로 바로 패스.
기존 Redux 의 복잡한 설정을 단순화해준다. 데이터 상태 관리의 흐름이 대략 이렇다, 라는 과정만 알아보기!
📌 RTK 의 데이터 흐름
- 사용자가 UI 에서 버튼을 클릭(이벤트 발생)
- Dispatch 함수 실행 (Action type, Payload)
- Action 실행
- Reducer 에서 상태 변경
- Store 가 새로운 상태 저장
- userSelector 로 변경된 상태를 가져옴
- 화면이 리렌더링됨
그럼 이제 이 과정을 단순화해주는 RTK 에 대해 알아보자.
📌 RTK 의 특징
- 코드가 훨씬 단순해짐 (자동으로 불필요한 반복 코드 제거)
- 비동기 상태 관리가 편리함 (createAsyncThunk 활용)
- Context API보다 더 확장성이 좋음 (대규모 프로젝트에서 효과적)
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-case 로 reducer 함수를 만들지 않고, 위 방식으로 직접 state 를 변경하는 방식을 적용할 수 있다!
configureStore() 를 사용하면 여러 개의 reducer 함수를 쉽게 합칠 수 있다.
import { configureStore } from "@reduxjs/toolkit";
import counterReducer from "./counterSlice"; // 만든 slice의 reducer 가져오기
const store = configureStore({
reducer: {
counter: counterReducer, // 여러 reducer를 등록 가능
},
});
export default store;
useSelector 와 useDispatch 를 사용해서 상태를 읽고 업데이트할 수 있다.
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 배울 생각을 하니, 쉬운 거 쓰다가 어떻게 다시 돌아오고 그러지, 싶어 막막하기도 하다. 하지만 둘을 같이 쓰는 경우도 있을테니 하루 빨리 익숙해져야만,,,