바닐라 자바스크립트 리덕스 Pure Redux

김아현·2022년 11월 4일
0

JavaScript

목록 보기
1/2
post-thumbnail

Redux 설치

리덕스 코어와 리덕스 툴킷이 존재합니다. 둘의 차이점은 Redux Toolkit은 Redux 코어 뿐만 아니라 Redux Thunk나 Reselect 등의 패키지를 포함한다는 점입니다. 또 리덕스는 의존성 패키지를 포함해 2Kb의 매우 작은 용량입니다.

# Redux Core 설치하기

# NPM
npm install redux

# Yarn
yarn add redux

기본 사용 예제

import { createStore } from 'redux'

/**
 * `function Func (state, action) => state` 형태는 순수한 함수인 리듀서입니다.
 * 리듀서는 action이 어떻게 state를 다음 state로 변경하는지 서술합니다.
 *
 * state의 type은 기본형(primitive)일수도, 배열일수도, 객체일수도 있습니다.
 * 심지어 Immutable.js 자료구조일수도 있습니다.  
 * state를 다룰 때 가장 중요한 점은 직접 mutate(변형)해서는 안된다는 점이며, 
 * state가 바뀐다면 변형이 아닌새로운 객체를 반환해야 한다는 것입니다.
 *
 * 이 예제에서 우리는 `switch` 구문과 문자열을 썼지만,
 * 여러분의 프로젝트에 맞게 `map`과 같은 다른 컨벤션을 따르셔도 좋습니다.
 * 또, case의 명칭을 const 상수로 지정하여 human error를 방지하고 최적화할 수 있습니다. 
 */
function counter(state = 0, action) {
  switch (action.type) {
    case 'INCREMENT':
      return state + 1
    case 'DECREMENT':
      return state - 1
    default:
      return state
  }
}

// 앱의 state를 보관하는 Redux store를 만듭니다.
// createStore의 API로는 { subscribe, dispatch, getState }가 있습니다.
let store = createStore(counter)

// subscribe()를 이용해 상태 변화에 따라 UI가 변경되게 할 수 있습니다.
// 보통은 subscribe()를 직접 사용하기보다는 뷰 바인딩 라이브러리(예를 들어 React Redux)를 사용합니다.
// 하지만 이는 현재 상태를 localStorage에 영속적으로 저장할 때에 편리합니다.

store.subscribe(() => console.log(store.getState())))

// 내부 상태를 변경하는 유일한 방법은 액션을 보내는 것뿐입니다.
// 액션은 직렬화할수도, 로깅할수도, 저장할수도 있으며 나중에 재실행할수도 있습니다.
store.dispatch({ type: 'INCREMENT' })
// 1
store.dispatch({ type: 'INCREMENT' })
// 2
store.dispatch({ type: 'DECREMENT' })
// 1

리덕스 사용 시 Action type의 상수화

규모가 작은 프로젝트에서는 액션 타입을 따로 상수로 선언할 필요는 없습니다. 하지만 여러 사람들과 협업이 이루어지는 큰 프로젝트에서는 액션 타입을 상수로 정의하는 이점들이 있습니다.

  1. 모든 액션 타입이 코드 상단 한 공간에 모이기 때문에 이름짓기의 일관성을 유지하는데 도움이 됩니다.
  2. 새 기능을 만들기 전에 기존의 모든 액션을 한눈에 볼 수 있습니다.
  3. 상단에 정리된 추가, 제거, 변경된 액션 타입의 목록은 풀 리퀘스트에서 팀원 모두가 새 기능의 범위와 구현을 따라가는걸 도와줍니다.
  4. 만일 상수를 사용하지 않고 액션 타입을 string으로 작성하다가 오타를 내면 아무일도 일어나지 않고 에러를 알아차리기 어렵습니다.
  5. 만약 여러분이 액션 상수를 불러오다가 오타를 내면 undefined가 나올겁니다. 액션을 스트링으로 쓰다 에러를 낸 것보다 그 원인을 쉽게 알아차릴 수 있습니다.

Redux의 3가지 사용 원칙

1. Redux State는 하나의 객체 트리로 이루어져있다.

앱의 모든 state는 하나의 store 안에 하나의 객체 트리 구조로 저장됩니다.
리덕스는 이를 통해서 다양한 환경에서 실행 가능한 범용적인 앱을 쉽게 만들 수 있습니다.
server로부터 가져온 state는 serialized되거나 수화되어 전달되며 클라이언트에서 추가적인 코딩 없이도 사용할 수 있습니다. 또한 빠른 개발 사이클을 위해 현재 개발중인 앱의 상태를 저장하며 state 객체 트리를 하나만 갖고 있기 때문에 undo, redo를 쉽게 구현할 수 있습니다.

2. State는 read only이다.

오직 'Action'을 reducer에게 전달하는 방법으로만 상태를 변화시켜야 합니다. 아래 예시처럼 직접 state를 mutate(변형)하지 않아야 합니다. 모든 상태 변화는 reducer등 API를 통해 중앙 store에서 관리되어야 합니다.

// Don't 
state_arr = []
state_arr.push([abc])

// Do This!!
const reducer = (state = [], action) => {
  switch (action.type) {
    case ADD_TODO:
      return [...state, { text: action.text, id: Date.now() }];
    case DELETE_TODO:
      return state.filter((toDo) => toDo.id !== action.id);
    default:
      return state;
  }
};

3. Reducer는 순수하게 Function으로 작성되어야한다.

변화는 순수 함수로 작성되어야 한다. 즉, 상태 트리의 변화에 대한 내용은 프로그래머가 순수한 함수로 작성해야 합니다.
리듀서는 이전 상태와 액션을 parameter로 받아 다음의 상태를 반환하는 함수입니다.
이때, 리듀서는 이전 상태를 변경하는 대신 Action에 따라 새로운 상태 객체를 생성해서 반환합니다.
작은 프로젝트에선 하나의 리듀서로 상태를 관리하는데에 충분하지만 앱이 커져감에 따라 특정 상태 트리를 조작하는 더 작은 단위의 리듀서로 나누고 다시 combine하는 것도 가능합니다.

// reducer를 나눈 예시
function visibilityFilter(state = 'SHOW_ALL', action) {
  switch (action.type) {
    case 'SET_VISIBILITY_FILTER':
      return action.filter
    default:
      return state
  }
}

function todos(state = [], action) {
  switch (action.type) {
    case 'ADD_TODO':
      return [
        ...state,
        {
          text: action.text,
          completed: false
        }
      ]
    case 'COMPLETE_TODO':
      return state.map((todo, index) => {
        if (index === action.index) {
          return Object.assign({}, todo, {
            completed: true
          })
        }
        return todo
      })
    default:
      return state
  }
}

import { combineReducers, createStore } from 'redux'
const reducer = combineReducers({ visibilityFilter, todos })
const store = createStore(reducer)

리덕스로 완성한 ToDoList 예제


참고 문서

- Redux 공식 문서
- Redux-Toolkit

profile
멘티를 넘어 멘토가 되는 그날까지 파이팅

0개의 댓글