1. 프로젝트 생성
yarn create react-app .
2. 라이브러리 설치
yarn add redux react-redux redux-actions axios redux-saga redux-devtools-extension
3. 프로젝트 구성
프로젝트 구성은 아래와 같이 할 계획입니다. /src 하위부분만 살펴보면 되겠습니다.
/src
ㄴ components
* Leader.jsx
ㄴ containers
* LeaderContainer.jsx
ㄴ lib
* api.js (서버에 요청하는 비동기 함수 정의부분)
ㄴ modules
* index.js (redux와 saga를 통합하는 부분)
* leader.js ( action 타입, action 생성함수, redux, saga 정의하는 부분)
* App.js (컴포넌트 로딩하는 부분)
* Index.js (스토어 생성, Middleware 적용)
(1) src/lib/api.js
우선 서버에 요청을 담당하는 함수를 먼저 정의합니다. JSONPlaceholder라는 곳에서 제공되는 가짜 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`);
(2) src/modules/leader.js
Action Type과 Action Generator, Action Handler(Reducer), Saga를 정의합니다.
import { createAction, handleActions } from "redux-actions";
import { call, put, takeLatest } from "redux-saga/effects";
import * as api from "../lib/api";
const GET_POST = "leader/GET_POST"; // post 를 요청하는 액션
const GET_POST_SUCCESS = "leader/GET_POST_SUCCESS"; // post 요청 성공 시 데이터(post)를 전달하는 액션
const GET_POST_FAILURE = "leader/GET_POST_FAILURE"; // post 요청 실패 시 처리하는 액션
const GET_USERS = "leader/GET_USERS"; // users 를 요청하는 액션
const GET_USERS_SUCCESS = "leader/GET_USERS_SUCCESS"; // users 요청 성공 시 데이터(users) 를 전달하는 액션
const GET_USERS_FAILURE = "leader/GET_USERS_FAILURE"; // users 요청 실패 시 처리하는 액션
export const getPost = createAction(GET_POST, (id) => id); // payload가 id인 액션객체 생성
export const getUsers = createAction(GET_USERS); // payload가 없는 액션객체 생성
// state 의 초기값 설정
const initialState = {
post: null,
users: null,
};
//** Redux **//
// post/users 요청 성공 시 Data 처리하는 redux만 정의
// post/users 요청 실패 시 처리할 데이터 없음
// post/users 요청 액션은 Saga에서 수행
const leader = handleActions(
{
[GET_POST_SUCCESS]: (state, action) => ({
...state,
post: action.payload,
}),
[GET_USERS_SUCCESS]: (state, action) => ({
...state,
users: action.payload,
}),
},
initialState
);
export default leader;
//** Saga **//
// GET_POST/GET_USERS 액션에 대한 서버요청을 위한 Saga
export function* leaderSaga() {
yield takeLatest(GET_POST, getPostSaga);
yield takeLatest(GET_USERS, getUsersSaga);
}
// GET_POST 액션 dispatch 시 실행될 함수. 비동기로 서버의 post 데이터 요청
function* getPostSaga(action) {
try {
const post = yield call(api.getPost, action.payload);
// post 데이터요청하는 비동기함수인 getPost을 요청하며, 인자로 action.payload인 id를 넘김
yield put({
type: GET_POST_SUCCESS, // 요청 성공 액션을 dispatch함.
payload: post.data, // 액션 dispatch할 때 payload로 post.data(포스트 내용)을 전달함
});
} catch (e) {
yield put({
type: GET_POST_FAILURE, // 요청 실패 액션을 dispatch함
payload: e, // 액션 dispatch할 때 payload로 에러를 전달함
error: true, // 액션 객체의 항목에 error: true를 넘겨줌
});
}
}
// GET_USERS 액션 dispatch 시 실행될 함수. 비동기로 서버의 users 데이터 요청
function* getUsersSaga() {
try {
const users = yield call(api.getUsers);
// users 데이터를 요청하는 비동기함수인 getUsers를 요청하며, 전달인자는 없음.
yield put({
type: GET_USERS_SUCCESS,
payload: users.data,
});
} catch (e) {
yield put({
type: GET_USERS_FAILURE,
payload: e,
error: true,
});
}
}
(3) src/modules/index.js
reducer, saga 를 통합해줍니다.
import { combineReducers } from "redux";
import leader, { leaderSaga } from "./leader";
import { all } from "redux-saga/effects";
const rootReducer = combineReducers({
leader,
});
export default rootReducer;
export function* rootSaga() {
yield all([leaderSaga()]);
}
(4) src/index.js (스토어 생성)
Saga를 불러오고, Saga를 포함한 미들웨어를 적용하여 스토어를 생성하고, Saga를 실행합니다.
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import createSagaMiddleware from "@redux-saga/core";
import { applyMiddleware, createStore } from "redux";
import rootReducer, { rootSaga } from "./modules";
import { composeWithDevTools } from "redux-devtools-extension";
import { Provider } from "react-redux";
const sagaMiddleware = createSagaMiddleware();
const store = createStore(
rootReducer,
composeWithDevTools(applyMiddleware(sagaMiddleware))
);
sagaMiddleware.run(rootSaga);
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById("root")
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
(5) src/containers/LeaderContainer.jsx
이제 데이터를 받아올 통로는 만들어졌으므로, 데이터를 실제 요청하고, 받은 데이터는 하위로 보내주는 데이터 담당 Container를 작성합니다.
import React from "react";
import { connect } from "react-redux";
import Leader from "../components/Leader";
import { getPost, getUsers } from "../modules/leader";
import { useEffect } from "react";
const LeaderContainer = ({ getPost, getUsers, post, users }) => {
useEffect(() => {
getPost(1); // 액션 생성함수 실행('leader/GET_POST' 액션타입, payload: id=1)
getUsers(); // 액션 생성함수 실행
}, [getPost, getUsers]); // getPost ,getUsers에 변경이 있을 시 마다 호출
return <Leader post={post} users={users} />;
};
export default connect(
({ leader }) => ({
// props로 전달해 줄 post/users에, state의 leader에서 각각의 값 추출하여 전달
post: leader.post,
users: leader.users,
}),
{
getPost, // props로, dispatch 함수 중 Post요청 액션생성함수 전달
getUsers, // props로, dispatch 함수 중 Users요청 액션생성함수 전달
}
)(LeaderContainer);
(6) src/components/Leader.jsx
최종적으로 데이터를 받아서 표현할 컴포넌트를 구현합니다.
import React from "react";
const Leader = ({ post, users }) => {
return (
<div>
<section>
<h1>포스트</h1>
{post && (
<div>
<h3>{post.title}</h3>
<h3>{post.body}</h3>
</div>
)}
</section>
<hr />
<section>
<h1>사용자 목록</h1>
{users && (
<ul>
{users.map((user) => {
return (
<li key={user.id}>
{user.username} ({user.email})
</li>
);
})}
</ul>
)}
</section>
</div>
);
};
export default Leader;
주의할 점은 {post && ...} 또는 {users && ...} 가 없을 경우 최초 로딩 시 아직 Data 전달이 안된 시점에는 에러를 띄우므로 데이터가 있을 경우에만 출력하도록 해야합니다.
(7) src/App.js
앱에서 불러와 출력합니다.
import "./App.css";
import LeaderContainer from "./containers/LeaderContainer";
function App() {
return (
<div className="App">
<LeaderContainer />
</div>
);
}
export default App;
<결과>
~끝~
'Programming > React' 카테고리의 다른 글
SWR을 활용한 비동기 요청 예제 (0) | 2021.06.29 |
---|---|
React-query 를 활용한 비동기 요청 예제 (0) | 2021.06.29 |
SWR로 로컬 상태 컨트롤 - Counter (0) | 2021.06.26 |
Next.js 시작하기4 - 설정파일 (0) | 2021.06.20 |
Next.js 시작하기3 - 스타일링 (0) | 2021.06.20 |