
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]);
참고