이전의 블로그와 다르게 이번에는 TypeScript로 애플리케이션을 구현하고 있는데 redux, redux-saga를 활용하려고 하는 부분에서 불편한 점이 많았고, 참고한 여러 포스트에서 typesafe-actions라는 모듈을 활용하여 Reducer코드를 리팩토링하는 포스트와 코드를 꽤 보았기에 저도 학습하여 사용하고자 했습니다.
typesafe-actions는 Redux 아키텍쳐에서 Reduce의 다변성과 복잡성을 감소시키기위해 설계된 Typesafe 유틸리티입니다.
단순 액션 생성함수입니다.
import { createAction } from 'typesafe-actions';
const ACTION = 'ACTION';
const action = createAction(ACTION, ({ ...parameters }) => ({ ...parameters }))()
typesafe-actions를 사용하는데 createAction
을 호출해주지 않으면 에러가 발생합니다.
비동기 액션을 생성시켜주는 함수입니다.
import { createAsyncAction } from 'typesafe-actions';
interface requestPayloadType {
...
};
interface successPayloadType {
...
};
const ACTION_REQUEST = 'ACTION_REQUEST';
const ACTION_SUCCESS = 'ACTION_SUCCESS';
const ACTION_FAILURE = 'ACTION_FAILURE';
const postingAsync = createAsyncAction(
ACTION_REQUEST,
ACTION_SUCCESS,
ACTION_FAILURE
)<requestPayloadType, successPayloadType, Error>();
만약 Axios를 활용하는 경우 requestPayloadType, AxiosResponse<successPayloadType>, AxiosError
와 같은 형식을 generic 인수로 입력해주면 됩니다.
createAction
과 마찬가지로 함수를 호출해주어야지 액션 생성함수가 정상적으로 생성됩니다.
리듀서를 생성해주는 함수입니다.
import { createReducer } from 'typesafe-actions';
// createAction action
const reducer = createReducer(initialState, {
[ACTION, ACTION2]: (state, action) => ({
...state,
key: action.payload.value
})
});
위와 같이 리듀서를 생성할 수 있고 배열을 통한 복수 케이스의 처리도 가능합니다.
createCustomAction
, AsyncActionCreator
등의 메서드가 있지만 이후에 코드 리팩토링을 진행하면서 자세하게 살펴볼 예정입니다.
npm i --save typesafe-actions
먼저 typesafe-actions
모듈을 install 해주었습니다.
import { createReducer, createAsyncAction, ActionType } from 'typesafe-actions';
import { ICategoryHead, IPost, IPostsState } from '@typings/datas';
import { AxiosError, AxiosResponse } from 'axios';
const initialState: IPostsState = {
Category: [],
posts: [],
numberOfPosts: 0,
isLoaddingPosts: false,
isLoadedPosts: false,
loadPostsErrorReason: null,
EndOfPosts: false,
};
export const LOAD_POSTS_REQUEST = 'posts/LOAD_POSTS_REQUEST';
export const LOAD_POSTS_SUCCESS = 'posts/LOAD_POSTS_SUCCESS';
export const LOAD_POSTS_FAILURE = 'posts/LOAD_POSTS_FAILURE';
export const loadPostsAsync = createAsyncAction(LOAD_POSTS_REQUEST, LOAD_POSTS_SUCCESS, LOAD_POSTS_FAILURE)<
null,
AxiosResponse<IPost[]>,
AxiosError
>();
const actions = {
loadPostsAsync
};
type PostsAction = ActionType<typeof actions>;
const postsReducer = createReducer<IPostsState, PostsAction>(initialState, {
[LOAD_POSTS_REQUEST]: (state) => ({
...state,
isLoaddingPosts: true,
}),
[LOAD_POSTS_SUCCESS]: (state, { payload }) => ({
...state,
isLoaddingPosts: false,
posts: state.posts.concat(payload.data),
EndOfPosts: payload.data.length !== 8,
}),
[LOAD_POSTS_FAILURE]: (state, { payload: error }) => ({
...state,
isLoaddingPosts: false,
loadPostsErrorReason: 'error',
})
});
export default postsReducer;
위와 같이 테스트를 위한 간단한 리듀서를 작성하였습니다.
다음 포스트에서는 Saga를 연결한 뒤 post를 정상적으로 불러오는 지 테스트해보겠습니다.