안녕하세요. Narvis2 입니다.
이번 시간에는 Redux를 사용하여 전역 상태를 관리하는 방법에 대하여 알아보겠습니다.
기존에는 React의 Context API를 사용하여 전역 상태를 관리했습니다.
Context API를 사용하면 웬만한 기능은 모두 구현할 수 있습니다. 하지만 전역적으로 다뤄야 할 상태가 다양해지고 복잡해질수록 준비해야 할 코드가 많아집니다.
특히 최적화가 필요한 경우에는 상태를 가져오는 Context와 상태를 업데이트하는 Context를 따로 관리해야 해서 준비할 코드의 양이 더욱 많아질 수 있습니다. 따라서 Redux에 관하여 알아보도록 하겠습니다.
🚩 Redux (리덕스)
Redux의 상태 업데이트 방법은useReducer와 비슷합니다.- 상태를 업데이트하는
리듀서함수를 기반으로 작동합니다.✅ 참고
- action: 변화를 정의하는 객체이며
type필드를 반드시 지니고 있어야 합니다. - state: 상태를 나타냅니다. 주의 ❗️ 리듀서 함수에서는
state와action값을 참조하여 업데이트된 상태를 반환해야 합니다.
예제 👇1 2 3
function reducer(state, action) { // action에 따라 업데이트된 상태를 반환 }
- action: 변화를 정의하는 객체이며
Context API보다 더 적은 코드로 기능을 구현할 수 있고, 선응을 최적화하기에도 용이합니다.미들웨어라는 기능도 사용할 수 있습니다.
1. Module(모듈) 작성하기
Reduc에서 상태 관리를 하기 위해Module을 작성해야 합니다.action 의 type:action type은 문자열입니다. 여기서의type은TypeScript와는 다릅니다.action객체를 만들 때type으로 사용될 값입니다.✅ 참고 :
action type은 주로 대문자로 선언합니다.
예제 👇1 2 3 4 5
// action 의 type const INCREASE = 'increase'; const DECREASE = 'decrease'; const INCREASE_BY = 'increase_by'; const DECREASE_BY = 'decrease_by';
action생성 함수✅ 참고
action생성 함수는action객체를 만들어주는 함수입니다.action객체를 만들때마다 직접 객체를 작성하는 게 아니라, 함수를 재사용하여 생성할 수 있도록action 생성 함수를 만듭니다.action 생성 함수는 추후Component에서 불러와야 하므로export해야 합니다.
예제 👇1 2 3 4 5
// action 생성 함수 export const increase = () => ({ type: INCREASE }); export const decrease = () => ({ type: DECREASE }); export const increaseBy = (by) => ({ type: INCREASE_BY, by }); export const decreaseBy = (by) => ({ type: DECREASE_BY, by });
- 초기 상태
예제 👇
1 2 3
const initialState = { value: 1 };
리듀서함수✅ 참고
state와action파라미터를 받아와서 업데이트된 상태를 반환합니다.useReducer의리듀서와 다른점은 초기 상태를 기본 파라미터 문법을 사용해state = initialState형식으로 지정해줘야 하며,default :케이스에서는 오류를 발생시키지 않고state를 반환하도록 만들어야 한다는 점입니다.리듀서함수에서는상태의 불변성을 유지하면서 업데이트 해줘야 합니다. 따라서state를 직접 수정하면 안 되고, 새로운 값을 만들어서 반환해줘야 합니다.주의 ❗️
- 객체에 여러 값이 있다면
{...state, value: state.value + 1}과 같은 형식으로불변성을 유지해야 합니다. - 만약 배열인 경우에는
push,splice처럼 배열에 직접 변화를 일으키는 함수를 사용하지 말고concat,filter처럼 새로운 배열을 생성시키는 함수를 사용해야 합니다. 리듀서함수는 추후 여러리듀서를 합쳐야 하므로 이 또한export해줘야 합니다. 주로export default로 내보내줍니다.
예제 👇1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// 리듀서 함수 export function counter(state= initialState, action) { switch (action.type) { case INCREASE: return { value: state.value + 1 }; case DECREASE: return { value: state.value - 1}; case INCREASE_BY: return { value: state.value + action.by }; case DECREASE_BY: return { value: state.value - action.by }; default: return state; } }
- 객체에 여러 값이 있다면
- 전체 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// action 의 type
const INCREASE = 'increase';
const DECREASE = 'decrease';
const INCREASE_BY = 'increase_by';
const DECREASE_BY = 'decrease_by';
// action 생성 함수
export const increase = () => ({ type: INCREASE });
export const decrease = () => ({ type: DECREASE });
export const increaseBy = (by) => ({ type: INCREASE_BY, by });
export const decreaseBy = (by) => ({ type: DECREASE_BY, by });
// 초기 상태
const initialState = {
value: 1
};
// 리듀서 함수
export function counter(state= initialState, action) {
switch (action.type) {
case INCREASE:
return { value: state.value + 1 };
case DECREASE:
return { value: state.value - 1};
case INCREASE_BY:
return { value: state.value + action.by };
case DECREASE_BY:
return { value: state.value - action.by };
default:
return state;
}
}
2. Root 리듀서 만들기
Redux를 사용할 때는 기능별로Module을 작성해야 합니다.- 각
Module에 있는 여러리듀서를 하나의리듀서로 합쳐야 합니다. 리듀서를 하나로 합칠 때는combineReducers라는 함수를 사용합니다.- 여러
리듀서를 합친리듀서를루트 리듀서라고 부릅니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 3개의 리듀서가 있다고 가정
counter : { value: 1 }
todos: [{
id: 1, text: '리덕스', done: false
}]
setting: { filter: 'all' }
// 리듀서를 하나로 합침 combineReducers 사용
import {combineReducers} from 'redux'
// combineReducers에 합치고 싶은 reducer 함수 넣기
const rootReducer = combineReducers({
counter,
todos,
setting
});
export default rootReducer;
3. Store(스토어) 만들기
Root Reducer를 만든 다음에는store를 만들어야 합니다.React Project에서는 단 하나의store를 생성합니다.Redux store에서는Redux에서 관리하는 모든 상태가 들어있으며, 현재 상태를 조회할 수 있는getState함수와action을 일으킬 수 있는dispatch함수가 들어있습니다.✅ 참고 :
dispatch(increase())와 같이action객체를 인자에 넣어서 함수를 호출하면 준비한reducer가 호출되면서 상태가 업데이트 됩니다.
예제 👇1 2 3 4
import {createStore} from 'redux' import rootReducer from './modules/' const store = createStore(rootReducer); export default store;
4. Provider로 React Project에 Redux 적용
React Project에서 작성한store를 사용하려면Provider Component를 사용하여 전체 앱을 감싸줘야 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import {createStore} from 'redux';
import {Provider} from 'react-native';
import rootReducer from './modules';
const store = createStore(rootReducer);
function App() {
return (
<Provider store={store}>
{/* .. */}
</Provider>
);
}
export default App;
5. useSelector과 useDispatch로 Component에서 Redux 연동하기
Provider를 사용하여Redux를 적용✅ 참고
useSelector:Hook을 사용해리덕스의 상태를 조회할 수 있습니다.useSelector Hook에서는 셀렉터 함수를 인자로 넣어줍니다.- 이 함수에서
state파라미터는store가 지니고 있는 현재 상태를 가리킵니다. - 셀렉터 함수에서는 이
Component에서 사용하고 싶은 값을 반환하면 됩니다.
useDispatch:action을 발생시켜 상태를 업데이트할 수 있습니다.- 셀렉터를 사용해 조회한 상태가 바뀔 때마다
Component가 렌더링됩니다. useSelector는 최적화되어 있기 때문에 우리가 원하는 상태가 바뀔 때만 리렌더링합니다.주의 ❗️ 이
component에서 의존하지 않는Redux에서 관리하는 다른 상태가 변경됐을 때는 렌더링되지 않습니다. 이 점이Context를 사용했을 때와 가장 다른 차이점 입니다.
- 셀렉터를 사용해 조회한 상태가 바뀔 때마다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import {useSelector, useDispatch} from 'react-redux';
import {increase, decrease} from './modules/counter/';
function Counter() {
const value = useSelector(state => state.counter.value);
const dispatch = useDispatch();
const onPressIncrease = () => {
dispatch(increase());
};
const onPressDecrease = () => {
dispatch(decrease()));
};
}
export default Counter;
🚩 Redux (리덕스) 개념 정리
Redux를 사용할 때는 상태의 종류별로Module을 작성해야 합니다.Module에는action type,action 생성 함수,Reducer를 선언합니다.- 여러
Reducer를combineReducers로 합쳐Root Reducers를 만듭니다. createStore를 사용하여store를 만듭니다.Provider를 사용하여React Project에Redux를 적용합니다.useSelector와useDispatch를 사용하여Redux의 상태를 조회하거나 업데이트합니다.
🚩 마치며
이번 포스팅에서는 Redux에 관하여 알아보았습니다.
Redux는 useReducer와 비슷하며 Redux를 통해 전역 상태를 관리하면 더 적은양의 코드로 최적화된 상태를 관리할 수 있습니다.
다음 포스팅에서는 Redux Toolkit에 대하여 알아보도록 하겠습니다.