오늘은 나의 리덕스 작성 방법 변천사에 대해 알아보려고 한다.
가장 만만한 예제는 역시 Counter 겠지요?
const initialState = {
value: 0
}
export const INCREMENT = 'INCREMENT'
export const DECREMENT = '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
어딜 찾아봐도 가장 흔히 볼 수 있는 코드지만, 리덕스를 처음 접했을 때는 이마저도 너무 어려웠다 ㅎㅎ
카운터기능 하나를 구현하는데 이렇게 많이 코드를 작성해놔야 한다고??
라는 의문으로 시작했던 리덕스...
앞의 모든 것들을 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 패턴이고 지금도 애용하고 있다.
파일은 하나로 합쳐졌지만 아직까지도 액션 생성 함수를 작성하는 것에 있어서 코드가 길다고 생각했다. 그 때 혜성처럼 눈앞에 등장했던 것이 바로 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를 이용하여 훨~씬 간결해 진 것을 알 수 있다. 그 다음 페이즈에 접어들기 전까지 가장 많이 사용했던 방법이었다.
그렇게 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을 사용하며 더 익숙해지는 시간을 갖도록 할 것이다,,,!