차례
1. redux 이거 왜 쓰는가?
2. redux 어떻게 쓸 수 있는가.
3. 마무리
redux를 쓰는 이유는 크게 두가지로 나눌 수 있다.
1. state의 중앙 집중 관리 시스템
2. redux dev tool을 이용한 관리
앞선 포스트에서도 말했듯이, redux는 모든 state를 하나의 store에 넣어서 보관한다. 이렇게 하게 되면 state가 산재되어 관리되는 것보다 훨씬 효율적으로 관리되고 생성, 변경, 삭제가 용이하게 되는 것이다.
특히 react같은 경우에는 useState를 통해 각 컴포넌트에서 state들을 관리할 수도 있다. 물론 프로젝트에 따라 다르겠지만 state를 중앙집중 관리 할 것인가 각 컴포넌트에서 관리할 것인가에 따라 useState를 사용할 것인가, redux를 사용할 것인가로 나뉘게 된다.
useState를 사용하는 경우 상위 컴포넌트에서 하위 컴포넌트에 까지 state를 전달하기 위해서는 Props를 통해서 전달해줘야 하고 이를 Props drilling이라고 하는데, 이렇게 되면 그 사이에 있는 모든 컴포넌트들을 거쳐가야 하고, 그럴 때마다 re-rendering되어 성능 저하가 일어나게 된다.
하지만 redux를 사용하게 되면 하나의 store에 모든 state가 저장되어 있고 각 컴포넌트는 그 store를 subscribe하고 있으면 store의 state가 변경될 때 마다 다른 컴포넌트들을 거치지 않고도 정보를 받아 올 수 있다는 장점이 있다.
redux를 사용하고 Store를 만들 때 아래와 같이 작성해놓으면,
let store = Redux.createStore(
reducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
);
chrome에서 아래와 같은 dev tool을 사용할 수 있는데,
이를 사용하게 되면, redux에서 store에게 전달된 action의 버전관리를 할 수 있으며, 각각의 버전들이 어떤 차이를 가지고 있는지 보여준다. 뿐만 아니라, 버전관리 시스템에 따라 이전 버전으로 돌아갈 수도 있다.
이러한 점을 이용해서 어플리케이션을 사용하는 이용자가 어떠한 문제가 생겼을 때, 그 사용자가 겪고 있는 문제의 상태를 그대로 받아올 수도 있다.
먼저 우리가 redux를 쓴다는 것은, 결국 store를 만들어서 store안의 state를 바꾸는 것이 redux를 사용하는 핵심이다. 아래 코드를 보면 더욱 자세히 알 수 있다.
function reducer(state, action) {
//...
}
const stroe = Redux.createStore(reducer)
reducer 함수는 dispatch에 의해서 action이 들어오게 되면, reducer가 그 action값과 기존에 있던 state값을 참조해서 새로운 state 값을 만들어 주는 것이다.
하지만, state가 없어서 정의되지 않을 시에는 초기 state값을 전달해줘야 한다.
function reducer(state, action) {
// initial state
if (state === undefined) {
return { color: 'yellow' }
}
}
const stroe = Redux.createStore(reducer)
이렇게 reducer에 의해서 store에 저장된 initial state값은 getState 함수를 통해서만 render할 수 있다. store안에 있는 state 값을 확인하기 위해 콘솔에 아래와 같이 출력해보면,
function reducer(state, action) {
if (state === undefined) {
return { color: 'yellow' }
}
}
const store = Redux.createStore(reducer)
console.log(store.getState())
아래와 같이 콘솔에 출력되게 된다. 즉, store에 저장된 state 값을 가져오기 위해서는 꼭 store.getState를 사용해야 한다.
state를 변경하기 위해서는 action을 만들어야 한다. 그리고 그것을 dispatch에게 제출하면 dispatch는 state와 action을 reducer를 호출한다.
store.dispatch({type:'CHANGE_COLOR', color : 'red'})
이렇게 작성하게 되면 우리가 초기에 작성했던 reducer 함수에 전달되게 된다. 근데 이렇게만 보면 궁금할 수 있다.
아니 reducer는 인자로 state와 action을 받는데 dispatch에는 하나의 인자밖에 없는데?
예리한 지적이다. 사실 reducer에서 받는 state값은 현재 store에 있는 state 값이기 때문에 이를 콘솔에서 확인해 보면 아래와 같이 나오는 걸 볼 수 있다.
맨처음은 state가 없기 때문에 undefined 으로 뜨는 것이고 두번째는 버튼 클릭 후 dispatch가 실행 됐기 때문에, 현재 state를 store안에 있는 state로 찍히는 것을 확인 할 수 있다.
여기서 잠깐!
우리가 store의 state값을 받아서 변경해서 그것을 다시 return 해주는 방식도 있지만, 그렇게 하게 되면 state의 원본을 망치는 것이기 때문에 redux가 가지고 있는 redo와 undo 그리고 redux dev tool의 기능들을 사용할 수 없게 한다.
따라서, 현재 stote에 있는 state를 복제하고 그 state를 변경한 후에 return 해주는 것이 좋다.
아래와 같이 예시 코드를 보면 state 원본을 받아 변경 후 return 해준다면, 원본을 건드는 것이기 때문에 좋은 방법이 아니다.
function reducer(state, action){
console.log(state,action)
if(state === undefined){
return {color:'yellow'}
} if (action.type === 'CHANGE_COLOR') {
state.color = 'red'
}
return state
}
아래와 같이 하게 되면, state를 복제하여 원본을 유지할 수 있게 된다.
객체 복사 method는 Object.assign()인데, 첫번째 인자로는 무조건!!! 빈 객체를 전달해야 한다.
즉, Object.assign( { }, {복제할 객체}, {복제할 객체} ...)
function reducer(state, action){
console.log(state,action)
if(state === undefined){
return {color:'yellow'}
}
let newState;
if (action.type === 'CHANGE_COLOR') {
newState = Object.assign({},state, {color : 'red'})
}
return newState
}
action을 통해 dispatch로 전달되어 reducer에 의해 새로운 state 값이 return 되었을 때, 자동으로 UI를 그려주기 위해서는 render 함수를 호출 해야 하는데, 여기 render 함수는 말그대로 UI를 그려주는 함수를 render 함수라고 한다. 이를 사용하기 위해서 아래와 같이 코드를 작성하면 된다.
function red() {
// UI를 그리는 함수
}
store.subscribe(red)
redux에서 reducer 함수가 하는 역할은 이전 state값과 dispatch가 전달해주는 action의 값을 이용해서 store의 state값을 새로운 state 값으로 변경해준다.
그리고 중요한 건, return 할 때는 state 원본을 바꾸는 것이 아니라 복제한 값을 가공하여 return 해야지 redux의 최대 효용을 활용할 수 있다.
이렇게 redux로 state를 관리하게 되었을 때는 action을 store에 dispatch 하고 그에 따라서 자신이 어떻게 변화되어야 하는지에 대한 내용을 UI를 그리는 함수안에 작성하고 그 함수를 subscribe하게 되면 state가 바뀔 때 마다 바뀌었음을 통보를 해주게 된다. 그렇기 때문에 이때마다 자신의 모습을 바꿔 줄 수 가 있는 것이다.
-끝-
[출처]
https://opentutorials.org/module/4078/24938 (생활코딩 Redux를 이용해서 애플리케이션 만들기)