[29] 리덕스 ver.자세한 설명
Redux에 대하여
Redux - 자바스크립트 앱을 위한 예측 가능한 상태 컨테이너. | Redux
자바스크립트 앱을 위한 예측 가능한 상태 컨테이너.
ko.redux.js.org
1. Redux
- 자바스크립트 앱을 위한 예측 가능한 상태 컨테이너
- data의 흐름을 일방향화하여 산재되어 있는 구조와 흐름을 정리해주고, 이에 따른 코드의 유지보수와 무결성을 높여주는 도구
- 일관적으로 동작하고, 서로 다른 환경에서 작동하며, 테스트하기 쉬운 앱을 작성하도록 도와줍니다.
- Redux는 React와 다른 라이브러리지만, 함께 사용할 수 있습니다.
- 애플리케이션의 모든 상태를 하나의 중앙 저장소(store)에서 관리하는 것이 특징입니다.
2. Redux의 핵심 개념
(1) Store
- 애플리케이션의 전체 상태(state)를 저장하는 객체
- 상태를 직접 수정할 수 없으며, 오직 특정 방식으로만 변경 가능
(2) Action
- 상태 변경을 일으키는 "사건"의 설명서
- 일반적으로 type이라는 필수 속성과 추가 데이터를 포함한 객체로 구성
const increment = { type: "INCREMENT" };
const addTodo = { type: "ADD_TODO", payload: "Buy groceries" };
(3) Reducer
- 순수 함수로, 현재 상태와 액션을 받아 새로운 상태를 반환
- 상태 변경의 로직을 정의
const counterReducer = (state = 0, action) => {
switch (action.type) {
case "INCREMENT":
return state + 1;
case "DECREMENT":
return state - 1;
default:
return state;
}
};
(4) Dispatch
- Action을 Store에 전달하는 함수
store.dispatch({ type: "INCREMENT" });
(5) Subscriber
- Store의 상태가 변경될 때마다 실행되는 함수
store.subscribe(() => {
console.log(store.getState());
});
3. Redux 동작 흐름
- UI에서 사용자가 액션을 발생시킴
- dispatch가 Action을 Store에 전달
- Store가 Action과 Reducer를 통해 새로운 상태를 생성
- UI가 새로운 상태를 받아서 업데이트됨
4. Redux를 사용하는 이유
- 예측 가능성: 모든 상태 변경이 Reducer를 통해 일어나므로 디버깅과 테스트가 쉬움
- 중앙 집중 관리: 애플리케이션의 상태를 한 곳에서 관리해 코드 구조가 명확해짐
- 확장성: 대규모 애플리케이션에서도 효율적인 상태 관리 가능
- 타임 트래블 디버깅: 상태 변화 과정을 시간 순으로 추적 가능
리덕스는 언제 사용해야 할까?
Redux를 사용할지 결정하려면 애플리케이션의 규모, 복잡성, 상태 관리 요구사항을 고려해야 합니다.
Redux는 강력한 도구지만 모든 프로젝트에 적합하지는 않습니다.
Redux를 사용해야 하는 경우 Redux를 사용하지 않아도 되는 경우 1. 애플리케이션 상태가 복잡할 때 1. 애플리케이션이 작거나 간단할 때 - 여러 종류의 상태를 관리해야 할 때 (예: 사용자 정보, UI 상태). - 상태가 몇몇 컴포넌트에만 영향을 미칠 때. 2. 상태가 여러 컴포넌트를 넘나들 때 (Prop Drilling 문제 해결) 2. 상태 공유가 거의 없을 때 - 깊은 컴포넌트 계층에서 상태를 쉽게 공유해야 할 때. - 대부분 상태가 로컬 컴포넌트에만 관련 있을 때. 3. 상태를 자주 업데이트해야 할 때 3. 프로젝트 초기 단계 - 사용자의 상호작용, 서버 요청 등으로 상태 변화가 빈번할 때. - 요구사항이 자주 바뀌는 초기에는 간단한 방법이 적합. 4. 상태를 중앙에서 관리해야 할 때 4. React만으로 충분할 때 - 팀원 간 협업이 많아 상태 추적이 중요할 때. - useState, useReducer, useContext로 충분할 때. 5. 비동기 상태 관리가 필요한 경우 5. 상태 관리가 Redux의 오버헤드가 될 때 - API 요청이나 서버 데이터 처리 등 비동기 작업이 많을 때. - Redux 설정 및 boilerplate 코드가 과도할 때. 6. 상태를 예측 가능하게 유지하고 싶을 때 - 상태 관리가 체계적이고 디버깅이 쉬워야 할 때.
5. Redux 사용 방법 (React 예제)
(1) Redux 설치
npm install redux react-redux
(2) Reducer 생성
const initialState = { count: 0 };
const counterReducer = (state = initialState, action) => {
switch (action.type) {
case "INCREMENT":
return { ...state, count: state.count + 1 };
case "DECREMENT":
return { ...state, count: state.count - 1 };
default:
return state;
}
};
(3) Store 생성
import { createStore } from "redux";
const store = createStore(counterReducer);
(4) React-Redux 연결
import { Provider } from "react-redux";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<Provider store={store}>
<App />
</Provider>
);
(5) 컴포넌트에서 상태와 액션 사용
- 상태 조회: useSelector
- 액션 디스패치: useDispatch
import { useSelector, useDispatch } from "react-redux";
const Counter = () => {
const count = useSelector((state) => state.count);
const dispatch = useDispatch();
return (
<div>
<h1>{count}</h1>
<button onClick={() => dispatch({ type: "INCREMENT" })}>+</button>
<button onClick={() => dispatch({ type: "DECREMENT" })}>-</button>
</div>
);
};
6. Redux의 중간 단계 : Middleware
- Redux의 동작을 확장할 수 있게 해줌.
- 가장 유명한 미들웨어:
- Redux Thunk: 비동기 작업 지원.
- Redux Saga: 복잡한 비동기 작업을 제너레이터 함수로 관리.
npm install redux-thunk
7. Redux Toolkit
- Redux Toolkit은 Redux의 복잡한 설정을 간소화한 라이브러리입니다.
- 기본 Redux 사용보다 더 간편하고 현대적인 방식으로 상태를 관리할 수 있습니다.
- RTK는 저장소 준비, 리듀서 생산과 불변 수정 로직 작성, 상태 "조각" 전부를 한번에 작성 등 일반적인 작업들을 단순화해주는 유틸리티를 포함하고 있습니다.
(1) 설치
npm install @reduxjs/toolkit
(2) 사용 예
import { createSlice, configureStore } from "@reduxjs/toolkit";
const counterSlice = createSlice({
name: "counter",
initialState: { count: 0 },
reducers: {
increment: (state) => { state.count += 1; },
decrement: (state) => { state.count -= 1; },
},
});
export const { increment, decrement } = counterSlice.actions;
const store = configureStore({
reducer: counterSlice.reducer,
});
export default store;
(3) React에서 사용: 기존 useSelector와 useDispatch로 동일하게 사용.
8. Redux의 한계와 대안
1. 복잡성 증가: 작은 애플리케이션에 사용하면 오히려 비효율적
-> 대안: Context API, Zustand, MobX 등이 있습니다.
2. 비동기 작업의 복잡성: Thunk나 Saga 등의 추가 라이브러리를 학습해야 함
9. Store가 제공하는 함수 - dispatch / subscribe / getState
dispatch, subscribe, getState의 실행 흐름은 다음과 같습니다.
- 사용자가 Action을 발생시킴 → dispatch(action) 호출.
- dispatch가 Action을 Reducer에 전달 → 새로운 상태 생성.
- 상태가 변경되면 subscribe에 등록된 함수가 실행.
- 필요하면 getState로 새로운 상태를 가져와 UI를 업데이트.
주요 포인트
- dispatch: 상태를 변경하기 위한 Action을 Store에 전달.
- subscribe: 상태 변경 시 특정 로직을 실행.
- getState: Store의 현재 상태를 읽음.
(1) dispatch
역할
- Action을 Store에 전달하여 상태(state)를 변경하는데 사용됩니다.
- Reducer를 호출하고 현재 상태와 전달된 Action을 기반으로 새로운 상태를 생성합니다.
사용법
store.dispatch(action);
- action: { type: "ACTION_TYPE", payload: someData } 형태의 객체.
예제
const incrementAction = { type: "INCREMENT" };
store.dispatch(incrementAction);
실행 흐름
- dispatch가 호출되면 Action이 Store로 전달됩니다.
- Store는 Reducer를 호출하여 현재 상태와 Action을 전달합니다.
- Reducer는 새로운 상태를 반환합니다.
- 상태가 업데이트되고, 구독된 컴포넌트(UI) 또는 로직이 실행됩니다.
(2) subscribe
역할
- 상태가 변경될 때마다 특정 콜백 함수를 실행하도록 등록합니다.
- 주로 UI를 상태 변화에 따라 업데이트하는 데 사용됩니다.
사용법
const unsubscribe = store.subscribe(listener);
- listener: 상태가 변경될 때 실행할 함수.
- unsubscribe: 구독을 취소하는 함수.
예제
const unsubscribe = store.subscribe(() => {
console.log("State has changed:", store.getState());
});
// 구독 취소
unsubscribe();
실행 흐름
- 상태가 변경되면 Redux는 등록된 모든 listener 함수를 호출합니다.
- UI 또는 기타 로직에서 getState를 통해 최신 상태를 가져옵니다.
(3) getState
역할
- Store에 저장된 현재 상태를 반환합니다.
- 상태를 읽기만 하며 변경하지 않습니다.
사용법
const currentState = store.getState();
예제
console.log("Initial State:", store.getState());
store.dispatch({ type: "INCREMENT" });
console.log("Updated State:", store.getState());
실행 흐름
- Store에서 현재 상태를 가져옵니다.
- 상태는 Reducer에 의해 변경되며, 이를 반영한 최신 상태를 반환합니다.
정리
Redux는 복잡한 상태 관리를 체계적으로 할 수 있도록 돕는 도구입니다.
하지만, 애플리케이션의 규모나 요구사항에 따라 꼭 필요한지 판단한 후 사용하는 것이 중요합니다.
Redux Toolkit을 사용하면 기존 Redux보다 더 쉽게 구현할 수 있으니, 처음 학습할 때는 Toolkit을 추천합니다. 😊
또한, dispatch, subscribe, getState를 조합하면 Redux Store와 효과적으로 상호작용할 수 있습니다.