2023.02.07 미니블로그 Page 컴포넌트 구현하기
다음은 블로그에 보여줄 가짜 데이터를 준비해야 합니다.
책에서 만들어둔 데이터를 사용하면 됩니다.
가짜 데이터는 JSON(javascript object notation)형태로 되어 있는데
현재 서버와의 통신에서 가장 많이 사용하는 데이터 유형입니다.
[
{
"id": 1,
"title": "리액트에서 리스트 렌더링하기",
"content": "안녕하세요, 소플입니다.\n이번 글에서는 리액트에서 리스트를 렌더링하는 방법에 대해서 배워보겠습니다.\n리스트를 렌더링하기 위해서는 자바스크립트 배열에서 제공하는 map함수를 사용합니다.",
"comments": [
{
"id": 11,
"content": "실제로 개발하다보면 map함수를 진짜 많이 쓰는 것 같아요😄"
},
{
"id": 12,
"content": "적용해보니 코드가 정말 간결해지네요ㅎㅎ"
},
{
"id": 13,
"content": "key를 꼭 넣어줘야 하는군요~"
},
{
"id": 14,
"content": "생산성이 확 올라가는 느낌입니다ㅋㅋ"
},
{
"id": 15,
"content": "오늘도 좋은 글 감사합니다!👍"
}
]
},
{
"id": 2,
"title": "리액트의 조건부 렌더링이란?",
"content": "안녕하세요, 소플입니다.\n이번 글에서는 리액트의 조건부 렌더링에 대해서 배워보도록 하겠습니다.\n조건부 렌더링은 말 그대로 조건에 따라서 렌더링을 다르게 한다는 의미입니다.",
"comments": [
{
"id": 21,
"content": "이렇게 사용하는 방법이 있었군요!"
},
{
"id": 22,
"content": "좋은 글 감사합니다ㅎㅎ"
},
{
"id": 23,
"content": "항상 ?만 사용했었는데, 이제 &&도 사용해봐야 겠네요."
},
{
"id": 24,
"content": "쉬운 설명 감사드립니다😁"
},
{
"id": 25,
"content": "바로 코드에 적용해보겠습니다!!"
}
]
},
{
"id": 3,
"title": "리액트 Hook에 대해서 배워볼까요?",
"content": "안녕하세요, 소플입니다.\n이번 글에서는 리액트의 Hook에 대해서 배워보도록 하겠습니다.\nHook은 리액트의 함수 컴포넌트의 흐름에 끼어들어서 다양한 작업들을 처리하기 위해서 사용합니다.",
"comments": [
{
"id": 31,
"content": "뭔가 어려운 개념이었는데, 글을 읽고 조금 정리가 된 것 같습니다."
},
{
"id": 32,
"content": "Hook이 뭔가 했더니 이런거였군요. 알려주셔서 감사합니다ㅎㅎ"
},
{
"id": 33,
"content": "처음에 훅을 접했을 때 너무 어려웠는데 감사합니다!👍"
},
{
"id": 34,
"content": "앞으로는 잘 사용할 수 있을것 같아요"
},
{
"id": 35,
"content": "이름부터 너무 어려운 훅...🥲"
}
]
},
{
"id": 4,
"title": "리액트 컴포넌트 개념 소개",
"content": "이번 글에서는 리액트의 컴포넌트에 대해서 설명을 해보려고 합니다.\n리액트가 컴포넌트 기반이라는 것은 리액트를 조금만 공부해보신 분들도 다 알고 계실겁니다.\n그렇다면 컴포넌트는 도대체 뭘까요?",
"comments": [
{
"id": 41,
"content": "헷갈렸던 개념을 확실히 이해할 수 있어서 좋네요ㅋㅋ"
},
{
"id": 42,
"content": "컴포넌트에 대한 쉬운 설명 감사드려요👏"
},
{
"id": 43,
"content": "컴포넌트를 제대로 이해하지 않은 상태로 사용하기만 했는데 확실히 개념을 잡을 수 있어서 좋습니다!👍"
},
{
"id": 44,
"content": "리액트는 컴포넌트 기반이라서 재사용성도 높고 정말 좋은것 같아요"
},
{
"id": 45,
"content": "리액트 최고!!👍"
}
]
},
{
"id": 5,
"title": "처음 만난 리액트 강의 소개",
"content": "안녕하세요, 소플입니다.\n오늘은 제가 만든 리액트 강의를 소개해드리려고 합니다.\n강의 이름은 '처음 만난 리액트'입니다.\n강의 이름에서 이미 느끼셨을텐데, 리액트 초보자분들을 위한 강의입니다.",
"comments": [
{
"id": 51,
"content": "강의 너무 좋아요~!"
},
{
"id": 52,
"content": "초보자도 쉽게 이해할 수 있어서 좋습니다😃"
},
{
"id": 53,
"content": "실습도 따라하면서 하는데 좋아요"
},
{
"id": 54,
"content": "좋은 강의 감사드립니다👍👍"
},
{
"id": 55,
"content": "오 이런 강의가 있었군요~"
}
]
},
{
"id": 6,
"title": "안녕하세요 소플입니다.",
"content": "제 블로그에 오신 것을 환영합니다.\n앞으로 유익한 글들을 자주 올리도록 하겠습니다!",
"comments": [
{
"id": 61,
"content": "많이 올려주세요!👍"
},
{
"id": 62,
"content": "와 좋습니다ㅎㅎ"
},
{
"id": 63,
"content": "리액트 너무 어려워요ㅠㅠ😂"
},
{
"id": 64,
"content": "소플님 강의 잘 듣고 있습니다~!"
},
{
"id": 65,
"content": "꾸준히 블로그 활동 해주세요!!😀"
}
]
}
]
이제 UI, List 컴포넌트 그리고 데이터까지 모두 준비되었습니다.
최종적으로 이 컴포넌트들과 데이터를 모아서 Page 컴포넌트들을 구현해 보도록 하겠습니다.
MainPage 컴포넌트는 우리가 만들 미니 블로그에 사용자가 처음 접속했을 때 보게 될 페이지입니다.
MainPage 컴포넌트에서는 글을 작성할 수 있는 버튼과 글 목록을 보여주어야 합니다.
import React from "react";
import {useNavigate } from "react-router-dom";
import styled from "styled-components";
import PostList from "../list/PostLIst";
import Button from "../ui/Button";
import data from '../../data.json';
const Wrapper = styled.div`
padding: 16px;
width: calc(100% - 32px);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
`;
const Container = styled.div`
width: 100%;
max-width: 720px;
& > * {
:not(:last-child){
margin-bottom: 16px;
}
}
`;
function MainPage(props) {
const {} = props;
const navigate = useNavigate();
return(
<Wrapper>
<Container>
<Button
title="글 작성하기"
onClick={() => {
navigate("/post-write");
}}
/>
<PostList
posts={data}
onClickItem={(item) => {
navigate(`/post/${item.id}`);
}}
/>
</Container>
</Wrapper>
)
}
export default MainPage;
위의 코드를 보면 앞에서 만든 Button 컴포넌트를 이용해서 글 작성하기 페이지로 이동할 수 있게 구현하고 있으며,
앞에서 만들어둔 PostList 컴포넌트를 이용해서 글 목록을 표시하고 있습니다.
페이지 이동을 위해 rext-touter-dom의 userNavigate() 훅을 사용하였습니다.
MainPage 컴포넌트의 코드는 그냥 만들어둔 컴포넌트들을 모아놓은 수준으로 굉장히 단순한데 이것이 바로 컴포넌트 기반으로 개발하는 리액트의 장점이라고 할 수 있습니다.
이번에는 글 작성을 위한 페이지를 PostWritePage 컴포넌트로 구현해 보겠습니다.
import React,{useState} from "react";
import {useNavigate } from "react-router-dom";
import styled from "styled-components";
import PostList from "../list/PostLIst";
import Button from "../ui/Button";
import data from '../../data.json';
import TextInput from "../ui/TextInput";
const Wrapper = styled.div`
padding: 16px;
width: calc(100% - 32px);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
`;
const Container = styled.div`
width: 100%;
max-width: 720px;
& > * {
:not(:last-child){
margin-bottom: 16px;
}
}
`;
function PostWritePage(props) {
const navigate = useNavigate();
const {title, setTitle} = useState("");
const { content, setContent} = useState("");
return(
<Wrapper>
<Container>
<TextInput
height={20}
value={title}
onChange={(event) => {
setTitle(event.target.value);
}}
/>
<TextInput
height={480}
value={content}
onChange={(event) => {
setContent(event.target.value);
}}
/>
<Button
title="글 작성하기"
onClick={() => {
navigate("/");
}}
/>
</Container>
</Wrapper>
)
}
export default PostWritePage;
PostWritePage 컴포넌트는 두 개의 state를 갖고 있습니다.
하나는 글의 제목을 위한 state이고
다른 하나는 글의 내용을 위한 state입니다.
두 개의 state 모두 useState() 훅을 이용하여 선언한 것을 볼 수 있습니다.
그리고 실제 화면에 나타나는 부분은 TextInput 컴포넌트를 사용해서 글의 제목과 내용을
각각 입력받을 수 있도록 구성하였습니다.
마지막으로 화면 제일 하단에는 Button 컴포넌트를 사용해서 글작성 버튼을 넣었습니다.
이제 마지막 페이지 컴포넌트인 PostViewPage 컴포넌트를 구현해 보도록 하겠습니다.
PostViewPage 컴포넌트는 글을 볼 수 있게 해주는 컴포넌트이기 때문에
글과 댓글을 보여주어야 하며 댓글 작성 기능도 제공해야 합니다.
import React,{useState} from "react";
import {useNavigate, useParams } from "react-router-dom";
import styled from "styled-components";
import CommentList from "../list/CommentList";
import Button from "../ui/Button";
import data from '../../data.json';
import TextInput from "../ui/TextInput";
const Wrapper = styled.div`
padding: 16px;
width: calc(100% - 32px);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
`;
const Container = styled.div`
width: 100%;
max-width: 720px;
& > * {
:not(:last-child){
margin-bottom: 16px;
}
}
`;
const PostContainer = styled.div`
padding: 8px 16px;
border: 1px solid grey;
border-radius: 8px;
`;
const TitleText = styled.p`
font-size: 28px;
font-weight: 500;
`;
const ContentText = styled.p`
font-size: 20px;
line-height: 32px;
white-space: pre-wrap;
`;
const CommentLabel = styled.p`
font-size: 16px;
font-weight: 500;
`;
function PostViewPage(props) {
const navigate = useNavigate();
const { postId} = useParams();
const post = data.find((item) => {
return item.id == postId;
});
const [comment, setComment] = useState("");
return (
<Wrapper>
<Container>
<Button
title="뒤로 가기"
onClick={() => {
navigate("/");
}}
/>
<PostContainer>
<TitleText>{post.title}</TitleText>
<ContentText>{post.content}</ContentText>
</PostContainer>
<CommentLabel>댓글</CommentLabel>
<CommentList comments={post.comments}/>
<TextInput
height={40}
value={comment}
onChange={(event) => {
setComment(event.target.value);
}}
/>
<Button
title="댓글 작성하기"
onClick={() => {
navigate("/");
}}
/>
</Container>
</Wrapper>
)
}
export default PostViewPage;
PostViewPage 컴포넌트에서는 먼저 URL 파라미터로 전달받은 글의 아이디를 이용하여
전체 데이터에서 해당되는 글을 찾습니다.
그리고 찾은 글의 제목,내용,댓글을 화면에 렌더링하게 되고 그 아래에는
TextInput 컴포넌트와 Button 컴포넌트를 이용해 댓글을 작성할 수 있도록 UI를 제공합니다.
이렇게 해서 이번미니 블로그 프로젝트에 필요한 세개의 페이지를 모두 구현했습니다.