[Redux] typesafe-actions로 Ducks 패턴 빠르게 작성하기

GONI·2022년 1월 1일
0
post-thumbnail

오늘은 나의 리덕스 작성 방법 변천사에 대해 알아보려고 한다.
가장 만만한 예제는 역시 Counter 겠지요?


1 - 입문용으로 따라서 작성한 가장 일반적인 코드

1. state 정의

const initialState = {
    value: 0
}

2. action 정의

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

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

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

3. reducer 생성

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

export default reducer

어딜 찾아봐도 가장 흔히 볼 수 있는 코드지만, 리덕스를 처음 접했을 때는 이마저도 너무 어려웠다 ㅎㅎ

카운터기능 하나를 구현하는데 이렇게 많이 코드를 작성해놔야 한다고??

라는 의문으로 시작했던 리덕스...


2 - ducks 패턴을 적용한 코드

1. counter.js module 생성

앞의 모든 것들을 module 폴더에 하나의 파일을 생성하여 때려박아주는 패턴이다. 다만 액션 타입을 구분해주기 위해 prefix를 선언해줘야 한다.

const initialState = {
    value: 0
}


const prefix = "project/counter
export const INCREMENT = `${prefix}INCREMENT`
export const DECREMENT = `${prefix}DECREMENT`


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

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


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

export default reducer

일단 파일이 하나로 합쳐진다는 것에서 너무 훌륭해 보였던 ducks 패턴이고 지금도 애용하고 있다.


3 - redux-actions을 활용한 리팩토링

파일은 하나로 합쳐졌지만 아직까지도 액션 생성 함수를 작성하는 것에 있어서 코드가 길다고 생각했다. 그 때 혜성처럼 눈앞에 등장했던 것이 바로 redux-actions 라이브러리이다.

import { createAction, handleActions } from "redux-actions"

const initialState = {
    value: 0
}


const prefix = "project/counter
export const INCREMENT = `${prefix}INCREMENT`
export const DECREMENT = `${prefix}DECREMENT`

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


const reducer handleActions(
    {
        [INCREMENT]: (state, action) => ({
            // 이 경우에는 payload가 없으므로 action을 생략해도 무관하다.
            value: state.value + 1
        }),
        [DECREMENT]: (state, action) => ({
            value: state.value - 1
        })
    },
    initialState
)

export default reducer

액션 생성 함수도 한줄로 줄여버렸다. reducer도 handleActions를 이용하여 훨~씬 간결해 진 것을 알 수 있다. 그 다음 페이즈에 접어들기 전까지 가장 많이 사용했던 방법이었다.


4 - typesafe-actions를 활용한 type 적용

그렇게 redux-actions와 절친이 될 때쯤,,, TypeScript에도 나름 익숙해지게 되면서 이제는 redux에도 TypeScript를 적용할 때가 되었다고 판단하였다. 하지만 redux-actions는 TypeScript와 그렇게 잘 어울리는 한 쌍은 아니었고, 그에 대한 대안으로 다시 혜성처럼 눈앞에 등장한 typesafe-actions였다.

import { createAction, createReducer, ActionType } from "typesafe-actions"

interface InitialState {
    value: number
}
  

const initialState: InitialState = {
    value: 0
}


const prefix = "project/counter
export const INCREMENT = `${prefix}INCREMENT` as const
export const DECREMENT = `${prefix}DECREMENT` as const

export const increment = createAction(INCREMENT)()
export const decrement = createAction(DECREMENT)()
// payload가 있을 경우
// export const increment = createAction(INCREMENT)<number>()
// 위 코드와 같이 작성 가능하다

// 액션에 대한 타입 지정
const actions = {increment, decrement}
type CounterActionType = ActionType<typeof actions>
  

const reducer = createReducer<InitialState, CounterActionType>(
    initialState,
    {
        [INCREMENT]: (state, action) => ({
            value: state.value + 1
        }),
        [DECREMENT]: (state, action) => ({
            value: state.value - 1
        })
    },
)

export default reducer

사실 redux-actions와 매우 비슷하다. 그래서 redux-actions를 사용하던 사람들 중 type에 대한 고민이 있었다면 충분히 넘어가기 좋은 라이브러리 인 것 같다.


이렇게 나의 리덕스 변천사는 총 4페이즈로 나뉘었고 앞으로는 typesafe-actions를 적극 도입해서 사용해 보려고 한다. 그 후에는 saga부분에서 type을 사용하며 더 익숙해지는 시간을 갖도록 할 것이다,,,!

profile
오로지 나의 기억력을 위한 일지

0개의 댓글