data의 흐름을 일방향화 하여 산재되어있는 구조와 흐름을 정리해주고,
이에 따른 코드의 유지보수와 무결성을 높여주는 도구.
React와 이름이 비슷하여 혼동할 수 있으나, React와는 완전히 별개의 라이브러리이다.
Redux를 활용하기 전 Javascript 코드의 data 흐름을 먼저 살펴본다.
//Vanilla
const add = document.getElementById("add")
const minus = document.getElementById("minus")
const number = document.querySelector("span")
let count = 0
number.innerText = count
const updateCount = () => {
number.innerText = count
}
const handleAdd = () => {
count = count + 1
updateCount()
}
const handleMinus = () => {
count = count - 1
updateCount()
}
add.addEventListener("click", handleAdd)
minus.addEventListener("click", handleMinus )
이 코드에서 변화하는 데이터 값인 count의 흐름을 나타내면 아래와 같다.
데이터를 저장하는 공간인 Store
Store와 같이 사용하는 reducer
Store(reducer) 구조
//Vanilla
import { createStore } from "redux"
const add = document.getElementById("add")
const minus = document.getElementById("minus")
const number = document.querySelector("span")
const reducer = () => {
return
}
const countStore = createStore(reducer)
console.log(countStore)
위 코드와 같이 Store를 생성한 후 reducer 함수를 연결해주면,
reducer 함수 내부의 data 값들을 상태반영할 수 있고 수정할 수 있게 된다.
Store를 log 출력을 통해 확인해보면 4가지 함수를 얻을 수 있다.
위 코드를 통해 Store를 log한 결과는 아래와 같이 4가지 reducer 함수이다.
이 4가지 함수를 활용하여 복잡한 State 관리를 하나의 functional Programming을 통해 구현할 수 있다.
getState를 통해 application의 상태관리에 반영할 data값을 바로 선언할 수 있다.
const reducer = () => {
return "HELLO"
}
const countStore = createStore(reducer)
console.log(countStore.getState)
특히 위와 같이 reducer 함수의 return 값에 반환할 문자열을 나타내주고 getState 함수를 활용하여 log를 확인하면
reducer 함수의 반환 값을 그대로 전달받을 수 있고, 이 상태값이 application의 data 값으로 그대로 반영된다.
Reducer를 통해 state를 update하고,
이를 return해서 createStore에 전달하여 data 변화 및 상태관리를 구현할 수 있다.
import {createStore} from 'reducer'
const reducer = (state) => {
modify state
return state.value
}
createStore(store.getState)
위와 같은 구조로 data를 변화하여 상태반영하고 이 반영한 상태값을 getState를 통해 전달해주면, 상태반영된 data값 즉 변화한 data값을 바로 활용할 수 있게 된다.
※ 유의할 점은 const reducer라는 함수는 사용자가 직접 정의해주어야 하며, 다만 createStore가 상태값을 그대로 받아올 수 있도록 구조에 유의하며 logic을 구성해야 한다.
reducer와 외부에서 소통하기 위해 만든 reducer 함수의 두번째 인자
const reducer = (state = initial_value, action) => {
return state
}
정리하면 선언한 reducer 함수 내에서
reducer 함수는 state와 action을 전달받을 수 있다.
const reudcer = (state = initial_value, action)
createStore는 기본적으로 4가지 함수를 가지고 있고,
이 중 dispatch 함수를 이용하면 해당 action을 reducer에게 전달할 수 있다.
위에서 보았듯이 createStore가 자체적으로 가지는 함수는 4가지가 있고,
const reducer = (state = 0, action) => {
console.log(action)
return state
}
const countStore = createStore(reducer)
countStore.dispatch({dispatced_action : "HELLO"})
위 logic 흐름을 살펴보면
- countStore을 통해 Store를 생성하였고, 여기에 reducer 함수를 넣어 해당 state와 action을 store로 연결해주었다.
- Store를 통해 선언된 reducer가 최초로 호출되어, state 값이 initial_value인 0으로 선언되고 action을 전달받는다(해당 action은 Store로 부터 받은 객체가 출력됨).
- 그 후 Store를 통해 dispatch(action)이 호출되어 reducer가 다시 작동하고 action이 reducer에게 최종적으로 전달된다.
- action 이후의 반영된 상태값, 즉 data가 return 되어 Store에 반영된다.
※ 유의할 점은 dispatch를 통해 전달하는 객체 값 자체가 action으로 default된 값이고, 이 action이 전달될 때 reducer가 호출된다.
즉, 외부에서 reducer를 호출하여 내부의 값을 수정할 수 있다.
※ 이는 기본적으로 createStore를 통해 reducer와 store를 연결해주었기 때문에 가능한 구조라는 것을 잊지말자.
const reducer = (state = 0, action) => {
if(action.type === "ADD"){
state = state + 1
}
if(action.type === "MINUS"){
state = state - 1
}
console.log(state)
return state
}
const countStore = createStore(reducer)
countStore.dispatch({type : "ADD"})
위 코드를 실행하게 되면
위와 같이 0, 1이 차례대로 나온다.
외부에서 reducer를 호출하여 내부 data 값을 변화시킬 수 있었다.
여기서 의문이 들 수 있는데, 최초 reducer가 호출되었을 때 바로 state값을 바꾸면 되지 않나는 생각이 들 수 있다.
결론부터 말하면 바꿀 수 있다.
action의 핵심은 reducer와의 소통, 외부에서 reducer를 호출할 수 있다는 점이다.
이는 dispatch를 통해 action 호출 시 reducer가 한 번 더 호출되는 원리에 기반하여 작동하기 때문에 가능한 것임을 기억하자.