Redux + JavaScript :: Redux 입문 (Store, Reducer, Action, Dispatch, Subscription)

iamhayoung·2021년 5월 11일
2

Redux

목록 보기
1/3

Redux와 React의 관계🧐

Redux상태 관리(state management)로 유명한 라이브러리이다.

Redux라는 기술에 대해 처음 인지했을 당시,
Redux는 주로 React에서 사용되기 때문에 React 전용의 프레임 워크라고 생각했다.
(그리고 Redux, React의 명칭이 둘 다 'Re'로 시작해서, ReduxReact에 속해있는 기술(?)인 줄만 알았다. 바보😶‍🌫️)

하지만 사실 ReduxJavaScript의 많고 많은 라이브러리 중 하나였으며, React와는 독립적인 존재라는 것을 알게 됐다.

for JS Apps.

Redux 공식 홈페이지의 대문에도 적혀 있듯이,
ReduxJavaScript로 만들어진 애플리케이션들의 state(상태)를 관리하기 위한 라이브러리이다.

Redux는 절대 React에 종속된 라이브러리가 아니다.
흔히 React에서 사용하는 ReduxReact Redux라는 기술이다.
React ReduxRedux 개발팀에서 공식적(Official)으로 개발한 React 전용 Redux이다.

이번 글에서는 React Redux를 사용해보기 전에, Vanilla JavaScript에서 Redux를 간단히 사용해보며,
Redux의 동작원리에 대해 공부해보고자 한다.


근데, 왜 Redux를 사용하는거지?

(부제: Redux는 어떻게, 왜 개발된 것일까?)

Flux와 Redux

Redux를 왜 사용하는 것인지, 어떻게 개발된 것인지 이해하기 위해서는 Redux의 탄생비화에 대해서 짚고 넘어갈 필요가 있다.

Redux는, Redux의 창시자인 Dan Abramov씨가 Flux라고 하는 아키텍쳐에 Reducer라는 것을 결합하여 만들었다고 한다.

Redux = Reducer + Flux

그럼 Flux가 뭐지?

Flux의 전신(?), MVC(Model-View-Controller) 패턴

Flux에 대해 알아보기 전에, Flux가 개발되게끔 원인을 제공(?)한 MVC 패턴에 대해 먼저 알아보자.
FluxRedux가 등장하기 전에 통용되던 프론트엔드 개발 디자인 패턴은 MVC(Model-View-Controller)라는 패턴이었다.

MVC 패턴양방향 데이터 흐름을 가진 개발 디자인 패턴으로,
애플리케이션을 3가지의 부분으로 나누어, 각각의 부분이 독자적인 처리를 행하도록 하는 디자인 패턴을 말한다.
그 각각의 부분은 다음과 같다.

MVC (Model-View-Controller)

  • Model: 데이터를 관리하고 변환, 처리하는 역할.
  • View: Model의 상태를 참조하여 사용자에게 눈에 보이는 형태로 표시함. 렌더링하는 역할. 인터페이스의 역할을 하기 때문에 때때로 사용자로부터 데이터를 입력(Action)받기도 함.
  • Controller: 유저로부터 입력(Action)을 받아, Model과 View에게 데이터를 변환, 업데이트하도록 지시하는 역할.

MVC 패턴양방향 데이터 흐름을 가졌다.

Model이 변경되면 View가 변경되며,
유저에 의해 View에서 의도치 않게 Action이 일어난다면
데이터를 관리하고 변환, 처리하는 역할을 가진 ModelView로부터의 데이터 또한 처리해야한다. (바쁘다 바빠)

중요한 문제는, MVC 패턴에서 애플리케이션의 규모가 커지면 커질수록 애플리케이션의 구조가 아주 복잡해진다는 것이다.

위의 그림과 같이 애플리케이션의 규모가 커질수록
한 개의 Model이 여러 개의 View를 조작하거나, 한 개의 View가 여러 개의 Model을 조작하게 되는 등
버그를 감지하기 어려워지고 데이터 흐름을 추적하는 것에 굉장한 비용이 들게 된다.

Flux의 등장!👊

그리하여 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의 흐름

    1. 유저가 View를 조작함으로써 Action이 발생된다.
    1. state 갱신 내용인 ActionDispatcher에 전달된다.
    1. DispatcherStoreAction을 전달하면서, Store에게 state 갱신을 명령한다.
    1. Storestate를 갱신한다.
    1. 갱신된 stateView에게 전달한다.
    1. 새로운 state브라우저(View)에 렌더링(표시)된다.

Flux의 가장 큰 특징은 애플리케이션 내의 모든 ActionDispatcher에 집약시킴으로써,
단방향 데이터 흐름을 이뤄냈다는 점이다.
View에서 발생한 Action은 반드시 Dispatcher를 거치게 되며,
DispatcherAction 내용을 바탕으로 Store에게 state의 갱신을 명령하며,
View는 변경된 데이터를 Store를 통해서 전달받는다.

이러한 Flux단방향 데이터 흐름은 기존의 MVC 패턴에 있던 state의 전이를 없애주고,
데이터의 흐름을 단순화시켜 예측 가능하게 해줌으로써 state 관리를 용이하게 해주도록 해준다.

그래. Flux는 알겠고, Redux는 왜 만든거야? Flux로 충분하지 않아?

Flux로 기존의 문제들을 충분히 해결했는데 왜 Redux를 개발한 것일까?

Flux의 구조에 Reducer를 결합한 Redux가 개발됐다.
이 질문에 대해 Redux의 창시자인 Dan Abramov씨가 직접 등장하여 대답한 글이 있다.
https://stackoverflow.com/questions/32461229/why-use-redux-over-facebook-flux

Flux와 Redux의 차이점

ReduxFlux의 파생이기 때문에, 공통점과 차이점을 함께 지니고 있다.
(Redux에 대해서는 아래에 후술.)

Flux Redux
차이점 Dispatcher가 애플리케이션 내의 모든 Action을 받으며,
각 Action에 따라 Store에게 state 갱신을 명령한다.
  • Reducer 함수가 Store에 보내져 온 Action과 기존의 state를 토대로
    새로운 state를 생성하여 return한다.
  • Store는 반드시 1개만 존재한다.
  • state의 변경, 갱신은 Action을 Store에게 dispatch(송신)하는 방법 뿐.
공통점 단방향 데이터 흐름을 가졌기 때문에, 데이터 구조가 복잡해지지 않는다.

그 외에 참고가 될만한, FluxRedux의 차이점에 대한 자료들이다.
(각 그림을 클릭하면 원본 출처로 이동)


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의 3원칙⭐️

Redux의 규칙, 근간.
Redux에는 Redux를 지지하고 있는, 반드시 지켜야만 하는 3원칙이 있다.
아주아주아주아주아주 중요하다.
Redux를 실제로 사용하다보면 이 원칙의 내용과 반드시 부딪히게 되기 때문에 짚고 넘어가도록 합시당.

Three Principles

  • 1. Single source of truth
    애플리케이션 내에 Store는 반드시 1개 뿐. Store는 반드시 1개만 존재한다.
  • 2. State is read-only
    state를 직접 변경해서는 안.된.다.
    state를 변화시키는 유일한 방법은 ActionReducer에 dispatch(송신, 전달)하는 방법 뿐이다.
    즉, state의 변경은 Reducer만 할 수 있다. Reducer 이외의 공간에서는 state는 읽기 전용인 것이다.
  • 3. Changes are made with pure functions
    Reducer순수 함수여야만 한다.
    Reducer 함수parameter기.존.의 stateAction을 받는데, Reducer 함수기존의 state를 직접 변경하지 않고, 새.로.운 state object(객체)를 작성해서 return해야한다.

출처: Three Principles (Redux 공식 문서)


Redux 기초 구문📝

Redux 도입 전 기존 코드

이제 본격적으로 Vanilla JavaScript에서 Redux를 사용해보자.
먼저, Vanilla JavaScript로 간단한 Counter 앱을 만들었다.

이 Counter 앱을 Redux를 사용해서 변경해나가보자 한다.

1️⃣ Redux 설치하기

제일 처음에서 언급했듯이, ReduxJavaScript의 라이브러리이다.
그렇기 때문에 사용하기 전에 설치를 할 필요가 있다.

npm i redux

또는

yarn add redux

를 터미널에 입력하여 Redux를 설치하자.

2️⃣ Store 생성하기

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 함수를 넣어줌으로써, Storereducer 함수를 등록시킨다.

그리고 ⭐️중요한 정보⭐️.
createStore()는 4개의 내장 함수를 가지고 있다.

createStore()의 내장 함수

  • dispatch(): Reducer 함수에게 Action을 송신하는 역할.
  • subscribe(): Store를 구독하는 것. StoreState에 변화가 있을 때를 감지하여 변화가 있을 때 호출할 콜백함수를 지정할 수 있음.
  • getState(): 해당 Store의 현재의 state 값을 출력함.
  • replaceReducer(): Reducer를 동적으로 로드하고 싶을 경우 사용. (다른 내장 함수에 비해 깊이가 있기 때문에 따로 조사해보는 것을 권함)

이 내장 함수들은 추후에 적극적으로 사용할 예정이므로 익혀두는게 좋다.

3️⃣ Reducer 생성하기

위의 과정을 통해 Store를 생성했고, StoreReducer 함수를 등록시켰다.
나는 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 함수를 사용하는 데에 있어서 아주아주아주아주아주 중요한 개념이다.

4️⃣ Action 생성하고, Dispatch를 이용하여 Reducer에게 Action을 송신하기

우리의 목적은 플러스 버튼과 마이너스 버튼을 누를 때마다
더하기 1, 빼기 1을 수행하도록 하여 그 값인 state(count)를 페이지에 표시해야한다.

여기서 '플러스 버튼과 마이너스 버튼을 누르는 동작'이 바로 Action이라고 말할 수 있다.
버튼을 누름으로써, state(count)을 갱신, 변경하기 때문이다.

이제 Reducer 함수의 두 번째 인자인 action을 생성할 차례이다.
앞서, Redux의 구성 요소에서

Actionstate(상태)를 갱신하기 위한 정보를 담은 object(객체)

라고 했다.

action가 무엇을 담고 있는지, object(객체)가 맞는지 구조를 살펴보자.

위의 그림처럼, actionobject(객체)이며, 기본적으로 type라고 하는 key를 가지고 있다.
보통, actiontype에 동작의 이름을 담아서 각각의 Action을 판별한다.

이게 무슨 말이냐하면, 이번 Counter 앱의 예시에서
플러스 버튼을 눌렀을 때는 더하기라는 행위를 해야함을 인지하여 더하기 1를 해줘야하고
마이너스 버튼을 눌렀을 때는 빼기라는 행위를 해야함을 인지하여 빼기 1을 해줘야한다.

그래서 플러스 버튼이 클릭되었을 때는 actiontype에 더하기를 나타내는 ADD라는 플래그를 보내어 더하기라는 행위를 해야함을 판별하고,
마이너스 버튼이 클릭되었을 때는 actiontype에 빼기를 나타내는 MINUS라는 플래그를 보내어 더하기라는 행위를 해야함을 판별시키는 것이다.

자 그럼, 플러스 버튼이 클릭되었을 때 플러스를 해야한다는 action을 어떻게 Reducer 함수에서 알아챌까?
Redux의 구성 요소createStore()의 내장 함수에서 설명했던 dispatch가 여기서 등장한다.

dispatchReducer 함수에게 Action을 송신하는 역할을 한다.

dispatchReducer 함수에게 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을 클릭했을 때 각각 handleAddhandleMinus라는 함수가 작동되도록 했다.

handleAdd 함수에서는 dispatch를 이용하여,
add 버튼을 클릭했을때 { type: ADD }라는 actionreducer에게 송신되도록 했으며,

handleMinus 함수에서는 dispatch를 이용하여
minus 버튼을 클릭했을때 { type: MINUS }라는 actionreducer에게 송신되도록 했다.

여기서의 포인트는

countStore.dispatch({ type: ADD });
countStore.dispatch({ type: MINUS });

인데, countStore라는 store의 내장 함수인 dispatch를 이용한 것이다.
dispatch는 인자로 action을 요구하기 때문에,
{ type: hoge }라는 object(객체) 형태의 action을 담아서
Reducer 함수에게 { type: hoge }라는 action을 보낸 것이다.

5️⃣ Reducer 함수에서 Action의 type 값에 따라 state를 갱신시키기

4️⃣ Action 생성하고, Dispatch를 이용하여 Reducer에게 Action을 송신하기의 과정을 통해,
View에서 발생한 actiondispatch를 통해 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);

6️⃣ state의 변화를 구독(subscribe)하기

actiontype에 따라 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라는 함수를 실행시키고 있다.

7️⃣ getState()를 이용하여 현재 state 값을 불러와서 View에 현재 state 값을 출력하기

그럼 이제는, 각 버튼을 클릭하여 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 + JavaScript로 Counter 만들기 Demo🔍


응용 과제: Redux + JavaScript로 To-do List 만들기 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를 사용하고 이해하는 것에 큰 도움이 된다고 생각한다.

정리가 굉장히 난해하고 장황하게 된 것 같다😭
지식을 공유하기 위해서는 내가 이해하고 있는 개념들을 정리해서 그것을 조리있게 표현하는 능력이 필요하다고 새삼 느꼈다.

혹시 부족하거나 틀린 정보가 있다면 마구마구 지적해주시면 감사하겠습니다🙇‍♀️

profile
Frontend Developer. 블로그 이사했어요 🚚 → https://iamhayoung.dev

0개의 댓글