1. 비동기 작업을 처리하는 미들웨어 사용
1.1.5 리펙토링
API를 요청해야 할떄마다 17줄이 넘는 thunk 함수를 작성하는 일과 로딩상태를 리듀서에서 관리하는 작업은 코드가 길어지고 번거로운 일입니다. 그렇기에 이번에는 반복되는 코드의 양을 줄여서 리펙토링을 진행 하겠습니다.
- lib/createRequestThunk.js
export default function createRequestThunk(type, request){
// 성공 및 실패 액션타입 정의
const SUCCESS = `${type}_SUCESS`
const FAILURE = `${type}_FAILURE`
return parms => async dispatch => {
dispatch({ type }) //시작
try {
const response = await request(parms)
dispatch({
type: SUCCESS,
payload: response.data
}) // 성공
} catch (e) {
dispatch({
type: FAILURE,
payload: e,
error: true
}) // 에러
throw e
}
}
}
// 사용법: createRequestThunk('GET_USERS', api, getUSers)
이번에 작성한 유틸함수를 사용하여 API 요청을 하는 thunk 함수를 한 줄로 줄여 보겠습니다.
액션 타입과 API 요청 함수를 파라미터로 넣어서 나머지 작업을 대신처리 해줍니다.
import { handleActions } from 'redux-actions'
import * as api from '../lib/api'
import createRequestThunk from '../lib/createRequestThunk'
// 액션 타입을 선언합니다.
// 한 요청당 세 개를 만들어야 합니다.
const GET_POST = "sample/GET_POST"
const GET_POST_SUCCESS = "sample/GET_POST_SUCCESS"
const GET_POST_FAILURE = "sample/GET_POST_FAILURE"
const GET_USERS = "sample/GET_USERS"
const GET_USERS_SUCCESS = "sample/GET_USERS_SUCCESS"
const GET_USERS_FAILURE = "sample/GET_USERS_FAILURE"
// thunk 함수를 생성합니다.
// thunk 함수 내부에서는 시작할 떄, 성공했을 떄, 실패했을 때 다른 액션을 디스패치합니다.
export const getPost = createRequestThunk(GET_POST, api.getPost)
export const getUsers = createRequestThunk(GET_USERS, api.getUsers)
// 초기 상태를 선언합니다.
// 요청의 로딩 중 상태는 loading이라는 객체에 관리합니다.
const initialState = {
loading: {
GET_POST: false,
GET_USERS: false,
},
post: null,
users: null,
}
const sample = handleActions({
[GET_POST]: state => ({
...state,
loading: {
...state.loading,
GET_POST: true, // 요청 시작
}
}),
[GET_POST_SUCCESS]: (state, action) => ({
...state,
loading: {
...state.loading,
GET_POST: false, // 요청 시작
},
post: action.payload
}),
[GET_POST_FAILURE]: (state, action) => ({
...state,
loading: {
...state.loading,
GET_POST: false, // 요청 완료
}
}),
[GET_USERS]: state => ({
...state,
loading: {
...state.loading,
GET_USERS: true, // 요청 완료
}
}),
[GET_USERS_SUCCESS]: (state, action) => ({
...state,
loading: {
...state.loading,
GET_USERS: false, // 요청 완료
},
users: action.payload
}),
[GET_USERS_FAILURE]: (state, action) => ({
...state,
loading: {
...state.loading,
GET_USERS: false, // 요청 완료
}
})
},
initialState
)
export default sample
코드는 훨씬 줄어들고 동일하게 작동하는 것을 볼수 있을겁니다.
이번에는 요청의 로딩 상태를 관리하는 작업을 개선하겠습니다. 기존에는 리듀서 내부에서 요청에 관련된 액션이 디스패치될 때마다 로딩 상태를 변경 해주었습니다. 이 작업을 로딩 상태만 관리하는 리덕스 모듈을 따로 생성하여 처리 해주겠습니다.
- modules/loading.js
import { createAction, handleActions } from "redux-actions"
const START_LOADING = "loading/START_LOADING"
const FINISH_LOADING = "loading/FINISH_LOADING"
/*
요청을 위한 액션 타입을 payload로 설정합니다 (ex: "sample/GET_POST")
*/
export const startLoading = createAction(
START_LOADING,
requesType => requesType
)
export const finishLoading = createAction(
FINISH_LOADING,
requesType => requesType
)
const initialState = {}
const loading = handleActions(
{
[START_LOADING]: (state, action) => ({
...state,
[action.payload]: true
}),
[FINISH_LOADING]: (state, action) => ({
...state,
[action.payload]: false
})
},
initialState
)
export default loading
리듀서 작성이 끝났다면 이제 루트리듀서에 추가해 줍니다.
- modules/index.js
import { combineReducers } from 'redux'
import counter from './counter'
import sample from './sample'
import loading from './loading';
// 루트 리듀서 작성
const rootReducer = combineReducers({
counter,
sample,
loading
});
export default rootReducer;
loading 모듈에서 만든 액션 생성 함수는 앞에서 만든 createRequestThunk에서 사용합니다.
lib/createRequestThunk.js
import { startLoading, finishLoading } from "../modules/loading"
export default function createRequestThunk(type, request){
// 성공 및 실패 액션타입 정의
const SUCCESS = `${type}_SUCCESS`
const FAILURE = `${type}_FAILURE`
return parms => async dispatch => {
dispatch({ type }) //시작
try {
const response = await request(parms)
dispatch({
type: SUCCESS,
payload: response.data
}) // 성공
dispatch(finishLoading(type))
} catch (e) {
dispatch({
type: FAILURE,
payload: e,
error: true
}) // 에러
dispatch(startLoading(true))
throw e
}
}
}
// 사용법: createRequestThunk('GET_USERS', api, getUSers)
로딩 상태를 조회 할 수 있도록 아래와 같이 수정합니다.
conatiner/SampleContainer.js
import React from "react"
import { connect } from "react-redux"
import Sample from "../components/Sample"
import { getPost, getUsers } from "../modules/sample"
const { useEffect } = React
const SampleContainer = ({
getPost,
getUsers,
post,
users,
loadingPost,
loadingUsers
}) => {
// 클래스 형태 컴포넌트이면 componentDidMount 사용
useEffect(() => {
getPost(1)
getUsers(1)
}, [getPost, getUsers])
return (
<Sample
post={post}
users={users}
loadingPost={loadingPost}
loadingUsers={loadingUsers}
/>
)
}
export default connect(
({ sample, loading }) => ({
post: sample.post,
users: sample.users,
loadingPost: loading['sample/GET_POST'],
loadingUsers: loading['sample/GET_']
}),
{
getPost,
getUsers
}
)(SampleContainer)
이제 sample 리듀서에서 불필요한 코드를 제거해 줍니다.
- modules/sample.js
import { handleActions } from 'redux-actions'
import * as api from '../lib/api'
import createRequestThunk from '../lib/createRequestThunk'
// 액션 타입을 선언합니다.
// 한 요청당 세 개를 만들어야 합니다.
const GET_POST = "sample/GET_POST"
const GET_POST_SUCCESS = "sample/GET_POST_SUCCESS"
const GET_USERS = "sample/GET_USERS"
const GET_USERS_SUCCESS = "sample/GET_USERS_SUCCESS"
// thunk 함수를 생성합니다.
// thunk 함수 내부에서는 시작할 떄, 성공했을 떄, 실패했을 때 다른 액션을 디스패치합니다.
export const getPost = createRequestThunk(GET_POST, api.getPost)
export const getUsers = createRequestThunk(GET_USERS, api.getUsers)
// 초기 상태를 선언합니다.
// 요청의 로딩 중 상태는 loading이라는 객체에 관리합니다.
const initialState = {
post: null,
users: null
}
const sample = handleActions({
[GET_POST_SUCCESS]: (state, action) => ({
...state,
post: action.payload
}),
[GET_USERS_SUCCESS]: (state, action) => ({
...state,
users: action.payload
})
},
initialState
)
export default sample
코드가 훨씬 가독성도 좋아지고 깔끔해졌습니다.
이제 sample 리듀서에서 로딩 중의 상태를 관리할 필요가 없어졌습니다. 성공시에 상태만 초기 값으로 받아서 처리해주면 됩니다.
별도로 실패했을 떄의 케이스를 관리하고 싶다면 _FAILURE 가 붙은 액션을 리듀서에서 처리해주면 됩니다.
또는 try/catch 문을 사용하여 에러 값을 조회할 수 있습니다.
'프론트엔드 > React' 카테고리의 다른 글
[프론트엔드] 리덕스 미들웨어를 통한 비동기 작업 관리[8] (0) | 2022.06.02 |
---|---|
[프론트엔드] 리덕스 미들웨어를 통한 비동기 작업 관리[7] (0) | 2022.05.01 |
[프론트엔드] 리덕스 미들웨어를 통한 비동기 작업 관리[5] (0) | 2022.04.26 |
[프론트엔드] 리덕스 미들웨어를 통한 비동기 작업 관리[4] (0) | 2022.04.19 |
[프론트엔드] 리덕스 미들웨어를 통한 비동기 작업 관리[3] (0) | 2022.04.19 |