회사에서 사용하고 있는 리덕스를 공부하던 중
typesafe-actions 이라는 라이브러리를 발견했다.
공식 설명으로는 아래와 같이 소개된다.
✌ Redux 아키텍처에서 유형의 장황함과 복잡성을 줄이기 위해 설계된 Typesafe 유틸리티입니다. (번역)
🤔그동안 공부하면서 이걸 왜 처음 들었지 싶었는데,
리덕스를 사용했을 때 부터 그동안 쭉 redux-toolkit과 redux-thunk 을 사용해왔기 때문이었다. 나에게 리덕스를 사용한다는 것은 당연히 이 두가지를 함께 사용하는 것과 같았다. 그래서 특별히 리덕스에 대한 보일러플레이트 불만이 없는 편이었다. 물론 recoil의 간편함에 비할 바는 아니지만..
아무튼 사용해보기 전 typesafe-actions와 redux-toolkit의 장단점과 차이를 알고 싶었다. 무엇보다 이 차이를 공부하다 보면 redux에 대해 더 깊이 이해할 수 있을 것 같다.
이에 간략히 검색해보니 아래의 대략적인 차이를 알 수 있었다.
redux-toolkit은 redux 공식 개발 툴이다.
그만큼 유지보수가 원활함. 글을 쓰는 시점에도 하루 전에 publish가 되어있지만 typesafe-actions는 3년전이다..
redux-toolkit의 thunk,immer 내장
redux-thunk는 미들웨어로써 redux에서 비동기 작업을 다루어야 할 때 많이 쓰인다. 액션객체를 그저 생성하는 것이 아닌, 액션 함수에서 여러가지 작업을 할 수 있게 만든다. redux 공식에서 권장하고 있는 방법이며, 굉장히 편리하다👍
사실 위의 두가지 만으로도 typesafe-actions이 아닌 redux-toolkit을 사용할 이유는 충분해 보인다. 또한 툴킷의 여러가지 유용한 api들도 무시할 수 없으니 나는 앞으로도 툴킷을 사용해야겠다고 판단했다.
하지만, 현재 회사 서비스는 툴킷 이전에 개발 되었기 때문에 쭉 typesafe-actions를 사용해오고 있다. 그런데 툴킷을 사용하지 않는다고 서비스에 문제가 있는 것도 아니며 이 라이브러리 또한 redux를 효율적으로 사용할 수 있도록 많은 도움을 준다.
그리고 무엇보다 어차피 나에겐 선택권이 없으니 typesafe-actions에 대해 간단히 알아보자
🤷♀️ 각설하고 우선 코드부터 보자.
https://react.vlpt.us/using-typescript/05-ts-redux.html
위 블로그를 참고해 간단 예시를 적어보자면
기존 리덕스는
const INCREASE = 'counter/INCREASE' as const;
export const increase = (num:number) => ({
type: INCREASE
});
와 같이 선언하고 액션 생성 함수를 직접 만들어야 했으나
const ADD_TODO = createAction('counter/INCREASE')
로 끝낼 수 있게 된다.
확실히 보일러플레이팅을 줄어들고 더 보기 좋게 코드를 작성할 수 있으며 타입선언을 보다 편하게 도와주고 있다.
import {
ActionType,
createAction,
getType,
isActionOf,
createAsyncAction,
} from 'typesafe-actions'
내가 공부하려고 하는 소스코드는 위의 api들로 스토어를 구성하고 있는데 역할을 하나씩 정리해보았다.
type helper api라고 적혀있는 이 api는 리덕스에서 nested구조로 복잡하게 되어있는 액션이어도 한번에 액션 type으로 정의해준다.
import { ActionType } from 'typesafe-actions';
// with "import * as ..."
import * as todos from './actions';
export type TodosAction = ActionType<typeof todos>;
// TodosAction: { type: 'action1' } | { type: 'action2' } | { type: 'action3' }
// with nested action-creator map case
const actions = {
action1: createAction('action1'),
nested: {
action2: createAction('action2'),
moreNested: {
action3: createAction('action3'),
},
},
};
export type RootAction = ActionType<typeof actions>;
// RootAction: { type: 'action1' } | { type: 'action2' } | { type: 'action3' }
출처 : https://github.com/piotrwitek/typesafe-actions
액션 함수를 생성해준다.
export const addTodo = createAction(ADD_TODO, action => (text: string) =>
action({
id: nextId++,
text
})
);
출처 : https://react.vlpt.us/using-typescript/05-ts-redux.html
파라미터를 기반한 payload를 설정하는 action 객체를 만드는 함수이다. 단순 createAction으로 싸매지 않고 action만 리턴하는 함수를 만들어도 구현은 가능하나, action-helpers-api(액션 타입을 체크하는 api)지원이 되지 않는다고 한다. 대표적으로 getType
/isActionOf
가 있다.
비슷하지만 사용법이 다른 createCustomAction
이라는 메소드도 있다.
const add = createCustomAction('todos/ADD', type => {
return (title: string) => ({ type, id: cuid(), title, completed: false });
});
createAction
은 콜백에 type을 넣어줘야하지만 createCustomAction
은 콜백에 포함해줘야하는, 말 그대로 커스텀 액션이다.
타입을 체크하는 action-helpers-api. actionCreator로 생성한 것들만 체크된다. 일반 action은 X
getType
actionCreator의 type을 가져옴
getType(actionCreator)
switch (action.type) {
case getType(add):
// action type is { type: "ADD"; payload: number; }
return state + action.payload;
default:
return state;
}
isActionOf
action이 주어진 actionCreator인자와 동일한 type인지 체크함
isActionOf(actionCreator, action)
[action1, action2, ...actionN]
.filter(isActionOf([addTodo, removeTodo])) // only actions with type `ADD` or 'REMOVE' will pass
.do((action) => {
// action type is { type: "todos/ADD"; payload: Todo; } | { type: "todos/REMOVE"; payload: Todo; }
...
출처 : https://www.npmjs.com/package/typesafe-actions
axios와 같은 비동기 흐름 처리를 단순화 하기 위해, 3가지로 구분된 액션 생성 함수를 포함하는 개체를 생성한다.
const fetchUsersAsync = createAsyncAction(
'FETCH_USERS_REQUEST',
'FETCH_USERS_SUCCESS',
'FETCH_USERS_FAILURE'
)<string, User[], Error>();
출처 : https://www.npmjs.com/package/typesafe-actions
전체적으로 typesafe-actions는 redux의 보일러플레이팅을 보조해준다는 느낌이고, redux-toolkit은 리덕스의 버전업?이라는 개인적인 느낌이 든다.
처음 회사에서 소스를 봤을 땐 rxjs와 redux-observable을 사용해서 스토어와 types가 상당히 복잡하게 얽혀있다. 이는 내가 rxjs를 아직 제대로 이해하지 못했기 때문에 미들웨어 부분은 추후 다시 공부해야 할 것 같다.
누군가 글에 틀린 점을 발견하셨다면 댓글 부탁드립니다!😘