axios
사용
API 호출 시, 주로 axios
(Promise 기반 웹 클라이언트) 사용
설치
$ yarn add axios
함수화 이유
API 호출 함수 → 따로 작성 시, 가독성 ↑ & 유지보수 ↑
코드
import axios from 'axios';
export const getPost = (id) =>
axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`);
export const getUsers = (id) =>
axios.get(`https://jsonplaceholder.typicode.com/users`);
export
사용 이유
다른 파일에서 불러와 사용 가능
- 모듈 생성(
modules/loading.js
)
import { createAction, handleActions } from 'redux-actions';
const START_LOADING = 'loading/START_LOADING';
const FINISH_LOADING = 'loading/FINISH_LOADING';
// 요청을 위한 액션 타입을 payload로 설정 (예: "sample/GET_POST")
export const startLoading = createAction(
START_LOADING,
(requestType) => requestType,
);
export const finishLoading = createAction(
FINISH_LOADING,
(requestType) => requestType,
);
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.is
)
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;
- api 요청 과정에 적용 (
lib/createRequestThunk.js
)
→ createRequestThunk.js
제작 후 적용
- container 컴포넌트에 적용 (
containers/SampleContainer.js
)
→ SampleContainer.js
제작 후 적용
액션 타입 선언
한 요청 당 세 개 만들기 → 시작, 성공, 실패
thunk 함수
함수 내부 → 시작/성공/실패 시 다른 액션 디스패치
초기 상태 선언
로딩 상태: 로딩 모듈에서 관리
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);
// 초기 상태 선언
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;
사용법
createRequestThunk(액션 타입, api 요청 함수)
코드
import { startLoading, finishLoading } from '../modules/loading';
export default function createRequestThunk(type, request) {
// 액션 타입 정의 (성공 및 실패)
const SUCCESS = `${type}_SUCCESS`;
const FAILURE = `${type}_FAILURE`;
return (params) => async (dispatch) => {
dispatch({ type }); // 시작됨
dispatch(startLoading(type));
try {
const response = await request(params);
dispatch({
type: SUCCESS,
payload: response.data,
}); // 성공
dispatch(finishLoading(type));
} catch (e) {
dispatch({
type: FAILURE,
payload: e,
error: true,
}); // 에러 발생
dispatch(startLoading(type));
throw e; // 나중에 컴포넌트 단에서 에러 조회 가능
}
};
}
import { combineReducers } from 'redux';
import counter from './counter';
**import sample from './sample';**
const rootReducer = combineReducers({
counter,
**sample**,
});
export default rootReducer;
중요: 데이터 불러와서 렌더링 시 → 유효성 검사 필수
이유: 해당 데이터를 사용 시 → 데이터가 없는 상태라면, js 오류 발생.
{!loadingPost && post && (
<div>
<h3>{post.title}</h3>
<h3>{post.body}</h3>
</div>
)}
import React from 'react';
const Sample = ({ loadingPost, loadingUsers, post, users }) => {
return (
<div>
<section>
<h1>포스트</h1>
{loadingPost && '로딩 중...'}
{!loadingPost && post && (
<div>
<h3>{post.title}</h3>
<h3>{post.body}</h3>
</div>
)}
</section>
<hr />
<section>
<h1>사용자 목록</h1>
{loadingUsers && '로딩 중...'}
{!loadingUsers && users && (
<ul>
{users.map((user) => (
<li key={user.id}>
{user.username} ({user.email})
</li>
))}
</ul>
)}
</section>
</div>
);
};
export default Sample;
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_USERS'],
}),
{ getPost, getUsers },
)(SampleContainer);
import React from 'react';
**import SampleContainer from './containers/SampleContainer';**
const App = () => {
return (
<div>
<**SampleContainer** />
</div>
);
};
_FAILURE
가 붙은 액션 → 리듀서에서 처리
컨테이너 컴포넌트 → try/catch 구문 사용 (에러 값 조회)
useEffect(() => {
// [에러 값 조회]
// useEffect에 파라미터로 넣는 함수는 async로 할 수 없기 때문에
// 그 내부에서 async 함수 선언 & 호출
const fn = async () => {
try {
await getPost(1);
await getUsers(1);
} catch (e) {
console.log(e); // 에러 조회
}
};
fn();
}, [getPost, getUsers]);
참고