일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- 매일메일
- axios
- Algorithm
- java
- useDispatch
- react-redux
- react-router
- 자바
- maeil-mail
- 항해99
- redux-saga
- 알고리즘
- createSlice
- json-server
- 프로그래머스
- Get
- redux-toolkit
- 이코테
- sw expert academy
- 코딩테스트합격자되기
- 항해플러스
- C++
- Python
- redux
- 리액트
- programmers
- 테코테코
- JavaScript
- SW
- react
- Today
- Total
Binary Journey
[React][CRUD] 게시판 만들기 All in One (4). 게시판 따라 다른 게시글 목록 불러오기, redux, redux-saga, redux-toolkit, react, axios 본문
[React][CRUD] 게시판 만들기 All in One (4). 게시판 따라 다른 게시글 목록 불러오기, redux, redux-saga, redux-toolkit, react, axios
binaryJournalist 2021. 4. 14. 14:53
목차돌아가기:
binaryjourney.tistory.com/pages/ReactCRUD-create-board-tutorial-v2
[React][CRUD] create-board-tutorial-v2
* 인용시 출처 부탁드립니다. 완성 소스 code: github.com/cruellaDev/react-create-board-v2 cruellaDev/react-create-board-v2 updated version of react-create-board. Contribute to cruellaDev/react-create-..
binaryjourney.tistory.com
조회 방식은 지난번 게시판 board 불러오는 것과 거의 비슷하다. 그러나 이번에는 articles 내 데이터 중 원하는 boardId 를 가진 것들만 불러올 것이다.
게시글 목록 불러오는 것과 한 게시글의 내용을 불러오는 것은 같은 slice와 saga를 쓸 것이다. (articleSlice, articleSaga)
Routes에서 정해놓은 바에 따라 게시판 중 "일기" 와 "할일" 을 클릭하면 ArticleList 컴포넌트가 렌더링될 것이다.
// Routes.js
const ArticleList = lazy(() => import('../views/ArticleList'));
<Route path={"/board/:boardId"} exact component={ArticleList} />
// Board.js
<Link to={{ pathname: `/board/${board?.id}` }}>
나는 url에서 게시판 id parameter 만 받아와서 조회하면 된다.
쓸 훅은 useParams, useSelector, useEffect가 다일 듯하다.
껍데기만 만들어놓은 articleSlice에 initialState 와 reducers 내용을 만들어주자.
// artilceSlice.js
import { createSlice } from "@reduxjs/toolkit";
const name = "article";
const initialState = {
articleList: [],
status: 0,
statusText: "Loading",
};
const reducers = {
getArticleList: (state, action) => {},
getArticleListSuccess: (state, action) => {
state.articleList = action.payload?.data ?? [];
state.status = action.payload?.status;
state.statusText = action.payload?.statusText ?? "Success";
},
getArticleListFail: (state, action) => {
state.articleList = initialState.articleList;
state.status = action.payload?.status ?? 500;
state.statusText = action.payload?.statusText ?? "Network Error";
}
}
const articleSlice = createSlice({
name,
initialState,
reducers
});
export const articleReducer = articleSlice.reducer;
export const articleActions = articleSlice.actions;
이름만 바뀌었을 뿐이지 boardSlice.js 와 거의 똑같다.
articleSaga.js 도 마찬가지다.
너무 똑같아서 변화를 주기로 하였다. 큰 변화는 아니고 다른 메서드 사용을 시도해보겠다.
// articleSaga.js
import { all, call, retry, fork, put, take, select } from 'redux-saga/effects';
import { articleActions } from '../slices/articleSlice';
import axios from '../utils/axios';
import qs from "query-string";
const SECOND = 1000;
// api 서버 연결 주소
function apiGetArticle(articleId) {
return axios.get(`articles/${articleId}`);
}
function apiGetArticleList(requestParams) {
return axios.get(`articles?${qs.stringify(requestParams)}`);
}
// api 서버 연결 후 action 호출
function* asyncGetArticleList(action) {
try {
// const response = yield call(apiGetArticleList, { boardId: action.payload });
const response = yield retry(3, 10 * SECOND, apiGetArticleList, { boardId: action.payload });
if (response?.status === 200) {
yield put(articleActions.getArticleListSuccess(response));
} else {
yield put(articleActions.getArticleListFail(response));
}
} catch(e) {
yield put(articleActions.getArticleListFail(e.response));
}
}
// action 호출을 감시하는 watch 함수
function* watchGetArticleList() {
while(true) {
const action = yield take(articleActions.getArticleList);
yield call(asyncGetArticleList, action);
}
}
export default function* articleSaga()
{
yield all([fork(watchGetArticleList)]);
}
boardSaga 와 다른 점은 우선 query-string 이 사용됐다 (이건 위에서 말한 변화가 아니다.)
function apiGetArticleList(requestParams) {
return axios.get(`articles?${qs.stringify(requestParams)}`);
}
requestParam 은 { baordId: 숫자 } 이고
query-string은 이걸 url 의 query-string으로 바꾸어준다.
그래서 호출되는 url은
articles?boardId=숫자 가 된다.
변화를 준 부분은 이 부분이다.
const response = yield retry(3, 10 * SECOND, apiGetArticleList, { boardId: action.payload });
retry 메서드는 call 을 가지고 있다. 다만 이름 그대로 retry로 주어진 조건에 따라 call 을 계속 시도하는 것이다.
redux-saga.js.org/docs/api/#retrymaxtries-delay-fn-args
API Reference | Redux-Saga
API Reference
redux-saga.js.org
위 API Reference를 참고하여 따라해보았다.
yield retry(횟수, 시간(mills), 호출함수, 호출함수input);
형식은 이러하다.
첫번째 call이 실패했을 때 정해진 시간 간격마다 정해진 횟수 안에 연결이 성공할 때까지 call을 시도한다.
// rootSaga.js
import { map } from 'ramda';
import { all, fork } from "redux-saga/effects"
import articleSaga from "./sagas/articleSaga";
import boardSaga from "./sagas/boardSaga";
let combineSagas = {};
combineSagas = Object.assign(combineSagas, { articleSaga, boardSaga });
export default function* rootSaga() {
yield all(map(fork, combineSagas));
}
rootSaga 에 articleSaga를 넣어주고
이제 ArticlesList.js 에서는 훅만 제대로 import만 해주면 된다! 형태도 Board와 거의 비슷하다.
// ArticleList.js
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link, useParams } from 'react-router-dom';
import { articleActions } from '../slices/articleSlice';
function ArticleList() {
const params = useParams();
const { articleList, status, statusText } = useSelector((state) => state.articleReducer);
const boardList = useSelector((state) => state.boardReducer.boardList);
const dispatch = useDispatch();
useEffect(() => {
dispatch(articleActions.getArticleList(params?.boardId ?? 0));
}, [dispatch, params?.boardId]);
return (
<>
{
status === 200 ?
<>
<div>
<span>게시판: </span>
<span>
{
boardList.length > 0 &&
boardList.find((board) => board.id === parseInt(params?.boardId))?.name
}
</span>
</div>
{ articleList.length > 0 ?
<div>
<div>
{
articleList.map((article, index) =>
<div key={article?.id ?? index}>
<Link to={{ pathname: `/article/${article?.id ?? 0}` }}>
<span>{article?.title ?? ""}</span>
</Link>
</div>
)
}
</div>
</div>
:
<div> 게시글이 없습니다. </div>
}
</>
:
<div>
<div>
<span>{status}</span>
</div>
<div>
<span>{statusText}</span>
</div>
</div>
}
</>
);
}
export default ArticleList;
useParams 는 url 중 parameter로 보낸 것만 받아주는 훅이다.
url에서 /숫자? 숫자를 보통 parameter(param)로 칭하고 ?key=value&key=value 이걸 query로 칭하는데 useParam 은 딱 param까지만 가져와준다.
다른 정보까지 얻고 싶다면 react-router 들의 훅 useRouterMatch, useLocation 이 있는데
const match = useRouterMatch();
const location = useLocation();
을 작성한 뒤 console에 값이 어떻게 나오는지 확인하는 것도 추천한다.
react-router 는 알면 알수록 재밌다.
화면이 제대로 나온다면 이렇게 나올 것이다.
목차돌아가기:
binaryjourney.tistory.com/pages/ReactCRUD-create-board-tutorial-v2
[React][CRUD] create-board-tutorial-v2
* 인용시 출처 부탁드립니다. 완성 소스 code: github.com/cruellaDev/react-create-board-v2 cruellaDev/react-create-board-v2 updated version of react-create-board. Contribute to cruellaDev/react-create-..
binaryjourney.tistory.com