일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 집합_SET
- 객체 비교
- 추상메서드
- abstract
- 다형성
- 메소드오버로딩
- 한국건설관리시스템
- 참조형변수
- exception
- Java
- 오라클
- 예외미루기
- 어윈 사용법
- EnhancedFor
- 예외처리
- 대덕인재개발원
- 환경설정
- 컬렉션프레임워크
- cursor문
- oracle
- 사용자예외클래스생성
- 컬렉션 타입
- GRANT VIEW
- 정수형타입
- 자동차수리시스템
- NestedFor
- 제네릭
- 자바
- 생성자오버로드
- 인터페이스
- Today
- Total
거니의 velog
(15) 프론트엔드 프로젝트 : 포스트 조회 기능 구현하기 2 본문
2. 포스트 목록 페이지 구현하기
* 이번에는 여러 개의 포스트를 보여 주는 포스트 목록 페이지를 구현해 본다.
(1) PostList UI 준비하기
* PostList라는 컴포넌트를 만든다. 이 컴포넌트에서는 포스트들을 배열로 받아 와서 렌더링해 준다. 사용자가 로그인 중이라면 페이지 상단 우측에 새 글 작성하기 버튼을 보여 준다.
* components/posts 디렉터리를 만들고, 그 안에 PostList 컴포넌트를 다음과 같이 작성해 주자.
[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';
const PostListBlock = styled(Responsive)`
margin-top: 3rem;
`;
const WritePostButtonWrapper = styled.div`
display: flex;
justify-content: flex-end;
margin-bottom: 3rem;
`;
const PostItemBlock = styled.div`
padding-top: 3rem;
padding-bottom: 3rem;
/* 맨 위 포스트는 padding-top 없음 */
&:first-child {
padding-top: 0;
}
& + & {
border-top: 1px solid ${palette.gray[2]};
}
h2 {
font-size: 2rem;
margin-bottom: 0;
margin-top: 0;
&:hover {
color: ${palette.gray[6]};
}
}
p {
margin-top: 2rem;
}
`;
const SubInfo = styled.div`
/* margin-top: 1rem; */
color: ${palette.gray[6]};
/* span 사이에 가운뎃점 문자 보여 주기 */
span + span::before {
color: ${palette.gray[4]};
padding-left: 0.25rem;
padding-right: 0.25rem;
content: '\\B7'; /* 가운뎃점 문자 */
}
`;
const Tags = styled.div`
margin-top: 0.5rem;
.tag {
display: inline-block;
color: ${palette.cyan[7]};
text-decoration: none;
margin-right: 0.5rem;
&:hover {
color: ${palette.cyan[6]};
}
}
`;
const PostItem = () => {
return (
<PostItemBlock>
<h2>제목</h2>
<SubInfo>
<span>
<b>username</b>
</span>
<span>{new Date().toLocaleDateString()}</span>
</SubInfo>
<Tags>
<div className="tag">#태그1</div>
<div className="tag">#태그2</div>
</Tags>
<p>포스트 내용의 일부분...</p>
</PostItemBlock>
);
};
const PostList = () => {
return (
<PostListBlock>
<WritePostButtonWrapper>
<Button cyan to="/write">
새 글 작성하기
</Button>
</WritePostButtonWrapper>
<div>
<PostItem />
<PostItem />
<PostItem />
</div>
</PostListBlock>
);
};
export default PostList;
* 이 컴포넌트에 사용된 SubInfo 컴포넌트와 Tags 컴포넌트는 PostViewer 에서 사용한 코드와 같다. 한 가지 차이점이라면, SubInfo 컴포넌트의 경우 margin-top이 없다는 것이다.
* 이렇게 똑같은 코드를 두 번 선언하는 대신, SubInfo 컴포넌트와 Tags 컴포넌트를 common 디렉터리에 따로 분리시켜서 재사용해 보자. 그리고 분리시킬 때 계정명이 나타나는 부분과 각 태그가 나타나는 부분에 Link를 사용하여 클릭 시 이동할 주소를 설정해 줄 것이다.
* 먼저 SubInfo 를 분리시킨다.
[components/common/SubInfo.js]
import React from 'react';
import styled, { css } from 'styled-components';
import { Link } from 'react-router-dom';
import palette from '../../lib/styles/palette';
const SubInfoBlock = styled.div`
${(props) =>
props.hasMarginTop &&
css`
margin-top: 1rem;
`}
color: ${palette.gray[6]};
/* span 사이에 가운뎃점 문자 보여주기*/
span + span:before {
color: ${palette.gray[4]};
padding-left: 0.25rem;
padding-right: 0.25rem;
content: '\\B7'; /* 가운뎃점 문자 */
}
`;
const SubInfo = ({ username, publishedDate, hasMarginTop }) => {
return (
<SubInfoBlock hasMarginTop={hasMarginTop}>
<span>
<b>
<Link to={`/@${username}`}>{username}</Link>
</b>
</span>
<span>{new Date(publishedDate).toLocaleDateString()}</span>
</SubInfoBlock>
);
};
export default SubInfo;
* SubInfo 컴포넌트는 hasMarginTop 값이 true 이면 상단 여백을 주고, 그렇지 않으면 여백이 없다. 그리고 username과 publishedDate를 props로 받아 와서 보여 주도록 설정했다.
* 다음으로 Tags 컴포넌트를 만든다.
[components/comons/Tags.js]
import React from 'react';
import styled from 'styled-components';
import palette from '../../lib/styles/palette';
import { Link } from 'react-router-dom';
const TagsBlock = styled.div`
margin-top: 0.5rem;
.tag {
display: inline-block;
color: ${palette.cyan[7]};
text-decoration: none;
margin-right: 0.5rem;
&:hover {
color: ${palette.cyan[6]};
}
}
`;
const Tags = ({ tags }) => {
return (
<TagsBlock>
{tags.map((tag) => (
<Link className="tag" to={`/?tag=${tag}`} key={tag}>
#{tag}
</Link>
))}
</TagsBlock>
);
};
export default Tags;
* Tags 컴포넌트에서는 tags 값을 props로 받아 와서 태그 목록을 렌더링해 준다. 각 태그 항목을 Link 컴포넌트로 작성했으며, 클릭했을 때 이동 경로는 /?tag=태그 로 설정했다.
* SubInfo 컴포넌트와 Tags 컴포넌트를 다 만들었으면, PostList에서 기존 SubInfo와 Tags를 지우고 이번에 새로 만든 컴포넌트를 불러와서 사용해 보자.
[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';
const PostListBlock = styled(Responsive)`
margin-top: 3rem;
`;
const WritePostButtonWrapper = styled.div`
display: flex;
justify-content: flex-end;
margin-bottom: 3rem;
`;
const PostItemBlock = styled.div`
padding-top: 3rem;
padding-bottom: 3rem;
/* 맨 위 포스트는 padding-top 없음 */
&:first-child {
padding-top: 0;
}
& + & {
border-top: 1px solid ${palette.gray[2]};
}
h2 {
font-size: 2rem;
margin-bottom: 0;
margin-top: 0;
&:hover {
color: ${palette.gray[6]};
}
}
p {
margin-top: 2rem;
}
`;
const PostItem = () => {
return (
<PostItemBlock>
<h2>제목</h2>
<SubInfo username="username" publishedDate={new Date()} />
<Tags tags={['태그1', '태그2', '태그3']} />
<p>포스트 내용의 일부분...</p>
</PostItemBlock>
);
};
const PostList = () => {
return (
<PostListBlock>
<WritePostButtonWrapper>
<Button cyan to="/write">
새 글 작성하기
</Button>
</WritePostButtonWrapper>
<div>
<PostItem />
<PostItem />
<PostItem />
</div>
</PostListBlock>
);
};
export default PostList;
* 컴포넌트를 잘 수정한 뒤, PostListPage 컴포넌트에서 PostList 컴포넌트를 렌더링하여 작성한 컴포넌트가 잘 나타나는지 확인해 보자.
[pages/PostListPage.js]
import React from 'react';
import HeaderContainer from '../containers/common/HeaderContainer';
import PostList from '../components/posts/PostList';
const PostListPage = () => {
return (
<div>
<HeaderContainer />
<PostList />
</div>
);
};
export default PostListPage;
- http://localhost:3000/
* PostList 컴포넌트가 잘 나타나는가?
* 이번에는 PostItem 컴포넌트를 만들 때 SubInfo와 Tags 컴포넌트를 재사용할 수 있도록 분리했었는데, 이 컴포넌트들을 이전에 만든 PostVIewer 에서도 재사용해 볼 것이다.
[components/post/PostViewer.js]
import React from 'react';
import styled from 'styled-components';
import palette from '../../lib/styles/palette';
import Responsive from '../common/Responsive';
import SubInfo from '../common/SubInfo';
import Tags from '../common/Tags';
const PostViewerBlock = styled(Responsive)`
margin-top: 4rem;
`;
const PostHead = styled.div`
border-bottom: 1px solid ${palette.gray[2]};
padding-bottom: 3rem;
margin-bottom: 3rem;
h1 {
font-size: 3rem;
line-height: 1.5;
margin: 0;
}
`;
const PostContent = styled.div`
font-size: 1.3125rem;
color: ${palette.gray[8]};
`;
const PostViewer = ({ post, error, loading }) => {
// 에러 발생 시
if (error) {
if (error.response && error.response.status === 404) {
return <PostViewerBlock>존재하지 않는 포스트입니다.</PostViewerBlock>;
}
return <PostViewerBlock>오류 발생!</PostViewerBlock>;
}
// 로딩중이거나, 아직 포스트 데이터가 없을 시
if (loading || !post) {
return null;
}
const { title, body, user, publishedDate, tags } = post;
return (
<PostViewerBlock>
<PostHead>
<h1>{title}</h1>
<SubInfo
username={user.username}
publishedDate={publishedDate}
hasMarginTop
/>
<Tags tags={tags} />
</PostHead>
<PostContent dangerouslySetInnerHTML={{ __html: body }} />
</PostViewerBlock>
);
};
export default PostViewer;
* 여러 곳에서 재사용할 수 있는 컴포넌트는 이렇게 따로 분리하여 사용하면, 코드의 양도 줄일 수 있을 뿐 아니라 유지 보수성도 높일 수 있어서 좋다.
'React_프론트엔드 프로젝트' 카테고리의 다른 글
(17) 프론트엔드 프로젝트 : 포스트 조회 기능 구현하기 4 (0) | 2024.02.23 |
---|---|
(16) 프론트엔드 프로젝트 : 포스트 조회 기능 구현하기 3 (0) | 2024.02.23 |
(14) 프론트엔드 프로젝트 : 포스트 조회 기능 구현하기 1 (0) | 2024.02.23 |
(13) 프론트엔드 프로젝트 : 글쓰기 기능 구현하기 4 (0) | 2024.02.23 |
(12) 프론트엔드 프로젝트 : 글쓰기 기능 구현하기 3 (0) | 2024.02.23 |