저번 Context API에 대해 공부를 할 때 전역 상태관리라는 것을 간단하게 살펴보았다.
간략한 설명이라 조금 이해가 잘 안된거 같아 이번엔 예시 그림을 가져와봤다.
그림과 같이 Redux는 Store라는 하나의 저장 공간에 여러가지의 상태들을 관리한다.
왼쪽에 Redux를 사용하지 않는다면 Props들이 뒤죽박죽 흘러가기 때문에 어디서 에러가 발생했을 때 찾기가 꽤나 힘들어보인다.
Redux는 전역 상태를 관리하는 라이브러리라는 것을 확인하였고...
그렇다면 대체 이 Redux는 어떤 동작 원리로 전역으로 상태를 관리해준다는 말인가 ?
위의 그림이 대표적인 Redux의 흐름이다.
Action과 Reducer 그리고 Store과 UI의 흐름이 반복되는 그림인데
하나하나 그 들의 역할을 알아보도록 하자.
Action은 말 그대로 어떠한 행동을 말한다.
React에서 특정 행동을 했을 때 상태를 변화시키는 용도로 사용을 한다.
const countingReducer = (state = 0, action) => {
switch (action.type) {
case 'plus': {
return state + 1
}
case 'minus': {
return state - 1
}
default:
return state
}
}
위의 코드는 Action에 대한 코드라기 보단 변수명과 같이 Reducer이다.
이 Reducer 안에 switch문(혹은 if문)이 있는데 안에 case가 곧 action이다.
plus
라는 action을 취한다면 state에 +1을 하여 return을 하게 되고
minus
라는 action을 취한다면 state에 -1을 하여 return을 해준다.
만약 어떠한 action도 취하지 않는다면 변하지 않는 state를 반환해준다.
Action은 switch문에서 보시다시피 type이라는 것이 있는데 말 그대로 어떠한 action을 취할 것인지 알려준다.
그리고 그 액션에 따른 payload(전송해줄 데이터)도 넣어 사용할 수 있다.
Reducer는 State에 변화를 주는 함수정도로 생각하면 될거 같다.
Reducer 안에서 어떠한 Action으로 어떻게 State가 변하는지 하나의 함수로 만들어 놓은 것이다.
Store라는 저장소 안에 여러가지 Reducer들이 존재할 수 있다.
어떠한 Reducer는 로그인과 관련된 Reducer이고
또 다른 Reducer는 Count와 관련된 Reducer일 것이다.
const countingReducer = (state = 0, action) => {
switch (action.type) {
case 'plus': {
return state + 1
}
case 'minus': {
return state - 1
}
default:
return state
}
}
위의 코드는 Count를 담당하는 Reducer이다.
오로지 Count에 대한 State를 관리하는 전역관리인 셈이다.
const userLoginReducer = (state = false, action) => {
switch (action.type) {
case 'userLogin': {
return state = true
}
case 'userLogout': {
return state = false
}
default:
return state
}
}
위의 코드는 User의 Login 상태를 담당하는 Reducer이다.
이렇게 여러가지 Reducer들을 사용하여 원하는 State를 관리할 수 있다.
위에서 알아본 Reducer들을 모아 놓은 곳.
그곳이 바로 Store이다.
Store은 Reducer들과 그 안에 State들을 보관하는 하나의 저장소라고 생각하면 된다.
이 저장소 안에 필요한 State와 Reducer들을 가져다가 사용을 하는 것이다.
export const store = configureStore({
reducer: {
counter: counterSlice.reducer
},
});
이걸 보고 있는 사람들 중에 createStore가 아닌 configureStore가 등장해 조금 의아해하는 분들이 계실 수 있다.
Redux에서 Redux Toolkit이라는 더 편리하고 사용하기 쉬운 새로운 라이브러리를 만들었으니 기존 createStore을 사용하는 것을 권장하지 않는다.
라는 뉘앙스와 함께 createStore 중간에 밑줄을 쫙 그어 버린다.
사실 위쪽에 Switch문을 활용한 Reducer 또한 Redux-Toolkit의 형태가 아닌 기본적인 Redux의 형태이다.
😾: 왜 기본 Redux 형태로 설명하다가 Toolkit으로 설명하냐 ?!
개인적으로 Toolkit보단 기존 Redux가 조금 더 설명하기 쉽고 이해하기 쉬울 것 같아 앞에선 기존 Redux로 설명을 했다.
(필자는 이해할 때 Switch문을 보고 이해를 했기 때문에 기존 Redux가 Reducer 설명에 있어 적합하다고 생각함.)
그렇다면 Toolkit은 어떠한 형태의 Reducer를 가지는가 ?
const countSlice = createSlice({
name: "count",
initialState: 0,
reducers: {
plus: (state, action) => {
return state = state + 1;
},
minus: (state, action) => {
return state = state - 1;
},
},
});
Toolkit이 더욱 간결하다는 것은 동의하나 기존 Redux가 Redux를 처음 배우고자하는 사람에겐 더 이해하기 쉬운 거 같다.
이렇게 만든 Store를 App.js에 넣어 전역적으로 사용할 수 있게끔 해준다.
import { Routes, Route, BrowserRouter } from "react-router-dom";
import { Provider } from "react-redux";
import { store } from "./store";
function App() {
return (
<Provider store={store}>
<BrowserRouter>
<Routes>
</Routes>
</BrowserRouter>
</Provider>
);
}
Provider로 감싸 그 안에 있는 컴포넌트들은 Store를 사용할 수 있게 끔해준다.
(필자는 App.js에다가 Provider를 넣었는데 index.js에 넣어 App 컴포넌트를 감싸도 된다.)
앞서 Store 안에 Reducer들과 State를 정하여 보관해 놓았다면 이제 이것을 사용할 차례가 온 것이다.
useSelector
는 Store 안에 있는 Reducer들 중 원하는 State를 가져오도록 한다.
그렇게 가져온 State는 변수로 지정하여 사용을 할 수 있게 된다.
useDispatch
는 action을 사용할 수 있게 해주는 함수이다.
코드로 표현해보자 !
const dispatch = useDispatch();
//counter이라는 reducer 안에 state를 가져옴
const count = useSelector(state => state.counter)
return <div>
<button onClick={()=>{
dispatch((counterSlice.actions.plus()))
}}>
+
</button>
</div>
간단하게 Redux의 개념 정도만 글을 끄적였다.
기존 Redux랑 Redux-Toolkit이랑 같이 글을 적다보니 읽는 사람 입장에선 똥글이라고 생각할 수도 있을 거 같다...
(이럴거면 차라리 두개를 나눌껄 그랬나보다... 나의 욕심...)
Redux는 React에서 정말 중요하게 사용되며 대부분의 기업들이 채용 공고에 올려놓는 라이브러리 중 하나이다.
아주 기초적인 Redux의 사용만 익혔기 때문에 다음에 Redux-saga, Redux-persist, thunk, middleware 등 여러가지 사용법과 라이브러리들을 경험해보고 싶다.