목차 돌아가기: binaryjourney.tistory.com/pages/ReactCRUD-create-board-tutorial
9편에서는 게시글 한 개의 내용을 불러오는 것을 다뤘고 이번 편에서는 게시글 목록 전체 조회를 다룰 것이다.
이번 편도 꽤나 쉽다!
articleSlice 와는 다른 initialState를 쓸 것이기 때문에 slice를 새로 만들어줘야 한다.
그래서 /src/slice/ 에 boardSlice.js 파일을 만들어준다.
방식은 articleSlice 와 같다.
createSlice룰 import 하고 createSlice 함수를 받는 변수 boardSlice를 선언한다.
// /src/slice/boardSlice
import { createSlice } from "@reduxjs/toolkit";
export const boardSlice = createSlice({
});
그리고 createSlice 부분에 configure할 내용들을 적는다.
name, initialState, reducers 를 board 특성에 맞게 적으면 끝이다.
// boardSlice.js
export const boardSlice = createSlice({
name: "board",
initialState: {
board: [],
isLoading: true,
isSuccess: false,
error: null,
},
reducers: {
getBoard: (state, { payload }) => {
console.log("getBoard 액션 호출");
},
getBoardAsync: (state, { payload: data }) => {
return {
...state,
board: data,
isSuccess: true,
isLoading: false,
};
},
},
});
name 은 board 이기 때문에 board 이고
initialState는 board 배열 리스트를 가져오는 것이어서 state를 하나하나 지정할 필요가 없다.
그래서 조회된 내용이 잘 가져와졌는지 확인 정도만 하는 loading, success, error 와 조회 내용 부분인 board 만 지정해주겠다.
그리고 뷰(BoardPage)에서 dispatch 할 액션 getBoard를 기본적으로만 만들어주고
saga에서 put할 액션 getBoardAsync를 만들어주었다.
saga에서 put할 때 변경될 state는 데이터가 성공적으로 조회된 상태만 내보내므로 getBoardAsync의 success는 true, loading은 false로 정해주었다.
saga 도 또한 /src/sagas 에 boardSaga.js 파일을 만들어준다.
boardSaga 에서는 board 전체 내용을 가져오는 것밖에 없기 때문에 일단 아래 코드처럼만 만들어준다.
// /src/sagas/boardSaga.js
import { call, put } from "redux-saga/effects";
import Axios from "axios";
import { boardActions } from "../slice/boardSlice";
export function* getBoardAsync() {
const response = yield Axios.get(`http://localhost:4000/board/`);
yield put(boardActions.getBoardAsync(response.data));
}
위의 코드는 데이터가 성공적으로 전송됐을 때만 실행되는 것이다.
그 외의 경우는 reducer를 포함하여 나중에 만들기로 한다.
이제 만든 reducer 와 saga를 묶어주는 것이 남았다.
boardSlice 하단에 reducer와 action을 export 해줄 코드를 적는다.
// boardSlice.js
export const boardReducers = boardSlice.reducer;
export const boardActions = boardSlice.actions;
그리고 rootReducer가 있는 rootSlice에서 boardReducer 를 import 하여 combineReducer로 묶어준다.
// rootSlice.js
import { combineReducers } from "redux";
import { articleReducers } from "./articleSlice";
import { boardReducers } from "./boardSlice";
const rootReducer = combineReducers({ articleReducers, boardReducers });
export default rootReducer;
rootSaga에 가서는 감시할 액션 타입과 호출할 함수를 import 해주고 rootWatcher 안에 적는다.
// rootSaga
import { takeEvery, takeLatest } from "redux-saga/effects";
import { articleActions } from "../slice/articleSlice";
import { boardActions } from "../slice/boardSlice";
import { registerArticleAsync, getArticleAsync } from "./articleSaga";
import { getBoardAsync } from "./boardSaga";
const { registerArticle, getArticle } = articleActions;
const { getBoard } = boardActions;
export default function* rootWatcher() {
yield takeLatest(registerArticle.type, registerArticleAsync);
yield takeEvery(getArticle.type, getArticleAsync);
yield takeEvery(getBoard.type, getBoardAsync);
}
필요한 기능은 다 구현해놨으니 이제 뷰인 BoardPage에서 액션을 호출하면 끝이다.
BoardPage 에 useDispatch와 useEffect, useSelector를 import 한다.
// BoardPage
const dispatch = useDispatch();
useEffect(() => {
dispatch(boardActions.getBoard());
}, [dispatch]);
const board = useSelector((state) => state.boardReducers.board);
화면이 로딩될 때 dispatch를 해주므로 액션 dispatch 문은 useEffect 문 안에 적어줬다.
[] 안은 현재 화면 로딩 외에 만들어놓은 것이 없으므로 사실 비워놔도 된다
(나는 ESLint 노란 줄 생기는 게 싫어서 dispatch 넣어둠).
그리고 useSelector로는 리듀서에서 변경된 state.boardReducers.board를 잡아온다.
프레젠테이셔널 컴포넌트로 BoardList를 만들어놓았으므로 가져온 board를 BoardList의 프로퍼티로 넣어준다.
// BoardPage
<div>
<BoardList board={board} />
</div>
BoardPage 의 전체 코드는 다음과 같다.
// BoardPage.js
import React, { useEffect } from "react";
import { Link } from "react-router-dom";
import BoardList from "./Sections/BoardList";
import { Button, Typography } from "antd";
import { useDispatch, useSelector } from "react-redux";
import { boardActions } from "../../../slice/boardSlice";
const { Title } = Typography;
function BoardPage() {
const dispatch = useDispatch();
useEffect(() => {
dispatch(boardActions.getBoard());
}, [dispatch]);
const board = useSelector((state) => state.boardReducers.board);
return (
<div style={{ maxWidth: "700px", margin: "2rem auto" }}>
<div>
<Title>Board Title</Title>
</div>
<div>
<Link to="/register">
<Button type="primary">New Post</Button>
</Link>
</div>
<div>
<BoardList board={board} />
</div>
</div>
);
}
export default BoardPage;
BoardList 컴포넌트에서는 props로 board를 받아 map으로 안의 내용을 풀어준다.
테이블로 그나마 보기 좋게 만들어준다.
// BoardList.js
import React from "react";
import { Link } from "react-router-dom";
function BoardList(props) {
console.log(props.board);
return (
<div>
<table>
<colgroup>
<col width="10%" />
<col width="10%" />
<col width="40%" />
<col width="40%" />
</colgroup>
<tr>
<th>번호</th>
<th>제목</th>
<th>조회수</th>
</tr>
{props.board.map((article) => (
<tr>
<td>{article.id}</td>
<Link to={`/article/${article.id}`}>
<td>{article.title}</td>
</Link>
<td>{article.views}</td>
</tr>
))}
</table>
</div>
);
}
export default BoardList;
여기서 Link to 는 해당 글의 제목을 클릭하면 이동하도록 해놨다.
ArticlePage에서 파라미터로 articleId를 읽으므로 경로 마지막에 article.id 를 넣어준 것이다.
결과물을 보면 이것과 비슷하게 나올 것이다.
글을 클릭하면
이전 과정에서 ArticlePage까지 만들어놨기 때문에
해당 글 내용까지 슈루룩 들어간다.
목록으로 가는 버튼까지만 만들고 이번 편을 마무리하겠다.
ArticleDetail 컴포넌트에 필요한 라이브러리를 import 한다.
// ArticleDetail
import { Link } from "react-router-dom";
import { Button, Typography } from "antd";
const { Title } = Typography;
그리고 table 이 들어간 div 위에 코드를 추가한다
<div style={{ margin: "2rem auto" }}>
<Link to="/">
<Button type="primary">목록으로 가기</Button>
</Link>
</div>
<div style={{ textAlign: "center" }}>
<Title>게시글</Title>
</div>
ArticleDetail 의 전체 코드는 다음과 같다.
// ArticleDetail
import React from "react";
import { Link } from "react-router-dom";
import { Button, Typography } from "antd";
const { Title } = Typography;
function ArticleDetail(props) {
return (
<div style={{ width: "80%", margin: "3rem auto" }}>
<div style={{ margin: "2rem auto" }}>
<Link to="/">
<Button type="primary">목록으로 가기</Button>
</Link>
</div>
<div style={{ textAlign: "center" }}>
<Title>게시글</Title>
</div>
<div>
<table>
<colgroup>
<col width="10%" />
<col width="40%" />
<col width="10%" />
<col width="40%" />
</colgroup>
<tr>
<th>번호</th>
<td>{props.id}</td>
<th>조회수</th>
<td>{props.views}</td>
</tr>
<tr>
<th>제목</th>
<td colspan="3">{props.title}</td>
</tr>
<tr>
<th>내용</th>
<td colspan="3">{props.content}</td>
</tr>
</table>
</div>
</div>
);
}
export default ArticleDetail;
파일을 저장하면 화면이 이렇게 바뀌어 있을 것이다.
버튼과 목록을 클릭해보며 왔다 갔다 해보자
기본적인 틀은 다 만들어놓았다고 생각한다.
조회수 늘리기, 수정하기, 삭제, 댓글 등 세부적인 기능은 다음 편에서부터 다루겠다.
목차 돌아가기: binaryjourney.tistory.com/pages/ReactCRUD-create-board-tutorial