1-3. PostList 페이지 UI 구현
이번에는 PostList 페이지에서 필요한 유저인터페이스를 구현하겠습니다. 이 페이지에서는 포스트 목록에 관련된 컴포넌트들이 사용됩니다. 이전에 우리가 공용되는 컴포넌트들은 common 디렉토리에 넣었던 것 처럼, PostList 에서 보여지는 컴포넌트들은 list 라는 디렉토리에 만들어주겠습니다.
components 디렉토리에 list 디렉토리를 만들고, 내부에 다음 컴포넌트들을 만드세요:
- ListWrapper: 페이지 내부의 컴포넌트들을 감싸줍니다
- Pagination: 다음 / 이전 페이지로 이동을 해줍니다
- PostList: 포스트의 목록을 보여줍니다
ListWrapper 컴포넌트
ListWrapper 컴포넌트는 내용을 페이지의 중앙에 정렬시켜주고, 위 아래에 패딩이 설정되어있으며, 브라우저의 크기에 따라 사이즈가 조정됩니다. 컴포넌트를 감싸는 역할을 하므로, 내부에 children 을 렌더링하세요.
src/components/list/ListWrapper/ListWrapper.js
import React from 'react';
import styles from './ListWrapper.scss';
import classNames from 'classnames/bind';
const cx = classNames.bind(styles);
const ListWrapper = ({children}) => (
<div className={cx('list-wrapper')}>
{children}
</div>
);
export default ListWrapper;
src/components/list/ListWrapper/ListWrapper.scss
@import 'utils';
.list-wrapper {
width: 1024px;
margin: 0 auto;
padding-top: 3rem;
padding-bottom: 3rem;
padding-left: 1rem;
padding-right: 1rem;
@include media("<wide") {
width: 768px;
}
@include media("<large") {
width: 512px;
}
@include media("<medium") {
width: 100%;
}
}
그 다음엔, 이 컴포넌트를 ListPage 에 렌더링 하세요.
src/pages/ListPage.js
import React from 'react';
import PageTemplate from 'components/common/PageTemplate';
import ListWrapper from 'components/list/ListWrapper';
const ListPage = () => {
return (
<PageTemplate>
<ListWrapper>
리스트
</ListWrapper>
</PageTemplate>
);
};
export default ListPage;

PostList 컴포넌트

이번에는 PostList 컴포넌트를 만들어보겠습니다. 이 컴포넌트는, 블로그 포스트 목록 데이터를 받아온 후, 이를 렌더링 해줍니다.
이 과정에서, 우리는 PostItem 이라는 컴포넌트를 PostList 컴포넌트 내부에 만들어주고, 이를 반복적으로 렌더링해주겠습니다.
지금은, UI 만 만드는 과정이기 때문에, 더미 데이터를 입력하여 보여주세요.
src/components/list/PostList/PostList.js
import React from 'react';
import styles from './PostList.scss';
import classNames from 'classnames/bind';
import { Link } from 'react-router-dom';
const cx = classNames.bind(styles);
const PostItem = () => {
return (
<div className={cx('post-item')}>
<h2><a>타이틀</a></h2>
<div className={cx('date')}>2017-10-24</div>
<p>내용</p>
<div className={cx('tags')}>
<a>#태그</a>
<a>#태그</a>
<a>#태그</a>
</div>
</div>
)
}
const PostList = () => (
<div className={cx('post-list')}>
<PostItem/>
<PostItem/>
<PostItem/>
<PostItem/>
</div>
);
export default PostList;
스타일링도 해볼까요?
src/components/list/PostList/PostList.scss
@import 'utils';
.post-list {
.post-item {
padding: 1.5rem;
transition: all .15s ease-in;
h2 {
font-size: 2rem;
font-weight: 400;
margin: 0;
color: $oc-gray-8;
a {
transition: all .15s ease-in; // 스타일 바뀔 때 애니메이션 효과
border-bottom: 1px solid transparent;
}
a:hover {
color: $oc-blue-6;
// 마우스 호버시 밑줄 (밑줄과 글자 사이 여백, 얇은 밑줄을 위해 border-bottom 사용)
border-bottom: 1px solid $oc-blue-6;
}
}
.date {
font-size: 0.85rem;
color: $oc-gray-5;
}
p {
font-weight: 300;
color: $oc-gray-7;
}
.tags {
font-size: 0.85rem;
color: $oc-blue-6;
a {
&:hover {
color: $oc-blue-5;
text-decoration: underline;
}
}
a + a { // 태그 사이 여백
margin-left: 0.25rem;
}
}
&:hover {
// 호버시 배경색 변경
background: rgba($oc-blue-6, 0.05);
}
}
.post-item + .post-item { // 아이템 사이 여백
border-top: 1px solid $oc-gray-3;
}
}
이제 이 컴포넌트를 ListPage 에 불러와서 ListWrapper 내부에 렌더링하세요.
src/pages/ListPage.js
import React from 'react';
import PageTemplate from 'components/common/PageTemplate';
import ListWrapper from 'components/list/ListWrapper';
import PostList from 'components/list/PostList';
const ListPage = () => {
return (
<PageTemplate>
<ListWrapper>
<PostList/>
</ListWrapper>
</PageTemplate>
);
};
export default ListPage;
아까 봤던 PostList 미리보기와 동일하게 나타났나요?
Pagination 컴포넌트
이번엔, ListPage 가 지니고있는 마지막 컴포넌트인 Pagination 을 만들어보겠습니다.

이 컴포넌트에는 양쪽에 두개의 버튼이 있고, 중간에는 페이지가 있습니다. 첫번째 페이지를 보고있을 때는, 왼쪽의 버튼이 비활성화 되며, 마지막 페이지를 보고있을 때는 오른쪽 버튼이 비활성화됩니다.
src/components/list/Pagination/Pagination.js
import React from 'react';
import styles from './Pagination.scss';
import classNames from 'classnames/bind';
import Button from 'components/common/Button';
const cx = classNames.bind(styles);
const Pagination = () => (
<div className={cx('pagination')}>
<Button disabled>
이전 페이지
</Button>
<div className={cx('number')}>
페이지 1
</div>
<Button>
다음 페이지
</Button>
</div>
);
export default Pagination;
우리가 이전에, 버튼의 theme 값을 지정해주지 않으면, 파란색으로 나타나게 했고, disabled 값이 true 가 되면 버튼을 비활성화시켜서 회색으로 나타나게 했습니다.
첫번째 버튼에 disabled 를 넣으세요. JSX 에서 따로 값을 설정하지 않고 props 명만 넣어주면 자동으로 disabled={true} 로 설정 됩니다.
그 다음엔 이 컴포넌트를 스타일링해주겠씁니다. Flex 를 사용하여 엘리먼트들을 세로 중앙 정렬을 하고, 페이지가 나타나는 부분에 flex:1 을 설정하여 양옆 버튼 사이즈를 제외한 사이즈를 꽉 채워서 버튼이 페이지의 양 끝에 보여지게 하겠습니다.
src/list/Pagination/Pagination.scss
@import 'utils';
.pagination {
margin-top: 2rem;
// 세로 중앙 정렬
display: flex;
align-items: center;
.number {
font-size: 0.85rem;
text-align: center;
color: $oc-gray-6;
flex: 1; // 남은 공간을 다 차지
}
}
이제 이 컴포넌트를 ListPage 에서 PostList 하단에 렌더링하겠습니다.
src/pages/ListPage.js
import React from 'react';
import PageTemplate from 'components/common/PageTemplate';
import ListWrapper from 'components/list/ListWrapper';
import PostList from 'components/list/PostList';
import Pagination from 'components/list/Pagination';
const ListPage = () => {
return (
<PageTemplate>
<ListWrapper>
<PostList/>
<Pagination/>
</ListWrapper>
</PageTemplate>
);
};
export default ListPage;
ListPage 를 저장하고, 컴포넌트들이 모두 제대로 나타나는지 확인하세요.

이제 List 페이지에서 필요한 컴포넌트들의 모양새를 갖추었습니다. 이어서, Post 페이지와 Editor 페이지에서 사용하는 컴포넌트들의 유저인터페이스를 만들고 그 다음에 실제 기능들을 붙여봅시다.