Redux
는 상태 관리(state management)
로 유명한 라이브러리이다.
Redux
라는 기술에 대해 처음 인지했을 당시,
Redux
는 주로 React
에서 사용되기 때문에 React
전용의 프레임 워크라고 생각했다.
(그리고 Redux
, React
의 명칭이 둘 다 'Re'로 시작해서, Redux
는 React
에 속해있는 기술(?)인 줄만 알았다. 바보😶🌫️)
하지만 사실 Redux
는 JavaScript
의 많고 많은 라이브러리 중 하나였으며, React
와는 독립적인 존재라는 것을 알게 됐다.
- Redux: A Predictable State Container for JS Apps
- 출처: Redux 공식 홈페이지
for JS Apps.
Redux 공식 홈페이지의 대문에도 적혀 있듯이,
Redux
는 JavaScript
로 만들어진 애플리케이션들의 state(상태)
를 관리하기 위한 라이브러리이다.
Redux
는 절대 React
에 종속된 라이브러리가 아니다.
흔히 React
에서 사용하는 Redux
는 React Redux라는 기술이다.
React Redux
는 Redux
개발팀에서 공식적(Official)으로 개발한 React
전용 Redux
이다.
이번 글에서는 React Redux
를 사용해보기 전에, Vanilla JavaScript
에서 Redux
를 간단히 사용해보며,
Redux
의 동작원리에 대해 공부해보고자 한다.
(부제: Redux
는 어떻게, 왜 개발된 것일까?)
Redux
를 왜 사용하는 것인지, 어떻게 개발된 것인지 이해하기 위해서는 Redux
의 탄생비화에 대해서 짚고 넘어갈 필요가 있다.
Redux
는, Redux
의 창시자인 Dan Abramov씨가 Flux
라고 하는 아키텍쳐에 Reducer
라는 것을 결합하여 만들었다고 한다.
Redux = Reducer + Flux
Flux에 대해 알아보기 전에, Flux
가 개발되게끔 원인을 제공(?)한 MVC 패턴
에 대해 먼저 알아보자.
Flux
와 Redux
가 등장하기 전에 통용되던 프론트엔드 개발 디자인 패턴은 MVC(Model-View-Controller)
라는 패턴이었다.
MVC 패턴
은 양방향 데이터 흐름을 가진 개발 디자인 패턴으로,
애플리케이션을 3가지의 부분으로 나누어, 각각의 부분이 독자적인 처리를 행하도록 하는 디자인 패턴을 말한다.
그 각각의 부분은 다음과 같다.
MVC (Model-View-Controller)
- Model: 데이터를 관리하고 변환, 처리하는 역할.
- View: Model의 상태를 참조하여 사용자에게 눈에 보이는 형태로 표시함. 렌더링하는 역할. 인터페이스의 역할을 하기 때문에 때때로 사용자로부터 데이터를 입력(Action)받기도 함.
- Controller: 유저로부터 입력(Action)을 받아, Model과 View에게 데이터를 변환, 업데이트하도록 지시하는 역할.
MVC 패턴
은 양방향 데이터 흐름을 가졌다.
Model
이 변경되면 View
가 변경되며,
유저에 의해 View
에서 의도치 않게 Action
이 일어난다면
데이터를 관리하고 변환, 처리하는 역할을 가진 Model
은 View
로부터의 데이터 또한 처리해야한다. (바쁘다 바빠)
중요한 문제는, MVC 패턴
에서 애플리케이션의 규모가 커지면 커질수록 애플리케이션의 구조가 아주 복잡해진다는 것이다.
위의 그림과 같이 애플리케이션의 규모가 커질수록
한 개의 Model
이 여러 개의 View
를 조작하거나, 한 개의 View
가 여러 개의 Model
을 조작하게 되는 등
버그를 감지하기 어려워지고 데이터 흐름을 추적하는 것에 굉장한 비용이 들게 된다.
그리하여 Flux
가 등장하게 되었다.
Facebook은 MVC 패턴
과 같이 양방향 데이터 흐름을 적용했을 때 나타나는 문제를 해결하기 위해
단방향 데이터 흐름의 디자인 패턴인 Flux
를 고안했다.
양방향 데이터 흐름을 가진 MVC 패턴
과는 반대로,
단방향 데이터 흐름을 가진 Flux
는 데이터의 흐름을 단방향(한 방향)으로 제한함으로써,
애플리케이션의 구조와 상태를 심플하게 파악할 수 있게 만들어졌다.
Flux의 구성 요소
- Store: 애플리케이션의
state(상태)
를 저장하는 object(객체).Dispatcher
로부터state(상태)
갱신을 위한 명령을 받아state(상태)
를 갱신하는 기능도 가지고 있음.- Action: 유저의 움직임로부터 발생된 이벤트(버튼 클릭, input에 데이터 입력 등)와 API로부터의 데이터 수신 등
state(상태)
를 갱신하기 위한 정보를 담은 object(객체)- Dispatcher: 애플리케이션 내의 모든 Action을 받음.
Store
에게Action
내용에 따른state(상태)
갱신(변경)을 명령하는 함수- View: 애플리케이션의 유저 인터페이스(UI)
Flux의 흐름
- 유저가
View
를 조작함으로써Action
이 발생된다.
state
갱신 내용인Action
가Dispatcher
에 전달된다.
Dispatcher
가Store
에Action
을 전달하면서,Store
에게state
갱신을 명령한다.
Store
가state
를 갱신한다.
- 갱신된
state
를View
에게 전달한다.
- 새로운
state
가브라우저(View)
에 렌더링(표시)된다.
Flux
의 가장 큰 특징은 애플리케이션 내의 모든 Action
이 Dispatcher
에 집약시킴으로써,
단방향 데이터 흐름을 이뤄냈다는 점이다.
View
에서 발생한 Action
은 반드시 Dispatcher
를 거치게 되며,
Dispatcher
는 Action
내용을 바탕으로 Store
에게 state
의 갱신을 명령하며,
View
는 변경된 데이터를 Store
를 통해서 전달받는다.
이러한 Flux
의 단방향 데이터 흐름은 기존의 MVC 패턴
에 있던 state
의 전이를 없애주고,
데이터의 흐름을 단순화시켜 예측 가능하게 해줌으로써 state
관리를 용이하게 해주도록 해준다.
Flux
로 기존의 문제들을 충분히 해결했는데 왜 Redux
를 개발한 것일까?
Flux
의 구조에 Reducer
를 결합한 Redux
가 개발됐다.
이 질문에 대해 Redux
의 창시자인 Dan Abramov씨가 직접 등장하여 대답한 글이 있다.
https://stackoverflow.com/questions/32461229/why-use-redux-over-facebook-flux
Redux
는 Flux
의 파생이기 때문에, 공통점과 차이점을 함께 지니고 있다.
(Redux에 대해서는 아래에 후술.)
Flux | Redux | |
차이점 |
Dispatcher가 애플리케이션 내의 모든 Action을 받으며, 각 Action에 따라 Store에게 state 갱신을 명령한다. |
|
공통점 | 단방향 데이터 흐름을 가졌기 때문에, 데이터 구조가 복잡해지지 않는다. |
그 외에 참고가 될만한, Flux
와 Redux
의 차이점에 대한 자료들이다.
(각 그림을 클릭하면 원본 출처로 이동)
서론이 굉장히 길었다. 오늘의 주인공인 Redux
에 대해 알아보기 전에
먼저 Redux
에 등장하는 Redux
의 구성 요소에 대해 알아보자.
↑ 그림 출처: Redux Essentials (Redux 공식문서)
Redux의 구성 요소
- Store: 애플리케이션의
state(상태)
를 저장하는 곳.Store
내에Reducer 함수
가 존재함.- Dispatch:
Reducer 함수
에게Action
을 송신하는 역할.- Action: 유저의 움직임로부터 발생된 이벤트(버튼 클릭, input에 데이터 입력 등)와 API로부터의 데이터 수신 등
state(상태)
를 갱신하기 위한 정보를 담은 object(객체). Dispatch 함수에 의해 Action을 Reducer에게 보낼 수 있다.- Reducer: 함수. Dispatch통해 받은 Action을 토대로, state를 modify, 갱신, 변경할 수 있는 유.일.한 함수.
- View: 애플리케이션의 유저 인터페이스(UI)
Redux
의 규칙, 근간.
Redux
에는 Redux
를 지지하고 있는, 반드시 지켜야만 하는 3원칙이 있다.
아주아주아주아주아주 중요하다.
Redux
를 실제로 사용하다보면 이 원칙의 내용과 반드시 부딪히게 되기 때문에 짚고 넘어가도록 합시당.
Three Principles
- 1. Single source of truth
애플리케이션 내에Store
는 반드시 1개 뿐.Store
는 반드시 1개만 존재한다.- 2. State is read-only
state
를 직접 변경해서는 안.된.다.
state
를 변화시키는 유일한 방법은Action
을Reducer
에 dispatch(송신, 전달)하는 방법 뿐이다.
즉,state
의 변경은Reducer
만 할 수 있다.Reducer
이외의 공간에서는state
는 읽기 전용인 것이다.- 3. Changes are made with pure functions
Reducer
는 순수 함수여야만 한다.
Reducer 함수
는parameter
로기.존.의 state
와Action
을 받는데,Reducer 함수
는기존의 state
를 직접 변경하지 않고, 새.로.운 state object(객체)를 작성해서 return해야한다.
출처: Three Principles (Redux 공식 문서)
이제 본격적으로 Vanilla JavaScript
에서 Redux
를 사용해보자.
먼저, Vanilla JavaScript
로 간단한 Counter 앱을 만들었다.
이 Counter 앱을 Redux
를 사용해서 변경해나가보자 한다.
제일 처음에서 언급했듯이, Redux
는 JavaScript
의 라이브러리이다.
그렇기 때문에 사용하기 전에 설치를 할 필요가 있다.
npm i redux
또는
yarn add redux
를 터미널에 입력하여 Redux
를 설치하자.
Redux
의 심장부에 해당하는 Store
.
Store
는 애플리케이션의 state
를 저장해두는 저장소인데,
Store
를 이용하기 위해서는 Store
를 생성해줄 필요가 있다.
import { createStore } from "redux";
const add = document.querySelector("#add"),
minus = document.querySelector("#minus"),
number = document.querySelector("#number");
// countStore라는 Store에 인자(argument)로 reducer 함수를 넣어줌으로써,
// Store에 reducer 함수를 등록시킨다.
const countStore = createStore(reducer);
createStore()
라는 메소드를 이용하면 Store
를 생성할 수 있다.
createStore()
는 인자(argument)
로 Reducer 함수
를 요구한다.
그러므로 인자(argument)
에 reducer 함수
를 넣어줌으로써, Store
에 reducer 함수
를 등록시킨다.
그리고 ⭐️중요한 정보⭐️.
createStore()
는 4개의 내장 함수를 가지고 있다.
createStore()의 내장 함수
- dispatch():
Reducer 함수
에게Action
을 송신하는 역할.- subscribe():
Store
를 구독하는 것.Store
의State
에 변화가 있을 때를 감지하여 변화가 있을 때 호출할 콜백함수를 지정할 수 있음.- getState(): 해당
Store
의 현재의state
값을 출력함.- replaceReducer(): Reducer를 동적으로 로드하고 싶을 경우 사용. (다른 내장 함수에 비해 깊이가 있기 때문에 따로 조사해보는 것을 권함)
이 내장 함수들은 추후에 적극적으로 사용할 예정이므로 익혀두는게 좋다.
위의 과정을 통해 Store
를 생성했고, Store
에 Reducer 함수
를 등록시켰다.
나는 Reducer
를 '함수'라고 표현했다. Reducer
는 함수이다.
이제는 그 Reducer 함수
를 생성할 차례다.
createStore()
의 인자(argument)
에 들어가는 것이 바로 Reducer 함수
의 이름이다.
위에서 reducer
라는 이름을 넣어주었기 때문에 reducer
라는 이름의 함수를 만들면 그것이 바로 Reducer 함수
가 된다.
import { createStore } from "redux";
const add = document.querySelector("#add"),
minus = document.querySelector("#minus"),
number = document.querySelector("#number");
const reducer = (state, action) => {
return;
}
// countStore라는 Store에 인자(argument)로 reducer 함수를 넣어줌으로써,
// Store에 reducer 함수를 등록시킨다.
const countStore = createStore(reducer);
Reducer 함수
는 기본적으로 두 개의 인자(argument)
를 받는다.
Reducer 함수
의인자(argument)
- 첫 번째 인자 state: current state를 말한다. 현재의 state.
- 두 번째 인자 action:
state(상태)
를 갱신하기 위한 정보를 담은 object(객체)
여기서 처음으로 state
가 등장했는데, 기존 코드에서 state
에 해당하는 부분이 어딜까?
그렇다. 기존 코드 5번째 줄의 count
이다.
count
의 값(데이터)은 플러스 버튼과 마이너스 버튼을 누르는 행위(=Action)를 할 때마다 변경되기 때문이다.
이 때, Reducer 함수
에서 state(count)
를 처음으로 불러오면
state
는 정의되어 있지 않기 때문에, 아래의 그림과 같이 undefined
으로 뜬다.
그러므로 state(count)
가 undefined
이 되지 않도록 state(count)
에 Default 값을 부여해야한다.
state(count)
의 초기값은 0이 되어야 하기 때문에 (페이지에 처음 들어와서 연산을 할 때는 0부터 시작할테니까)
state(count)
의 Initial 값을 0으로 설정하자.
import { createStore } from "redux";
const add = document.querySelector("#add"),
minus = document.querySelector("#minus"),
number = document.querySelector("#number");
// 페이지를 처음 로딩했을 때 number의 초기값으로 0을 출력되도록 설정
number.innerText = 0;
// count(state)의 Initial 값을 0으로 설정
const reducer = (count = 0, action) => {
return;
}
// countStore라는 Store에 인자(argument)로 reducer 함수를 넣어줌으로써,
// Store에 reducer 함수를 등록시킨다.
const countStore = createStore(reducer);
잊지 말자.
Reducer 함수
는 state
를 modify, 갱신, 변경할 수 있는 유.일.한 함수이다.
그리고 Reducer 함수
에서 아주아주 중요한 ⭐️포인트⭐️.
Reducer 함수
에서 return되는 값은 새로운 state
가 된다.
위의 그림을 보면, Reducer 함수
에서 1를 return하고 있다.
제일 아래의 구문에서는 앞서 설명한 createStore()의 내장 함수인 getState()
를 이용하여
현재 state
의 값을 조회하니, 그 결과는 1이 나왔다.
이것은 Reducer 함수
를 사용하는 데에 있어서 아주아주아주아주아주 중요한 개념이다.
우리의 목적은 플러스 버튼과 마이너스 버튼을 누를 때마다
더하기 1, 빼기 1을 수행하도록 하여 그 값인 state(count)
를 페이지에 표시해야한다.
여기서 '플러스 버튼과 마이너스 버튼을 누르는 동작'이 바로 Action
이라고 말할 수 있다.
버튼을 누름으로써, state(count)
을 갱신, 변경하기 때문이다.
이제 Reducer 함수
의 두 번째 인자인 action
을 생성할 차례이다.
앞서, Redux의 구성 요소에서
Action
은state(상태)
를 갱신하기 위한 정보를 담은 object(객체)
라고 했다.
action
가 무엇을 담고 있는지, object(객체)가 맞는지 구조를 살펴보자.
위의 그림처럼, action
은 object(객체)
이며, 기본적으로 type
라고 하는 key
를 가지고 있다.
보통, action
의 type
에 동작의 이름을 담아서 각각의 Action
을 판별한다.
이게 무슨 말이냐하면, 이번 Counter 앱의 예시에서
플러스 버튼을 눌렀을 때는 더하기라는 행위를 해야함을 인지하여 더하기 1를 해줘야하고
마이너스 버튼을 눌렀을 때는 빼기라는 행위를 해야함을 인지하여 빼기 1을 해줘야한다.
그래서 플러스 버튼이 클릭되었을 때는 action
의 type
에 더하기를 나타내는 ADD
라는 플래그를 보내어 더하기라는 행위를 해야함을 판별하고,
마이너스 버튼이 클릭되었을 때는 action
의 type
에 빼기를 나타내는 MINUS
라는 플래그를 보내어 더하기라는 행위를 해야함을 판별시키는 것이다.
자 그럼, 플러스 버튼이 클릭되었을 때 플러스를 해야한다는 action
을 어떻게 Reducer 함수
에서 알아챌까?
Redux의 구성 요소와 createStore()의 내장 함수에서 설명했던 dispatch
가 여기서 등장한다.
dispatch
는Reducer 함수
에게Action
을 송신하는 역할을 한다.
dispatch
가 Reducer 함수
에게 Action
을 보내줌으로써
Reducer 함수
는 Action
의 값을 받고, Action
이 발생했다는 것을 감지하게 된다.
dispatch
의 기본 구문은 아래와 같다.
store.dispatch(action)
코드와 결과 화면을 보는 것이 이해하기 더 빠르기 때문에,
바로 코드와 결과 화면을 보자.
import { createStore } from "redux";
const add = document.querySelector("#add"),
minus = document.querySelector("#minus"),
number = document.querySelector("#number");
// 페이지를 처음 로딩했을 때 number의 초기값으로 0을 출력되도록 설정
number.innerText = 0;
const ADD = "ADD";
const MINUS = "MINUS";
// count(state)의 Initial 값을 0으로 설정
const reducer = (count = 0, action) => {
console.log(action);
return;
};
const handleAdd = () => {
// add 버튼을 클릭했을때 { type: ADD }라는 action을 reducer에게 송신
countStore.dispatch({ type: ADD });
};
const handleMinus = () => {
// minus 버튼을 클릭했을때 { type: MINUS }라는 action을 reducer에게 송신
countStore.dispatch({ type: MINUS });
};
// countStore라는 Store에 인자(argument)로 reducer 함수를 넣어줌으로써,
// Store에 reducer 함수를 등록시킨다.
const countStore = createStore(reducer);
add.addEventListener("click", handleAdd);
minus.addEventListener("click", handleMinus);
위의 코드와 결과 화면을 보면,
add
버튼과 minus
을 클릭했을 때 각각 handleAdd
와 handleMinus
라는 함수가 작동되도록 했다.
handleAdd
함수에서는 dispatch
를 이용하여,
add
버튼을 클릭했을때 { type: ADD }
라는 action
을 reducer
에게 송신되도록 했으며,
handleMinus
함수에서는 dispatch
를 이용하여
minus
버튼을 클릭했을때 { type: MINUS }
라는 action
을 reducer
에게 송신되도록 했다.
여기서의 포인트는
countStore.dispatch({ type: ADD });
countStore.dispatch({ type: MINUS });
인데, countStore
라는 store
의 내장 함수인 dispatch
를 이용한 것이다.
dispatch
는 인자로 action
을 요구하기 때문에,
{ type: hoge }
라는 object(객체) 형태의 action
을 담아서
Reducer 함수
에게 { type: hoge }
라는 action
을 보낸 것이다.
4️⃣ Action 생성하고, Dispatch를 이용하여 Reducer에게 Action을 송신하기의 과정을 통해,
View
에서 발생한 action
을 dispatch
를 통해 Reducer 함수
에 보내는 것에 성공했다.
Reducer 함수
는 수령한 Action
의 정보에 따라 state
를 갱신하는 임무를 가지고 있다.
현재 Reducer 함수
는 플러스 버튼이 클릭되었을 때는 { type: ADD }
라는 Action
을 받았고,
마이너스 버튼이 클릭되었을 때는 { type: MINUS }
라는 Action
을 받았다.
그러므로 더하기와 빼기의 행위를 판별하고 그에 맞는 처리를 하기 위해서는
간단하다. reducer 함수
내에서 조건에 대해 각각의 처리를 행하도록 하는 switch 문을 이용하면 된다.
import { createStore } from "redux";
const add = document.querySelector("#add"),
minus = document.querySelector("#minus"),
number = document.querySelector("#number");
// 페이지를 처음 로딩했을 때 number의 초기값으로 0을 출력되도록 설정
number.innerText = 0;
const ADD = "ADD";
const MINUS = "MINUS";
// count(state)의 Initial 값을 0으로 설정
const reducer = (count = 0, action) => {
console.log(count);
switch (action.type) {
case ADD:
// action.type이 ADD일 때
return count + 1;
case MINUS:
// action.type이 MINUS일 때
return count - 1;
default:
return count;
}
};
const handleAdd = () => {
// add 버튼을 클릭했을때 { type: ADD }라는 action을 reducer에게 송신
countStore.dispatch({ type: ADD });
};
const handleMinus = () => {
// minus 버튼을 클릭했을때 { type: MINUS }라는 action을 reducer에게 송신
countStore.dispatch({ type: MINUS });
};
// countStore라는 Store에 인자(argument)로 reducer 함수를 넣어줌으로써,
// Store에 reducer 함수를 등록시킨다.
const countStore = createStore(reducer);
add.addEventListener("click", handleAdd);
minus.addEventListener("click", handleMinus);
action
의 type
에 따라 state(count)
를 변경하는 것에 성공했다.
이제 페이지(View)
에 변경된 state(count)
의 최신 값을 렌더링을 할 차례다.
그보다 먼저, 각 버튼이 클릭될 때마다 실시간으로 number(View 내의 출력 값)
이 갱신되도록 하기 위해서는
state(count)
가 변경되는 것을 감지해야만, 알아채야만 한다.
앞서 createStore()의 내장 함수에서 소개했던 subscribe()
가 여기서 사용된다.
subscribe()
는Store
를 구독(subscribe)함으로써,Store
의 첫번째 인자인State
에 변화가 있을 때를 감지하며,State
에 변화가 있을 때subscribe()
에 지정된 콜백함수를 실행한다.
바로 코드와 subscribe()의 이용 화면을 보자.
import { createStore } from "redux";
const add = document.querySelector("#add"),
minus = document.querySelector("#minus"),
number = document.querySelector("#number");
// 페이지를 처음 로딩했을 때 number의 초기값으로 0을 출력되도록 설정
number.innerText = 0;
const ADD = "ADD";
const MINUS = "MINUS";
// count(state)의 Initial 값을 0으로 설정
const reducer = (count = 0, action) => {
console.log(count);
switch (action.type) {
case ADD:
// action.type이 ADD일 때
return count + 1;
case MINUS:
// action.type이 MINUS일 때
return count - 1;
default:
return count;
}
};
const handleAdd = () => {
// add 버튼을 클릭했을때 { type: ADD }라는 action을 reducer에게 송신
countStore.dispatch({ type: ADD });
};
const handleMinus = () => {
// minus 버튼을 클릭했을때 { type: MINUS }라는 action을 reducer에게 송신
countStore.dispatch({ type: MINUS });
};
// countStore라는 Store에 인자(argument)로 reducer 함수를 넣어줌으로써,
// Store에 reducer 함수를 등록시킨다.
const countStore = createStore(reducer);
const printCount = () => {
console.log("state updated");
};
// state(count)가 변경되는 것을 감지하여,
// state(count)가 변경되면 printCount라는 함수를 실행시킴
countStore.subscribe(printCount);
add.addEventListener("click", handleAdd);
minus.addEventListener("click", handleMinus);
위의 그림을 보면, 각 버튼을 클릭하여 state(count)
의 값이 변화되면
subscribe()
가 state(count)
의 변화를 감지하여, printCount
라는 함수를 실행시키고 있다.
그럼 이제는, 각 버튼을 클릭하여 action
의 정보에 따라 갱신된
현재(최신)의 state(count)
값을 View에 렌더링시키는 일만 남았다.
현재(최신)의 state(count)
값을 불러오는 방법은 무엇일까?
이것도 앞서 createStore()의 내장 함수에서 소개한 getState()
가 여기서 사용된다.
getState()
는 해당Store
의 현재의state
값을 출력한다.
getState()
의 기본 구문은 아래와 같다.
store.getState()
이 getState()
를 사용하여 최종적으로 더하기, 빼기한 결과값을 출력시켜보자.
import { createStore } from "redux";
const add = document.querySelector("#add"),
minus = document.querySelector("#minus"),
number = document.querySelector("#number");
// 페이지를 처음 로딩했을 때 number의 초기값으로 0을 출력되도록 설정
number.innerText = 0;
const ADD = "ADD";
const MINUS = "MINUS";
// count(state)의 Initial 값을 0으로 설정
const reducer = (count = 0, action) => {
console.log(count);
switch (action.type) {
case ADD:
// action.type이 ADD일 때
return count + 1;
case MINUS:
// action.type이 MINUS일 때
return count - 1;
default:
return count;
}
};
const handleAdd = () => {
// add 버튼을 클릭했을때 { type: ADD }라는 action을 reducer에게 송신
countStore.dispatch({ type: ADD });
};
const handleMinus = () => {
// minus 버튼을 클릭했을때 { type: MINUS }라는 action을 reducer에게 송신
countStore.dispatch({ type: MINUS });
};
// countStore라는 Store에 인자(argument)로 reducer 함수를 넣어줌으로써,
// Store에 reducer 함수를 등록시킨다.
const countStore = createStore(reducer);
const printCount = () => {
// state(count)의 현재 값을 출력하는 내장 함수인 getState()를 사용하여
// number의 innerText를 state(count)의 현재(최신) 값이 출력되도록 함
number.innerText = countStore.getState();
};
// state(count)가 변경되는 것을 감지하여,
// state(count)가 변경되면 printCount라는 함수를 실행시킴
countStore.subscribe(printCount);
add.addEventListener("click", handleAdd);
minus.addEventListener("click", handleMinus);
countStore.getState()
를 이용하여 state(count)
의 현재(최신) 값을 불러 왔고
그 불러온 값을 number.innerText
에 담음으로써
state(count)
의 현재(최신) 값을 View
에 표시시켰다.
최종 결과 화면과 코드를 아래의 Demo에서 확인해보자.
추가로 Redux와 Vanilla JavaScript로 To-do List를 만들어보았다.
(Local Storage 기능은 없음)
Counter 앱에서 사용해보지 못했던 Action creator와,
⭐️Redux의 3원칙⭐️의 2번 항목을 경험해볼 수 있다.
위 Redux의 기초 구문에 대해 이해를 하고 있다면
해석하기 어렵지 않을 것이라고 생각한다.
각 코드에 대한 설명은 코드의 주석에 달아놓았기 때문에 한 번 살펴보면 도움이 될 듯 하다.
Counter 앱 예제를 통해, Vanilla JavaScript
에서 Redux
를 사용해보며
Redux
의 기초 구문을 사용해보았다.
추후 React Redux
를 사용하기 전에 앞서서
Vanilla JavaScript
에서 Redux
를 사용해보는 것이 React Redux
를 사용하고 이해하는 것에 큰 도움이 된다고 생각한다.
정리가 굉장히 난해하고 장황하게 된 것 같다😭
지식을 공유하기 위해서는 내가 이해하고 있는 개념들을 정리해서 그것을 조리있게 표현하는 능력이 필요하다고 새삼 느꼈다.
혹시 부족하거나 틀린 정보가 있다면 마구마구 지적해주시면 감사하겠습니다🙇♀️