What is Redux?
Redux란?
Redux는 Javascript로 빌드된 앱을 위한 예측 가능한 상태 컨테이너다. Redux는 일관적으로 동작하고, 서로 다른 환경(서버, 클라이언트, 네이티브) 등 모든 환경에서 작동한다. 또한 상태 관리 뿐만 아니라 시간여행형 디버거를 제공한다.
예측이 가능하다는 것은 애플리케이션 내의 컴포넌트들이 가진 결합도를 낮추고 응집도를 올려준다는 말이다. 즉, 각각의 컴포넌트들은 자신이 할일에만 집중하게 되고, 애플리케이션을 관통하고 있는 state 정보는 중앙집중식으로 관리하게 도와주는 툴이다.
Redux 구조
Redux의 핵심은 store에 있다. 웹 애플리케이션이 사용자와 상호작용하면서 발생하는 모든 정보들은 store에 집중된다. 임의의 방법으로 store에 접근해서는 안되고 dispatch/reducer를 통해서 실제 정보가 저장되어 있는 state에 접근해서 수정하고 getState()를 통해 state 정보를 가져오게 된다.
dispatch()는 action과 현재 state를 reducer에게 보낸다. 그리고 현재 상태정보를 subscribe에 연결해서 HOT MODULE RELOADING이 가능하게 구현한다.
<form type='submit' onSubmit={store.dispatch({type:'create', payload:{title:title, desc:desc}})}
reducer는 state에 접근 및 수정이 가능한 함수다. 아래 처럼 작성한다. action을 받아서 현재 state의 정보를 업데이트 하는 역할을 한다.
function reducer(oldState, action){
// Todo Something ...
}
var store = Redux.createStore(reducer);
render()은 Javascript 코드를 가지고 웹 애플리케이션에 UI를 그리는 역할을 한다. getState() 함수를 실행시켜 state의 정보를 가져오고 UI를 그린다. state가 변경될 때 마다 render함수가 실행되어 새로운 정보를 기반으로 한 UI를 작성하기 위해서 필요한 함수가 store의 subscribe()다.
store.subscribe(render);
Redux 활용
Redux는 보통 Vue.js나 React 처럼 프론트엔드 프레임워크에서 사용하지만, 일반 HTML에서도 Redux의 기능을 맛볼 수 있다. 다음과 같은 HTML 소스가 있다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/4.2.0/redux.js"></script>
<title>Document</title>
</head>
<body>
<style>
.container{
border : 5px solid black;
padding : 10px;
}
</style>
<div id="red"></div>
<script>
function red(){
document.querySelector("#red").innerHTML = `
<div class="container" id="component_red">
<h1>red</h1>
<input type="button" value="Button" onclick="
document.querySelector('#component_red').style.backgroundColor='red';
">
</div>
`
}
red();
</script>
</body>
</html>
store 생성
Redux.createStore()를 통해 새로운 Store을 생성해준다. Store는 reduce를 인자로 받기 때문에 먼저 reducer 함수를 생성해준다. reduce는 state와 action 2가지 인자를 받는다. 초기 state 값은 "undefined"로 설정되어 있다. 이 경우 최초 state값을 {color : 'green'} 데이터로 설정해준다.
이제 store객체의 getState() 메소드를 사용하면 state값을 받아올 수 있게 되었다. 따라서 컴포넌트의 배경색을 state에서 가져와서 설정할 수 있다. store 객체에서 getState()를 실행한 결과를 state에 담고, background-color를 state.color로 변경해준다.
function reducer(state, action){
if(state === undefined){
return {color : 'green'};
}
}
var store = Redux.createStore(reducer)
console.log(store.getState())
function red(){
var state = store.getState();
document.querySelector("#red").innerHTML = `
<div class="container" id="component_red" style="background-color:${state.color}">
<h1>red</h1>
<input type="button" value="Button" onclick="
document.querySelector('#component_red').style.backgroundColor='red';
">
</div>
`
}
dispatch
reducer 함수는 최초 무조건 한번 실행되게 되어 있다. dispatch 함수는 코드 내부적으로 reducer를 호출하고 자신이 전달받은 인자를 reducer 함수에게 action 인자로 전달하게 된다. reducer는 이전의 state와 action을 받아서 새로운 state를 반환해준다.
store.dispatch()로 action값을 전달하고 있다. reducer 함수 내부에서 console을 찍어보면 state와 action 값을 확인할 수 있다.
function reducer(state, action){
console.log(state, action);
if(state === undefined){
return {color : 'green'};
}
}
function red(){
var state = store.getState();
document.querySelector("#red").innerHTML = `
<div class="container" id="component_red" style="background-color:${state.color}">
<h1>red</h1>
<input type="button" value="Button" onclick="
store.dispatch({type:'CHANGE_COLOR', color:'red'})
">
</div>
`
}
첫번째 라인은 최초 실행되었을 때 state와 action 값이다. 두번째 라인은 버튼을 한번 클릭했을 때 dispatch함수를 실행하여 reducer에 {type:'CHANGE_COLOR', color:'red'}를 action 인자로 전달한다. dispatch 함수에서 type은 무조건 있어야 하는 속성이다.
reducer
reducer는 state와 action 2개를 인자로 받아서 새로운 state로 반환해준다. 이 때 state 값에 접근해서 바로 변경하는 것이 아니라 state 객체의 복사본을 가져와야 한다. 이 때 사용하는 Javascript 내장 메소드가 Object.assign이다. 복사본의 state를 관리하지 않으면 UNDO/REDO를 사용하지 못하고 Time Traveler도 사용이 안된다.
Object.assign({}, {복사할 객체 1}, {복사할 객체 2})
만약 복사할 객체 1과 복사할 객체 2가 동일한 속성을 가지고 있다면 뒤에 추가되는 객체의 속성으로 뒤집어 써지게 된다. 이 속성을 이용해서 현재의 state를 안전하게 관리할 수 있게 된다.
function reducer(state, action){
if(state === undefined){
return {color : 'green'};
}
// 복사본을 관리할 state
let newState;
// action 타입에 따라 state를 관리할 수 있다.
if(action.type === 'CHANGE_COLOR'){
newState = Object.assign({}, state, {color : 'red'});
}
return newState;
}
subscribe
state가 변경될 때 마다 state를 사용하고 있는 컴포넌트도 변경되어야 한다. 이 때 사용하는 함수가 store.subscribe()함수다. UI를 그려주는 render() 함수를 state가 변경될 때 마다 실행하기 위해서는 subscribe()에 인자로 render 함수를 보내준다.
function render(){
var state = store.getState();
document.querySelector("#red").innerHTML = `
<div class="container" id="component_red" style="background-color:${state.color}">
<h1>red</h1>
<input type="button" value="Button" onclick="
store.dispatch({type:'CHANGE_COLOR', color:'red'})
">
</div>
`
}
store.subscribe(render);
아래 예시 처럼 컴포넌트들이 여러개일 때 Redux의 활용도가 올라간다. newState에 color 옵션을 action의 color 값으로 설정하고, 컴포넌트들을 subscribe에 등록해주면 끝이다. 이제 각 컴포넌트가 작동할 때 마다 state값이 action을 참고해서 변경되고, state가 변경될 때 마다 store.subscribe()를 호출해서 각 컴포넌트들이 해야 할일을 지정해준다.
function reducer(state, action){
if(state === undefined){
return {color : 'green'};
}
let newState;
if(action.type === 'CHANGE_COLOR'){
newState = Object.assign({}, state, {color : action.color});
}
return newState;
}
render_red();
render_grey();
render_white();
store.subscribe(render_red);
store.subscribe(render_grey);
store.subscribe(render_white);
function render_red(){
var state = store.getState();
document.querySelector("#red").innerHTML = `
<div class="container" id="component_red" style="background-color:${state.color}">
<h1>red</h1>
<input type="button" value="Button" onclick="
store.dispatch({type:'CHANGE_COLOR', color:'red'})
">
</div>
`
}
function render_grey(){
var state = store.getState();
document.querySelector("#grey").innerHTML = `
<div class="container" id="component_grey" style="background-color:${state.color}">
<h1>grey</h1>
<input type="button" value="Button" onclick="
store.dispatch({type:'CHANGE_COLOR', color:'grey'})
">
</div>
`
}
function render_white(){
var state = store.getState();
document.querySelector("#white").innerHTML = `
<div class="container" id="component_white" style="background-color:${state.color}">
<h1>white</h1>
<input type="button" value="Button" onclick="
store.dispatch({type:'CHANGE_COLOR', color:'white'})
">
</div>
`
}
Redux 정리
React에서 Redux를 사용하지 않을 때 state를 관리하는데 상당히 많은 자원이 소모된다. React에서는 데이터의 흐름이 단방향이기 때문에 하위 컴포넌트에서 상위 컴포넌트에서 전달받은 props를 수정할 수 없다. 수정해서는 안된다.
따라서 상위 컴포넌트의 state를 변경하기 위한 setState 따위의 함수를 props로 함께 전달받아야 하고, 이 과정이 중첩되고 중첩되면서 애플리케이션의 state는 미궁속으로 빠져버리기 쉽다.
굳이 비교하자면 React에서 useState는 dispatch()와 reducer() getState()와 맥을 같이 한다. 현재 state에 접근하여 state를 업데이트하고 값을 Read 하기 위한 함수다.
React의 useEffect()는 subscribe()와 같은 용도로 사용된다. 각 컴포넌트의 state가 업데이트 되었을 때 state를 사용하고 있는 컴포넌트가 수정되야 한다. 이 때 React에서는 HookAPI 생명주기에 따라 useEffect()를 사용하지만, Redux에서는 subscribe에 필요한 컴포넌트를 연결해주면 된다.
Reux를 사용하는 이유는 간단하다. state를 편리하게 관리하기 위함이고, 각 컴포넌트들의 의존도를 낮추기 위함이다. 성능이 좋은 애플리케이션일 수록 낮은 Coupling(결합도)과 높은 Cohesion(응집도)를 가진다. Redux는 이 목적을 달성하기 위한 유용한 도구다.
Reference
'Programming' 카테고리의 다른 글
[MongoDB] mongoexport / mongoimport / mongodump / mongorestore 사용법 (0) | 2022.06.03 |
---|---|
[Redux] 리덕스 CRUD ! (0) | 2022.06.01 |
[Database] MongoDB operators list, 연산자 종류 사용법 (0) | 2022.05.31 |
[Database] MongoDB CRUD ! (0) | 2022.05.31 |
[Database] MongoDB Document 데이터 BSON JSON 형식 차이점 (0) | 2022.05.31 |
댓글