2023.02.07 미니블로그 UI,LIST 컴포넌트 구현하기
지금부터 구성한 컴포넌트를 구현하기 전에 웹사이트에 필수적으로 필요한 UI컴포넌트들을 구현해 보도록 하겠습니다.
UI 컴포넌트는 버튼,텍스트 입력 등 사용자가 직접 입력을 할 수 있게 해주는 컴포넌트를 의미합니다.
프로젝트를 설계할 떄에는 Top-down 방식으로
큰 그림을 먼저 그리고 이후에 작은 부분을 구체화시켜 나갔습니다.
하지만 프로젝트를 구혈할 때에는 반대로 Bottom-up방식으로
작은 부분부터 구현한 후에 작은 부분들을 모아 큰 부분을 완성하게 됩니다.
먼저 Button 컴포넌트를 구현해 봅시다.
버튼은 그냥 HTML에서 제공하는 button 태그를 사용해도 되지만,
버튼의 스타일을 변경하고 버튼에 들어갈 텐스트도 props로 받아서 좀 더 쉽게 사용할 수 있게 하기 위해서 입니다.
src/component/ui 폴더에 Button.jsx 파일 생성
import React from "react";
import styled from "styled-components";
const StyledButton = styled.button`
padding: 8px 16px;
font-size: 16px;
border-width: 1px;
border-radius: 8px;
cursor: pointer;
`;
function Button(props) {
const { title, onClick } = props;
return <StyledButton onClick={onClick}>{title || "button"}</StyledButton>;
}
export default Button;
먼저 styled-components를 사용해서 button태그에 스타일을 입힌 StyledButton이라는 컴포넌트를 만들었습니다.
또한 Button 컴포넌트에서 props로 받은 title이 버튼에 표시되도록 해주었고,
props로 받은 onClick은 SytledButton의 onClick에 넣어줌으로써 클릭 이벤트를 상위 컴포넌트에서 받을 수 있도록 했습니다.
보통 입력을 받을 때 input 태그를 사용하지만 여기에서는 여러 줄에 걸친 텍스트를 입력받아야 하기 때문에 textarea 태그를 사용합니다.
import React from "react";
import styled from "styled-components";
const StyledTextarea = styled.textarea`
width: calc(100% - 32px);
${(props) =>
props.height &&
`
height: ${props.height}px;
`}
padding: 16px;
font-size: 16px;
line-height: 20px;
`;
function TextInput(props) {
const { height, vlaue, onChange } = props;
return < StyledTextarea height={height} value={value} onChange={onChange} />;
}
export default TextInput;
Button컴포넌트와 동일하게 styled-components를 사용했습니다.
그리고 TextInput 컴포넌트의 props로는 높이 설정을 위한 height,
입력된 값을 표시하기 위한 value,
변경된 값을 상위 컴포넌트로 전달하기 위한 onChange가 있습니다.
지금부터 List 컴포넌트들을 하나씩 구현해 보겠습니다.
가장 먼저 구현할 List 컴포넌트는 PostListItem 컴포넌트 입니다.
PostList가 아닌 PostListItem을 먼저 구현하는 이유는 PostList 컴포넌트에서
PostListItem을 필요로 하기 때문입니다.
import React from "react";
import styled from "styled-components";
const Wrapper = styled.div`
width: calc(100% - 32px);
padding: 16px;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
border: 1px solid grey;
border-radius: 8px;
cursor: pointer;
background: white;
:hover {
background: lightgray;
}
`;
const TitleText = styled.p`
font-size: 20px;
font-weight: 500;
`;
function PostListItem(props) {
const { post, onClick } = props;
return (
<Wrapper onClick={onClick}>
<TitleText>{post.title}</TitleText>
</Wrapper>
);
}
export default PostListItem;
PostListItem 컴포넌트는 글의 제목만 표시에 주면 되기 때문에 굉장히 단순합니다.
그래서 위와 같이 TitelText를 이용해서 props로 받은 post객체에 들어있는 title 문자열을 표시해 줍니다.
앞에서 PostListItem 컴포넌트를 구현했기 때문에 이제 PostList 컴포넌트를 구현해 보겠습니다.
PostList 컴포넌트도 굉장히 단순하지만 map( ) 함수를 사용하여 글의 개수만큼 PostListItem 컴포넌트를 생성한다는 점이 다른 컴포넌트들과는 조금 다릅니다.
import React from "react";
import styled from "styled-components";
import PostListItem from "./PostListItem";
const Wrapper = styled.div`
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
& > * {
:not(:last-child) {
margin-bottom: 16px;
}
}
`;
function PostList(props) {
const { posts, onClickItem } = props;
return (
<Wrapper>
{posts.map((post, index) => {
return (
<PostListItem
key={post.id}
post={post}
onClick={() => {
onClickItem(post);
}}
/>
);
})}
</Wrapper>
);
}
export default PostList;
먼저 앞에서 만든 PostListItem 컴포넌트를 사용하기 위해 임포트해줍니다.
PostList컴포넌트의 props로 받은 posts라는 배열에는 post 객체들이 들어있습니다.
이 posts 배열의 map( ) 함수를 이용하여 각 post 객체에 대해 PostListItem 컴포넌트를 만들어서 렌더링하게 됩니다.
CommentListItem 컴포넌트는 스타일이 약간 다르다는 점을 제외하고는 PostListItem 컴포넌트와 거의 동일합니다.
import React from "react";
import styled from "styled-components";
const Wrapper = styled.div`
width: calc(100% - 32px);
padding: 8px 16px;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
border: 1px solid grey;
border-radius: 8px;
cursor: pointer;
background: white;
:hover {
background: lightgray;
}
`;
const ContentText = styled.p`
font-size: 16px;
white-space: pre-wrap;
`;
function CommentListItem(props) {
const { comment } = props;
return (
<Wrapper>
<ContentText>{comment.content}</ContentText>
</Wrapper>
);
}
export default CommentListItem;
CommentListItem 컴포넌트는 props에서 comment 객체 하나만 사용합니다.
comment 객체에는 사용자가 작성한 댓글 내용이 들어있습니다.
이를 styled-components를 통해 만든 ContentText라는 컴포넌트를 이용해서 화면에 표시하게 됩니다.
글은 클릭이 가능했지만 댓글은 별도로 클릭하는 기능이 없기 떄문에 onClick 이벤트를 따로 처리해 주지 않아도 됩니다.
CommenList 컴포넌트도 위에서 만든 PostList 컴포넌트와 거의 동일합니다.
반복적으로 렌더링하는 아이템으 CommentListItem 컴포넌트라는 점을 제외하고는 말이죠.
import React from "react";
import styled from "styled-components";
import CommentListItem from "./CommentListItem";
const Wrapper = styled.div`
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
& > * {
:not(:last-child) {
margin-bottom: 16px;
}
}
`;
function CommentList(props) {
const { comments } = props;
return (
<Wrapper>
{comments.map((comment, index) => {
return (
<CommentListItem
key={comment.id}
comment={comment}
/>
);
})}
</Wrapper>
);
}
export default CommentList;
CommentList 컴포넌트의 props로는 comments라는 배열이 들어오게 됩니다.
이 배열에는 comment 객체들이 들어있으며 이 배열의 map( )함수를 사용해서 각 댓글 객체를
CommentListItem 컴포넌트로 넘겨 화면에 댓글을 표시합니다.