일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 29 | 30 |
- 추상메서드
- oracle
- GRANT VIEW
- 참조형변수
- Java
- 예외처리
- 한국건설관리시스템
- 다형성
- 인터페이스
- exception
- 컬렉션프레임워크
- 컬렉션 타입
- 예외미루기
- EnhancedFor
- 집합_SET
- 정수형타입
- 어윈 사용법
- 메소드오버로딩
- 생성자오버로드
- abstract
- 환경설정
- 사용자예외클래스생성
- 오라클
- cursor문
- 객체 비교
- 자동차수리시스템
- 제네릭
- NestedFor
- 자바
- 대덕인재개발원
- Today
- Total
거니의 velog
(16) 프론트엔드 프로젝트 : 포스트 조회 기능 구현하기 3 본문
(2) 포스트 목록 조회 API 연동하기
* postLIst 컴포넌트에서 실제 데이터를 보여 줄 수 있도록 API를 연동해 보자. 우리가 사용할 list API는 username, page, tag 값을 쿼리 값으로 넣어서 사용한다. API를 사용할 때 파라미터로 문자열들을 받아 와서 직접 조합해도 되지만, 여기서는 qs 라이브러리를 사용하여 쿼리 값을 생성할 것이다. 이 라이브러리를 사용하면 쿼리 값을 더 편리하게 생성하고 JSON으로 변환할 수 있다.
* yarn 으로 qs를 설치하자.
$ yarn add qs
* 그리고 lib/api/posts.js 파일에 다음 함수를 추가하자.
import qs from 'qs';
import client from './client';
export const writePost = ({ title, body, tags }) =>
client.post('/api/posts', { title, body, tags });
export const readPost = (id) => client.get(`/api/posts/${id}`);
export const listPosts = ({ page, username, tag }) => {
const queryString = qs.stringify({
page,
username,
tag,
});
return client.get(`/api/posts?${queryString}`);
};
* listPosts API를 호출할 때 파라미터로 값을 넣어 주면 /api/posts?username=tester&page=2와 같이 주소를 만들어서 호출한다.
* 이제 위 요청의 상태를 관리하는 리덕스 모듈을 만들어 보자. modules 디렉터리에 posts.js 파일을 만들어서 다음 코드를 작성하자.
[modules/posts.js]
import { createAction, handleActions } from 'redux-actions';
import createRequestSaga, {
createRequestActionTypes,
} from '../lib/createRequestSaga';
import * as postsAPI from '../lib/api/posts';
import { takeLatest } from 'redux-saga/effects';
const [LIST_POSTS, LIST_POSTS_SUCCESS, LIST_POSTS_FAILURE] =
createRequestActionTypes('posts/LIST_POSTS');
export const listPosts = createAction(
LIST_POSTS,
({ tag, username, page }) => ({ tag, username, page }),
);
const listPostsSaga = createRequestSaga(LIST_POSTS, postsAPI.listPosts);
export function* postsSaga() {
yield takeLatest(LIST_POSTS, listPostsSaga);
}
const initialState = {
posts: null,
error: null,
};
const posts = handleActions(
{
[LIST_POSTS_SUCCESS]: (state, { payload: posts }) => ({
...state,
posts,
}),
[LIST_POSTS_FAILURE]: (state, { payload: error }) => ({
...state,
error,
}),
},
initialState,
);
export default posts;
* 다 작성한 뒤에는 루트 리듀서와 루트 사가에 방금 만든 리듀서와 사가를 등록하자.
[modules/index.js]
import { combineReducers } from 'redux';
import { all } from 'redux-saga/effects';
import auth, { authSaga } from './auth';
import loading from './loading';
import user, { userSaga } from './user';
import write, { writeSaga } from './write';
import post, { postSaga } from './post';
import posts, { postsSaga } from './posts';
const rootReducer = combineReducers({
auth,
loading,
user,
write,
post,
posts,
});
export function* rootSaga() {
yield all([authSaga(), userSaga(), writeSaga(), postSaga(), postsSaga()]);
}
export default rootReducer;
* 다음으로 containers 디렉터리 안에 posts 디렉터리를 만들고, 그 안에 PostListContainer 컴포넌트를 만든다. 이 컴포넌트는 주소에 있는 쿼리 파라미터를 추출하여 우리가 만들었던 listPosts API를 호출해 준다.
[containers/posts/PostListContainer.js]
import React, { useEffect } from 'react';
import qs from 'qs';
import { withRouter } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import PostList from '../../components/posts/PostList';
import { listPosts } from '../../modules/posts';
const PostListContainer = ({ location, match }) => {
const dispatch = useDispatch();
const { posts, error, loading, user } = useSelector(
({ posts, loading, user }) => ({
posts: posts.posts,
error: posts.error,
loading: loading['posts/LIST_POSTS'],
user: user.user,
}),
);
useEffect(() => {
const { username } = match.params;
const { tag, page } = qs.parse(location.search, {
ignoreQueryPrefix: true,
});
dispatch(listPosts({ tag, username, page }));
}, [dispatch, location.search, match.params]);
return (
<PostList
loading={loading}
error={error}
posts={posts}
showWriteButton={user}
/>
);
};
export default withRouter(PostListContainer);
* PostList 컴포넌트를 사용할 때 showWriteButton props를 현재 로그인 중인 사용자의 정보를 지니고 있는 user 객체로 설정해 주었다. 이렇게 하면 user 객체가 유효할 때, 즉 사용자가 로그인 중일 때만 포스트를 작성하는 버튼이 나타난다.
* 컨테이너 컴포넌트를 완성한 후, PostListPage 컴포넌트에서 PostList를 PostListContainer 로 대체시키자.
[pages/PostListPage.js]
import React from 'react';
import HeaderContainer from '../containers/common/HeaderContainer';
import PostListContainer from '../containers/posts/PostListContainer';
const PostListPage = () => {
return (
<div>
<HeaderContainer />
<PostListContainer />
</div>
);
};
export default PostListPage;
* 그리고 PostList에서 받아온 props에 따라 결과물을 보여 주자.
[components/posts/PostList.js]
import React from 'react';
import styled from 'styled-components';
import Responsive from '../common/Responsive';
import Button from '../common/Button';
import palette from '../../lib/styles/palette';
import SubInfo from '../common/SubInfo';
import Tags from '../common/Tags';
import { Link } from 'react-router-dom';
(...)
const PostItem = ({ post }) => {
const { publishedDate, user, tags, title, body, _id } = post;
return (
<PostItemBlock>
<h2>
<Link to={`/@${user.username}/${_id}`}>{title}</Link>
</h2>
<SubInfo
username={user.username}
publishedDate={new Date(publishedDate)}
/>
<Tags tags={tags} />
<p>{body}</p>
</PostItemBlock>
);
};
const PostList = ({ posts, loading, error, showWriteButton }) => {
// 에러 발생 시
if (error) {
return <PostListBlock>에러가 발생했습니다.</PostListBlock>;
}
return (
<PostListBlock>
<WritePostButtonWrapper>
{showWriteButton && (
<Button cyan to="/write">
새 글 작성하기
</Button>
)}
</WritePostButtonWrapper>
{/* 로딩 중 아니고, 포스트 배열이 존재할 때만 보여줌 */}
{!loading && posts && (
<div>
{posts.map((post) => (
<PostItem post={post} key={post._id} />
))}
</div>
)}
</PostListBlock>
);
};
export default PostList;
* 여기까지 작성하면 페이지에 다음과 같은 결과가 나타날 것이다.
* 내용이 나타나는 부분에 HTML 태그가 그대로 보인다. 이 태그를 없애는 작업은 서버 쪽에서 해 주어야 한다. 물론 클라이언트에서 처리하는 방법도 있지만, 현재는 포스트 리스팅을 할 때 body의 글자수를 200자로 제한하는 기능이 있다. 이 때문에 완성된 HTML이 아니라 HTML의 일부분만 전달되어 HTML 태그를 없애는 작업이 잘 이루어지지 않을 가능성이 있다.
'React_프론트엔드 프로젝트' 카테고리의 다른 글
(18) 프론트엔드 프로젝트 : 포스트 조회 기능 구현하기 5 (0) | 2024.02.23 |
---|---|
(17) 프론트엔드 프로젝트 : 포스트 조회 기능 구현하기 4 (0) | 2024.02.23 |
(15) 프론트엔드 프로젝트 : 포스트 조회 기능 구현하기 2 (0) | 2024.02.23 |
(14) 프론트엔드 프로젝트 : 포스트 조회 기능 구현하기 1 (0) | 2024.02.23 |
(13) 프론트엔드 프로젝트 : 글쓰기 기능 구현하기 4 (0) | 2024.02.23 |