본문 바로가기

상태관리 라이브러리/Redux

[Redux] 사용법부터 이론까지 살펴보는 Redux: 2 바닐라 자바스크리트에서의 사용 방법

이 글에서는 바닐라 자바스크립트에서 리덕스를 사용하는 기본 방법에 대해 소개한다. 리덕스는 react나 vue, angular 등의 라이브러리 혹은 프레임워크와 결합해서 사용하는 게 일반적이지만, 일단은 리덕스 자체에 집중하기 위해 일반 자바스크립트를 사용했다.

 

프로젝트 세팅


우선 바벨이나 모듈 등의 설정을 위해서 Create-react-app(CRA)을 사용했다. CRA는 react 앱을 만들기 위해 여러 세팅을 간편하게 해주는 아주 유용한 앱이다. (홈페이지-  https://create-react-app.dev/docs/getting-started )

프로젝트를 생성하자.

npx create-react-app my-app
cd my-app
npm start

 

 

그리고 바닐라 자바스크립트 사용을 위해 src/ 내에 index.js를 제외한 모든 파일을 삭제하고 index.js 파일은 비웠다.

이제리덕스를 이용해서 간단한 카운터 기능 만들어보자.

 

 

스토어 세팅


우선 데이터를 저장할 스토어를 생성하자.

import { createStore } from "redux";

const reduxReducer = function (state = 0, action) {
  switch (action.type) {
    case "INCREASEMENT":
      return state + action.payload;
    case "DECREASEMENT":
      return state - action.payload;
    default:
      return state;
  }
};

let reduxStore = createStore(reduxReducer);
  • createStore | 스토어를 생성하는 함수. 인자로 reducer을 받음
  • reducer | 전송 받은 데이터 변경 요청(action)에 따라 상태값을 변경하는 함수
    • 인자 state | 최초의 상태 값
    • 인자 action | view로부터 전송받는 데이터 변경 요청을 담은 객체
    • 리듀서의 규칙
      • 기존 상태값은 불변성을 유지해야 한다
      • 반환되는 값이 새로운 상태값이 된다.
      • 리듀서 내에서는 비동기 작업이나 다른 사이드 이펙트를 만들어선 안된다. 즉, 순수 함수이어야 한다.
        • 외부 상태에 의존하는 작업(ex: math.random(), Date.now() ..)은 액션 생성자에서 작업 후 전달하자.
  • 컨셉 | 단일 스토어, 단일 리듀서를 지향하는 리덕스는 하나의 스토어와 리듀서만을 두는 것이 일반적이다. 단, 앱이 방대해짐에 따라 리듀서가 방대해지면 메인 리듀서를 하위 리듀서로 분할하는 방식을 취한다.
  • 팁 | 리듀서에서 액션을 분기처리하는 로직은 swith 문을 사용하면 편하다.

 

구독 설정


자바스크립트를 기반으로 하는 리덕스는 상태값 변경 시 자동으로 view를 업데이트하는 자동 바인딩이 없다. 리엑트는 이를 자동으로 지원하지만, 그렇지 않을 때는 이를 세팅해줘야 한다. 아까의 코드에 추가해보자.

import { createStore } from "redux";

//@스토어에 사용할 reducer
const reduxReducer = function (state = 0, action) {
  switch (action.type) {
    case "INCREASEMENT":
      return state + action.payload;
    case "DECREASEMENT":
      return state - action.payload;
    default:
      return state;
  }
};

//@스토어
let reduxStore = createStore(reduxReducer)


//@ 바인딩 함수
const bindingUi = function (dom, state) {
  if (!dom instanceof HTMLElement) return;
  dom.innerText = state;
};

//@ 스토어를 구독함. 데이터 변경 시 콜백 함수를 호출
  reduxStore.subscribe(() =>
    bindingUi(임의의 컴포넌트, reduxStore.getState())
  );

 

  • store.subscribe(callback) | 스토어를 구독하여 데이터 변경을 감지한다. 감지 시, 등록된 콜백 함수를 실행한다.
  • store. getState() | 스토어의 현재 값을 불러온다.
  • 주의사항 | 리듀서 함수의 반환 값이 동일하거나 반환이 없어도 실행됨

 

상태 값 변경을 요청하기


스토어 만들기, 리듀서 설정, 구독 설정 이후에는 데이터 값을 변경해달라고 요청할 차례다. 이 때는 store의 dispather 메소드를 사용한다. 이 메소드는 인자로 할당된 객체를 스토어의 리듀서로 전달한다. 이 때, 이 객체에는 무엇을 변경할지(종류), 얼마나 변경할지(값)를 담는게 일반적이다. 다시 카운터 예시를 보자.

 

import { createStore } from "redux";

//스토어에 사용할 reducer
const reduxReducer = function (state = 0, action) {
  switch (action.type) {
    case "INCREASEMENT":
      return state + action.payload;
    case "DECREASEMENT":
      return state - action.payload;
    default:
      return state;
  }
};

//스토어
let reduxStore = createStore(reduxReducer)


// 바인딩 함수
const bindingUi = function (dom, state) {
  if (!dom instanceof HTMLElement) return;
  dom.innerText = state;
};

//스토어를 구독함. 데이터 변경 시 콜백 함수를 호출
  reduxStore.subscribe(() =>
    bindingUi(임의의 컴포넌트, reduxStore.getState())
  );
 
//html에서 가져온 버튼
  const addButton = document.querySelector("button.add")
  const minusButton = document.querySelector("button.minus")
  
 //버튼 클릭 시, dispatch로 action 전송
 addButton.addEventListener("click,()=>reduxStore.dispatch({type:"INCREASEMENT",payload:2}))
 addButton.addEventListener("click,()=>reduxStore.dispatch({type:"DECREASEMENT",payload:2}))

 

  • store.dispath(action) | 스토어의 리듀서로 데이터 변경을 요청함
  • action | 데이터 변경 요청을 담은 객체, 일반적으로 type과 payload로 요청을 명시한다.
  • 팁 | action.type은 오타를 방지하기 위해 상수타입의 변수를 만들어 관리하면 편하다.
  • 팁 | action은 직접 객체를 생성하기 보단, 액션을 생성하는 함수를 별도로 만들어주는 게 일반적이다. 이를 액션생성자라 부른다.

액션 생성자의 예시

function actionAddCounter(payload) {
	return {
    		type:"INCREASE",
            payload:payload
}

 

 

정리


  • 리덕스의 세팅 | 스토어 생성, 리듀서 설정, 구독
    • 스토어와 리듀서는 단일로 설정하는 것이 리덕스의 컨셉에 맞음
    • 앱이 커지면 리듀서는 하위 리듀서로 분할하여 관리
    • 리듀서의 처리 로직은 switch 문이 일반적
    • 리듀서의 규칙
      • 기존 상태값은 불변성을 유지해야 한다. (오류 방지)
      • 반환되는 값이 새로운 상태값이 된다.
      • 리듀서 내에서는 비동기 작업이나 다른 사이드 이펙트를 만들어선 안된다
  • 상태 값 읽기 | store.getState() 
  • 상태값 변경 요청 | store.dispatch(action)
    • action은 객체로 type, payload 명시가 일반적
    • type은 상수타입 문자열을 따로 선언해주면 오타 방지
  • 데이터 변경 시 콜백 함수 실행 | store.subsribe(callback)
    • 리듀서 함수의 반환 값이 동일하거나 반환이 없어도 실행됨

 

 

 

 

참고 자료

https://ko.redux.js.org/

 

Redux - 자바스크립트 앱을 위한 예측 가능한 상태 컨테이너. | Redux

자바스크립트 앱을 위한 예측 가능한 상태 컨테이너.

ko.redux.js.org