0. Redux 등장배경
- MVC 패턴(양방향 데이터 흐름)이 주류였지만, 양방향 데이터 흐름은 복잡하고 데이터 흐름을 판단하기 불편했다. 이러한 해결방법으로 단방향 데이터 흐름인 Redux 가 등장했다.
🔸MVC 패턴(양방향 데이터 흐름)
- 어떤 action 이 발생하면 데이터 상태가 변경되고 그에 따라 디스플레이를 변경하는데 상태가 변경되었다는 정보를 View와 Model이 서로 양방향으로 주고 받는 형태이다.
1) Model : 어플리케이션의 데이터를 관리해주는 부분
2) View : 어플리케이션이 사용자에게 어떻게 보여지는지에 대한 관리
3) Controller : Model 의 자료와 View의 인터렉션을 총괄하는 어플리케이션 로직
그러나 몇몇 사람들이 MVC가 대규모 어플리케이션 개발에는 관리하기 어렵다고 느끼기 시작했다. 프로젝트가 단순하다면 위 그림처럼 간단하게 표현될 수 있다. 그러나 프로젝트 규모가 커지면 데이터 자료의 양과 화면이 많아지면서 Model과 View가 급격히 늘어나고 그에 따라 각각의 모듈들이 어떤 식으로 연결되어있는지 파악하기가 매우 어려워지기 마련이다.
🔸FLUX 패턴(단방향 데이터 흐름)
View는 MVC 패턴과 달리 데이터를 변경시키지 않고, Action을 넘겨준다. 이때 Action은 반드시 Dispatcher을 통해서 데이터가 변경된다. 변경된 데이터를 Store을 통해서 View가 전달받는다.
전역상태를 전부 하나의 저장소(store)안에 있는 객체 트리에 저장하며, 상태를 변경하는 것은 어떤 일이 일어날지 서술하는 객체인 action 을 내보내는(dispatch) 것이 유일한 방법이다. 그리고 액션이 전체 애플리케이션의 상태를 어떻게 변경할지 명시하기 위해서는 리듀서 작성이 필요하다.
1. Redux 는 보일러플레이트가 너무 많아
- 액션 타입, 액션 생성함수, 리듀서 3가지 종류로 코드를 준비해야한다.
- 세부적인 업데이트가 늘어날 수록 불변성을 지키기 위하여 ...state를 사용하는 것도 번거롭다.
// 액션 타입 설정
export const OPEN = 'msgbox/OPEN';
export const CLOSE = 'msgbox/CLOSE';
// 액션 생성함수
export const open = (message) => ({ type: OPEN, message });
// 초기 상태
const initialState = {
open: false,
message: '',
};
//reducer 정의
export default msgboxReducer(state = initialState, action) {
switch (action.type) {
case OPEN:
return { ...state, open: true, message: action.message };
case CLOSE:
return { ...state, open: false };
default:
return state; // 알 수 없는 액션 타입이 들어올경우 현재 상태 그대로 반환
}
}
2. Redux-toolkit
🔸등장배경
- 리덕스 스토어 환경 설정은 너무 복잡하고, 보일러플레이트(어떤 일을 하기위해 꼭 작성해야하는 상용구 코드)를 너무 많이 요구한다.
- Immer 가 내장되어있기 때문에 불변성을 유지하기 위해 번거로운 코드를 작성하지 않고 원하는 값을 직접 변경하면 알아서 불변성이 유지되면서 상태가 업데이트 된다.
- 이러한 단점을 해결하기 위해 리덕스 툴킷이 등장하고 공식문서에서도 리덕스 로직을 작성하는 표준 방식이 되기 위한 의도로 만들어졌다고 한다.
🔸기본적인 counter 앱 만들어보기
1) Redux slice 생성
import { createSlice } from '@reduxjs/toolkit';
const counterSlice = createSlice({
name: 'counter',
initialState: 0,
reducers: {
increment: (state) => {
return state + 1;
},
decrement: (state) => {
return state - 1;
}
},
});
export const { increment, decrement } = counterSlice.actions;
export default counterSlice.reducer;
- initialState를 통해 state의 처음 상태를 정의한다.
- 초기값을 변경하고 싶으면 reducers에서 액션(state 변경함수)을 설정한다. (increment,decrement)
- increment와 decrement 를 export 한다.
- slice는 slice.reducer로 내보낸다.
2) 스토어 생성
import { configureStore } from '@reduxjs/toolkit';
import counterReducer from './counterSlice';
const store = configureStore({
reducer: {
counter: counterReducer,
},
});
export default store;
3) 리액트 컴포넌트 생성
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from './counterSlice';
const Counter = () => {
const count = useSelector(state => state.counter);
const dispatch = useDispatch();
return (
<div>
<button onClick={() => dispatch(decrement())}>-</button>
<span>{count}</span>
<button onClick={() => dispatch(increment())}>+</button>
</div>
);
};
export default Counter;
- useSelector() 는리덕스의 상태를 조회할 수 있다.
- useDispatch() 는 생성한 액션을 발생시키며, 액션 생성함수를 가져온다.
4) 앱에 스토어 연결
import React from 'react';
import { Provider } from 'react-redux';
import store from './store';
import Counter from './Counter';
function App() {
return (
<Provider store={store}>
<Counter />
</Provider>
);
}
export default App;
참고자료
https://bestalign.github.io/translation/cartoon-guide-to-flux/
https://kjwsx23.tistory.com/552
https://react.vlpt.us/redux/03-prepare.html
'프론트엔드 개발 > React' 카테고리의 다른 글
React) 제어컴포넌트 비제어컴포넌트 (0) | 2023.09.13 |
---|---|
리액트) state의 모든 것 (props/state/로컬/전역/서버상태) (0) | 2023.08.16 |
React-Query 사용 방법 (0) | 2023.07.11 |
React) setState 함수의 모든 것 (0) | 2023.07.11 |
React) 커스텀 훅(Custom Hook) 만들기 (0) | 2023.07.10 |