리덕스를 사용하다 보면, 느끼는 것이 있습니다.

  • 잘 사용하기 위해서는 너무 복잡한 구성을 필요로 합니다.
  • 복잡한 구성 만큼이나 많은 미들웨어와 추가적인 패키지를 설치해야 됩니다.
  • 보일러플레이트 코드가 과도하게 많이 사용 됩니다.

위 문제점을 toolkit이 완전하게 해결 하지는 못하지만, redux를 사용하는 사용자들의 불편을 완화 시켜 준다고 합니다.
줄여서 RSK 라고 부릅니다.

설치

yarn add @reduxjs/toolkit

or 

npm install @reduxjs/toolkit

toolkit에 포함된 도구들

  • configureStore() : createStore 를 통한 복잡한 설정을 단순화 하고, redux devtools 설정을 추가 해주었습니다.
  • createReducer() : switch 문을 작성하지 않고, case reduce 처리. state의 불변 업데이트 지원.
  • createAction() : Flux standard action 지원
  • createSlice() : duck 패턴 지원 
  • createSelector : Reselector 지원 

configStore

store를 생성하고 미들웨어를 추가하는 부가적인 작업들이 아래와 같이 간단하게 변경 되었습니다.

const store = configureStore({
  reducer: rootReducer,
  middleware: [thunk, logger],
})

createSlice

createReducer & createAction 기능을 통합하여 createSlice 라는 것이 생겼습니다.

더 자세히 알고 싶으시다면, ducks패턴 에 대해서 알아 보시면 됩니다.

import { createSlice } from '@reduxjs/toolkit'

const todosSlice = createSlice({
  name: 'todos',
  initialState: [],
  reducers: {
    addTodo(state, action) {
      const { id, text } = action.payload
      state.push({ id, text, completed: false })
    },
    toggleTodo(state, action) {
      const todo = state.find(todo => todo.id === action.payload)
      if (todo) {
        todo.completed = !todo.completed
      }
    }
  }
})

export const { addTodo, toggleTodo } = todosSlice.actions

export default todosSlice.reducer

구문

createSlice는 옵션과 함께 옵션 객체를 인수로 사용합니다.

  • name: 생성 된 조치 유형의 접 두부로 사용되는 문자열
  • initialState : Reducer의 초기 상태 값
  • reducers : 초기 문자열이 키가 되며, 이후 함수는 실행될 내용입니다. (case reducers)

불필요한 'default' 처리

default 처리가 필요 없습니다. createSlice는 현재 상태를 반환하여 주므로 switch문을 사용하던 것과 같이 default를 나열하지 않아도 됩니다.

immutable로 부터 해방

state는 불편 하게 처리 하여야 되기 때문에 수정이 불가능 하였습니다. RSK는 createReducer, createSlice의 API의 내부적 처리를 대신 해주기 때문에 mutable한 state 객체를 주게 됩니다.

createAction

redux-actions 에서 지원하는 API중 createAction를 동일한 이름 을 지원 합니다.

const INCREMENT = 'INCREMENT'
const DECREMENT = 'DECREMENT'

function increment() {
  return { type: INCREMENT }
}

function decrement() {
  return { type: DECREMENT }
}

function counter(state = 0, action) {
  switch (action.type) {
    case INCREMENT:
      return state + 1
    case DECREMENT:
      return state - 1
    default:
      return state
  }
}

const store = Redux.createStore(counter)

document.getElementById('increment').addEventListener('click', () => {
  store.dispatch(increment())
})

위 구문이 createAction를 사용하면 아래 구문이 됩니다.

const increment = createAction('INCREMENT')
const decrement = createAction('DECREMENT')

function counter(state = 0, action) {
  switch (action.type) {
    case increment.type:
      return state + 1
    case decrement.type:
      return state - 1
    default:
      return state
  }
}

const store = Redux.createStore(counter)

document.getElementById('increment').addEventListener('click', () => {
  store.dispatch(increment())
})

payload

FSA(Flux Standard Actions) 규약은 임의의 이름을 가진 데이터 필드를 직접 가지지 않고, payload라는 이름을 가진 객체에 데이터 필드를 가져야 된다고 제안 합니다.
즉, {type, id, password}가 있다면, id와 password를 payload에 넣어 {type, payload}로 구성 한다는 것입니다.

interface Action<Payload> extends AnyAction {
  type: string
  payload: Payload
  error?: boolean
  meta?: Meta
}

createReducer

action과 reducer를 연결해주는 역할을 하며, redux-actions에서는 handleAction 이라는 API로 지원 되었습니다. 이를 createReducer라는 이름으로 지원 합니다.

import { createAction, createReducer } from 'redux-toolkit'

const increment = createAction('INCREMENT')
const decrement = createAction('DECREMENT')

const counter = createReducer(0, {
  [increment.type]: state => state + 1,
  [decrement.type]: state => state - 1,
})

const store = configureStore({
  reducer: counter,
})

또, createSlice 와 동일하게, immutable 관련 문제를 지원 합니다.

createSelector

Reselector API를 통해 사용하던 selector 기능 입니다. store에 어떤 값에 반복 접근할때 memorization을 통해 성능을 향상 시키게 됩니다. Vue진영에서는 Vuex getter, MobX는 computed로 기본 적으로 제공 되던 기능인듯 합니다.

import { createSelector } from '@reduxjs/toolkit'

const selectTodos = state => state.todos
const selectFilter = state => state.visibilityFilter

const selectVisibleTodos = createSelector(
  [selectTodos, selectFilter],
  (todos, filter) => {
    switch (filter) {
      case VisibilityFilters.SHOW_ALL:
        return todos
      case VisibilityFilters.SHOW_COMPLETED:
        return todos.filter(t => t.completed)
      case VisibilityFilters.SHOW_ACTIVE:
        return todos.filter(t => !t.completed)
      default:
        throw new Error('Unknown filter: ' + filter)
  }
}

prefix가 get보다 select를 권장한다고 합니다.

'React.js' 카테고리의 다른 글

WebStorm 에서 prettier / ESLint 사용하기  (0) 2020.02.15

 프로젝트가 점점 커지면서, "코드가 일괄적이면 좋겠다." 라는 생각을 하게 되었고, prettier & ESLint를 동시에 설치 하게 되었습니다.

설치후, 많은 에러를 보고 한숨을 크게 한번 쉬었지만, ESLint가 선생님 처럼 좋은 코드를 알려주니, 또 귀찮다는 이유로 시간이 부족하다는 이유로 뛰엄뛰엄 작성 하던 코드를 다시 잡아주니, 공부도 되고 좋다는 생각을 하게 되었습니다.

 

그럼 Prettier 부터 설치 해보기로 하겠습니다.

Prettier

참조 : https://prettier.io/index.html

Prettier란?

코드 스타일을 통일해서 가독성을 높여 주는 도구입니다. 예를 들어 싱글 따움표를 체크하여 쌍따움표로 바꿔 준다는지, Object의 마지막 속성의 쉼표가 불필요 하여 제거 한다던지 하는 역할을 합니다. ( 말로 표현할려니 힘드네요 ㅠㅠ )

Prettier 설치

Webstorm plugin 중에 Prettier를 설치 합니다.

webstorm plugin에서 prettier을 설치 

project에 의존성을 추가 해줍니다. 개발용일때만 사용을 하니 devDependencies 로 추가해줍니다.

> npm install --save-dev prettier

 

이후, 저의 경우 package.js에 prettier을 설정 하였지만, rc파일로 관리 하셔도 됩니다. prettier를 다음과 같이 설정 합니다.

  "prettier": {
    "printWidth": 100,
    "trailingComma": "none",
    "tabWidth": 2,
    "semi": true,
    "singleQuote": true
  },

저와 같은 경우 위와 같이 설정 하였습니다.

이후, 파일을 저장 할때, prettier가 동작 하도록 한다면 매우 좋겠죠?

이것은 File Watchers 를 이용합니다.

Webstorm의 Settings > Tools > File Watchers 에서 '+' 를 선택 하시고, Prettier를 선택 합니다.

저의 경우 다음과 같이 설정 하였습니다.

Webstorm 에서 Prettier 설정 

Prettier 설치는 완료! 입니다. 

 

ESLint 

참조 : https://eslint.org/

ESLint는 문법적인 스타일을 검사하고, 룰에 맞지 않는다면 에러가 발생 합니다. 그래서 어떤 룰을 사용하느냐에 따라서 달라지는데요. 저의 경우 엄격하게 관리하기 위해 airbnb에서 제공 하는 ESLint를 사용하게 되었습니다. 

 

ESLint 설치 

CRA를 이용하여 설치 하였다면, ESLint가 기본적으로 설치 되어 있겠지만, 여기서 중요한것은 Prettier와 연동일 것입니다. 그러므로 빠른 진행을 위해 devDependencies 를 확인하고 바로 설정 정보를 공유 하겠습니다.

"eslint-config-prettier" 라는 녀석이 Prettier와 ESLint를 서로 충돌하지 않고 연동 되도록 도와 줍니다. 저의 경우 ESLint를 설치하고 File Watchers 에 등록하여, 파일을 저장과 함께 ESLint를 꼼꼼하게 확인 합니다.

 

개발 의존성은 다음과 같습니다.

"devDependencies": {
    "eslint": "^6.1.0",
    "prettier": "^1.14.3",
    "babel-eslint": "^10.0.3",
    "eslint-config-airbnb": "^18.0.1",
    "eslint-config-prettier": "^6.10.0",
    "eslint-plugin-import": "^2.20.0",
    "eslint-plugin-jsx-a11y": "^6.2.3",
    "eslint-plugin-react": "^7.18.0",
    "eslint-plugin-react-hooks": "^1.7.0"
  },

이후, ESLint를 설정합니다. ESLint 또한 rc파일을 이용하셔도 되지만, 저는 package.js파일에 등록 하였습니다.

"eslintConfig": {
    "parser": "babel-eslint",
    "env": {
      "browser": true,
      "node": true,
      "es6": true
    },
    "extends": [
      "airbnb",
      "plugin:react/recommended",
      "prettier"
    ],
    "plugins": [
      "react"
    ],
    "rules": {
      "react/prefer-stateless-function": 0,
      "react/jsx-filename-extension": 0,
      "react/jsx-one-expression-per-line": 0,
      "jsx-a11y/click-events-have-key-events": 0,
      "jsx-a11y/mouse-events-have-key-events" : 0,
      "jsx-a11y/interactive-supports-focus" : 0,
      "react/jsx-props-no-spreading": ["error",{
        "html" : "enforce",
        "custom" : "ignore",
        "explicitSpred": "ignore"
      }],
      "no-console" : ["error", { "allow": ["warn", "error"] }],
      "comma-dangle": [
        "error",
        "never"
      ]
    },
    "settings": {
      "import/resolver": {
        "node": {
          "paths": [
            "src"
          ],
          "extensions": [
            ".js",
            ".jsx",
            ".ts",
            ".tsx"
          ]
        }
      }
    }
  },
  "lint-staged": {
    "src/**/*.{js,ts}": [
      "eslint --fix",
      "git add"
    ]
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  }

저의 경우 불필요한 룰은 OFF시키거나, 완화 하였습니다. 이것은 도저히 사용 못하겠다고 생각 되는 것은 그렇게 하였습니다.

다음은 File Watchers 설정입니다.

ESLint File Watchers 설정

위와 같이 하였습니다. 

끝으로..

회사에서는 지향하는 코드 스타일이 있어서 그것을 별도로 적용 하여 사용중이고, 이것은 개인 프로젝트의 설정을 기억하는 차원에서 나름 끄적 보았는데요. 도움이 되실길 바랍니다.

혹시, ESLint 사용중 test 파일을 검사에서 제외 하고 싶다면,  eslintignore 설정이 있으니 설정 해보시면 좋을듯 합니다. 

감사합니다 :)

'React.js' 카테고리의 다른 글

Redux toolkit을 사용해보자  (0) 2020.02.21

+ Recent posts