Home 미니 블로그 만들기 - 03
Post
X

미니 블로그 만들기 - 03

App.jsx 파일 생성하기

src 폴더에 App.jsx 파일을 작성하겠습니다.


레이아웃 설정

먼저 콘텐츠 영역을 구분하기 위해 Wrapper로 묶어줍니다.

Wrapper에는 max-widthpadding을 설정해주고 margin : 0 auto로 중앙정렬 해줍니다.

그 다음 Container로 묶어 안의 내용을 display : flex로 설정해주고 요소 사이에 margin-bottom을 넣어주겠습니다.

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
import React from "react";
import { styled } from "@mui/material/styles";

const Wrapper = styled("div")`
  max-width: 720px;
  padding: 0 16px;
  margin: 0 auto;
`;

const Container = styled("div")`
  display: flex;
  flex-direction: column;

  & > *:not(:last-child) {
    margin-bottom: 1rem;
  }
`;

const App = () => {
  return (
    <Wrapper>
      <Container></Container>
    </Wrapper>
  );
};

export default App;

React-Router 설정하기

React-Router의 자세한 내용은 이전에 작성한 포스팅을 참고해주세요.

뒤에 구현할 Page 컴포넌트인 글 목록을 보여줄 메인 페이지글 작성 페이지, 글 상세 페이지를 각각 라우팅해줍니다.

자세한 방법은 React-router 글을 확인해주세요.

Header 대신 제목을 하나 작성해주겠습니다.

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import React from "react";
import { styled } from "@mui/material/styles";
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
import BoardPage from "./components/page/BoardPage";
import PostViewPage from "./components/page/PostViewPage";
import PostWritePage from "./components/page/PostWritePage";

const Wrapper = styled("div")`
  max-width: 720px;
  padding: 0 16px;
  margin: 0 auto;
`;

const Container = styled("div")`
  display: flex;
  flex-direction: column;

  & > *:not(:last-child) {
    margin-bottom: 1rem;
  }
`;

const MainTitleText = styled(Link)`
  margin-top: 2rem;
  display: block;
  text-align: center;
  text-decoration: none;
  font-size: 2rem;
  font-weight: bold;

  color: #8179d7;

  &:active {
    color: #7165f0;
  }
`;

const App = () => {
  return (
    <Wrapper>
      <Container>
        <Router>
          <MainTitleText to="board">미니 블로그</MainTitleText>
          <Routes>
            <Route path="board" element={<BoardPage />} />
            <Route path="board/write" element={<PostWritePage />} />
            <Route path="board/:postId" element={<PostViewPage />} />
          </Routes>
        </Router>
      </Container>
    </Wrapper>
  );
};

export default App;

이제 import할 Page 컴포넌트만 작성하면 미니 블로그 완성입니다.

이제 Page 컴포넌트를 구현해보겠습니다.


Page 컴포넌트 구현하기

작성한 글 목록을 보여주는 BoardPage.jsx, 글을 작성하는 PostWritePage.jsx, 하나의 글을 보여주는 PostViewPage.jsx를 구현해보겠습니다.

  • 폴더 구성하기

    src

    component

    list
    ui
    page


BoardPage 글 목록 페이지

page 폴더에 BoardPage.jsx를 생성합니다.


버튼 생성

글 목록에서 글 작성 페이지로 넘어가기위한 버튼을 생성해보겠습니다.

미리 만들어둔 UI 컴포넌트의 BasicButton 컴포넌트를 import해서 사용하겠습니다.

버튼의 위치를 조정하기위해 BtnWrapper로 감싸주겠습니다.

margin-left : auto를 이용해 오른쪽에 붙여서 사용하겠습니다.

BasicButton 컴포넌트에 props로 title, onClickItem을 넘깁니다.

  • title

    버튼안에 입력할 문자열

  • onClickItem

    useNavigate() Hook을 사용해 글 작성 페이지로 이동하는 함수

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
import React from "react";
import { useNavigate } from "react-router-dom";
import styled from "styled-components";
import BasicButton from "../ui/BasicButton";

const BtnWrapper = styled.div`
  margin-left: auto;
`;

const MainPage = () => {
  const navigate = useNavigate();

  return (
    <>
      <BtnWrapper>
        <BasicButton
          title={"글 작성하기"}
          onClickItem={() => navigate("/board/write")}
        ></BasicButton>
      </BtnWrapper>
    </>
  );
};

export default MainPage;

글 목록 보여주기

글 목록 보여주기위해 미리 만들어둔 List 컴포넌트의 PostList 컴포넌트를 import해서 사용하겠습니다.

먼저 넘겨줄 글 목록 데이터 파일인 data.jon을 import합니다.

PostList 컴포넌트에 props로 posts, onClickItem을 넘깁니다.

  • posts

    글 정보를 담은 data

  • onClickItem

    useNavigate() Hook을 사용해 글 상세 페이지로 이동하는 함수

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
31
32
33
34
import React from "react";
import { styled } from "@mui/material/styles";
import BasicButton from "../ui/BasicButton";
import { useNavigate } from "react-router-dom";
import PostList from "../list/PostList";
import data from "../../data.json";

const BtnWrapper = styled.div`
  margin-left: auto;
`;

const BoardPage = () => {
  const navigate = useNavigate();

  return (
    <>
      <BtnWrapper>
        <BasicButton
          title={"글 작성하기"}
          onClickItem={() => navigate("/post-write")}
        ></BasicButton>
      </BtnWrapper>

      <PostList
        posts={data}
        onClickItem={(item) => {
          navigate(`/board/${item.post_id}`);
        }}
      ></PostList>
    </>
  );
};

export default BoardPage;

PostWritePage 글 작성 페이지

page 폴더에 PostWritePage.jsx를 생성합니다.


버튼 생성

글 작성 페이지에서 메인 페이지로 돌아가기위한 버튼과 글 작성 완료 버튼을 생성해보겠습니다.

메인 페이지의 버튼과 같은 방법으로 구현합니다.

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
31
32
33
34
35
36
import React from "react";
import { styled } from "@mui/material/styles";
import BasicButton from "../ui/BasicButton";
import { useNavigate } from "react-router-dom";

const BtnWrapper = styled("div")`
  margin-left: auto;
`;

const PostWritePage = () => {
  const navigate = useNavigate();

  return (
    <>
      <BtnWrapper>
        <BasicButton
          title={"뒤로 가기"}
          onClickItem={() => {
            navigate("/board");
          }}
        ></BasicButton>
      </BtnWrapper>

      <BtnWrapper>
        <BasicButton
          title={"글 작성 완료"}
          onClick={() => {
            navigate("/");
          }}
        ></BasicButton>
      </BtnWrapper>
    </>
  );
};

export default PostWritePage;

글 작성 부분 생성

사용자가 제목과 내용을 입력할 부분을 생성해보겠습니다.

미리 만들어둔 UI 컴포넌트의 TextInput 컴포넌트를 import해서 사용하겠습니다.

TextInput 컴포넌트에 props로 rows, value, onChangeItem, placeholder을 넘깁니다.

  • row

    화면에 보이는 값을 입력할 행의 수

  • value

    값 부분으로 useState로 설정한 변수

  • placeholder

    사용자가 값을 입력하기전에 입력할 내용을 알려주는 도움말

  • onClickItem

    useState() Hook을 사용해 값이 변하면 State를 업데이트해 재랜더링하는 함수

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
import React, { useState } from "react";
import { useNavigate } from "react-router-dom";
import { styled } from "@mui/material/styles";
import BasicButton from "../ui/BasicButton";
import TextInput from "../ui/TextInput";

const BtnWrapper = styled("div")`
  margin-left: auto;
`;

const PostWritePage = () => {
  const navigate = useNavigate();

  const [title, setTitle] = useState("");
  const [content, setContent] = useState("");

  return (
    <>
      <BtnWrapper>
        <BasicButton
          title={"뒤로 가기"}
          onClickItem={() => {
            navigate("/");
          }}
        ></BasicButton>
      </BtnWrapper>

      <TextInput
        rows={1}
        placeholder="제목"
        value={title}
        onChange={(event) => {
          setTitle(event.target.value);
        }}
      ></TextInput>

      <TextInput
        rows={14}
        placeholder="내용"
        value={content}
        onChange={(event) => {
          setContent(event.target.value);
        }}
      ></TextInput>

      <BtnWrapper>
        <BasicButton
          title={"글 작성 완료"}
          onClick={() => {
            navigate("/");
          }}
        ></BasicButton>
      </BtnWrapper>
    </>
  );
};

export default PostWritePage;

PostViewPage 글 상세 페이지

page 폴더에 PostViewPage.jsx를 생성합니다.


버튼 생성

글 상세 페이지에서 메인 페이지로 돌아가기위한 버튼과 댓글 작성 완료 버튼을 구현합니다.

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
31
32
33
34
35
36
import React from "react";
import { useNavigate } from "react-router-dom";
import { styled } from "@mui/material/styles";
import BasicButton from "../ui/BasicButton";

const BtnWrapper = styled.div`
  margin-left: auto;
`;

const PostViewPage = () => {
  const navigate = useNavigate();

  return (
    <>
      <BtnWrapper>
        <BasicButton
          title={"뒤로 가기"}
          onClickItem={() => {
            navigate("/Board");
          }}
        ></BasicButton>
      </BtnWrapper>

      <BtnWrapper>
        <BasicButton
          title={"댓글 작성하기"}
          onClickItem={() => {
            navigate("/");
          }}
        ></BasicButton>
      </BtnWrapper>
    </>
  );
};

export default PostViewPage;

글 정보 부분

다음으로 글 정보를 보여주는 부분을 구현해보겠습니다.

먼저 본문 영역을 PostContainer로 감싸주고 paddingborder를 주겠습니다.

그 후, 컨테이너 안에 TitleTextContentText를 생성합니다.

각각 제목과 내용을 출력할 요소로 스타일을 바꿔줍니다.

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import React from "react";
import { useNavigate } from "react-router-dom";
import { styled } from "@mui/material/styles";
import TextInput from "../ui/TextInput";
import BasicButton from "../ui/BasicButton";
import data from "../../data.json";

const BtnWrapper = styled("div")`
  margin-left: auto;
`;

const PostContainer = styled("div")`
  padding: 1rem;
  border: 1px solid grey;
  border-radius: 8px;
`;

const TitleText = styled("p")`
  margin: 0;
  font-size: 2rem;
  font-weight: 600;
`;

const ContentText = styled("p")`
  font-size: 1rem;
  line-height: 2rem;
  white-space: pre-wrap;
`;

const PostViewPage = () => {
  const navigate = useNavigate();

  return (
    <>
      <BtnWrapper>
        <BasicButton
          title={"뒤로 가기"}
          onClickItem={() => {
            navigate("/Board");
          }}
        ></BasicButton>
      </BtnWrapper>

      <PostContainer>
        <TitleText></TitleText>
        <ContentText></ContentText>
      </PostContainer>

      <BtnWrapper>
        <BasicButton
          title={"댓글 작성하기"}
          onClickItem={() => {
            navigate("/");
          }}
        ></BasicButton>
      </BtnWrapper>
    </>
  );
};

export default PostViewPage;

이제 보여줄 글 정보인 data.json을 import하고 출력해보겠습니다.

해당하는 글을 구분하기위해 parameterpostId를 넘겨줬습니다.

이를 받기위해 useParams Hook을 사용합니다.

useParams Hookparameter 정보를 받아옵니다.

useParams을 import한 후 postId 정보를 받아온 후 해당 값을 key로 글 정보를 받아옵니다.

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
import React from "react";
import { useNavigate, useParams } from "react-router-dom";
import { styled } from "@mui/material/styles";
import TextInput from "../ui/TextInput";
import BasicButton from "../ui/BasicButton";
import data from "../../data.json";

const BtnWrapper = styled("div")`
  margin-left: auto;
`;

const PostContainer = styled("div")`
  padding: 1rem;
  border: 1px solid grey;
  border-radius: 8px;
`;

const TitleText = styled("p")`
  margin: 0;
  font-size: 2rem;
  font-weight: 600;
`;

const ContentText = styled("p")`
  font-size: 1rem;
  line-height: 2rem;
  white-space: pre-wrap;
`;

const PostViewPage = () => {
  const navigate = useNavigate();
  // parameter 를 받아옵니다.
  const { postId } = useParams();

  // 받아온 parameter와 id가 일치하는 data를 post에 담습니다.
  const post = data.find((item) => {
    return item.id === Number(postId);
  });

  return (
    <>
      <BtnWrapper>
        <BasicButton
          title={"뒤로 가기"}
          onClickItem={() => {
            navigate("/Board");
          }}
        ></BasicButton>
      </BtnWrapper>

      <PostContainer>
        <TitleText>{post.title}</TitleText>
        <ContentText>{post.content}</ContentText>
      </PostContainer>

      <BtnWrapper>
        <BasicButton
          title={"댓글 작성하기"}
          onClickItem={() => {
            navigate("/");
          }}
        ></BasicButton>
      </BtnWrapper>
    </>
  );
};

export default PostViewPage;

댓글 목록 보여주기

댓글 목록을 보여주는 부분을 구현해보겠습니다.

이전에 만들어 둔 List 컴포넌트인 CommentList 컴포넌트를 Import합니다.

post에 담긴 현재 글에 대한 댓글들(post.comments)을 CommentList 컴포넌트에게 넘겨줍니다.

CommentList 컴포넌트는 props로 받은 comments를 map함수로 나열해 출력합니다.

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import React from "react";
import { useNavigate, useParams } from "react-router-dom";
import { styled } from "@mui/material/styles";
import CommentList from "../list/CommentList";
import TextInput from "../ui/TextInput";
import BasicButton from "../ui/BasicButton";
import data from "../../data.json";

const BtnWrapper = styled("div")`
  margin-left: auto;
`;

const PostContainer = styled("div")`
  padding: 1rem;
  border: 1px solid grey;
  border-radius: 8px;
`;

const TitleText = styled("p")`
  margin: 0;
  font-size: 2rem;
  font-weight: 600;
`;

const ContentText = styled("p")`
  font-size: 1rem;
  line-height: 2rem;
  white-space: pre-wrap;
`;

const CommentLabel = styled("p")`
  font-size: 16px;
  font-weight: 500;
`;

const PostViewPage = () => {
  const navigate = useNavigate();

  const { postId } = useParams();

  const post = data.find((item) => {
    return item.id === Number(postId);
  });

  return (
    <>
      <BtnWrapper>
        <BasicButton
          title={"뒤로 가기"}
          onClickItem={() => {
            navigate("/Board");
          }}
        ></BasicButton>
      </BtnWrapper>

      <PostContainer>
        <TitleText>{post.title}</TitleText>
        <ContentText>{post.content}</ContentText>
      </PostContainer>

      <CommentLabel>댓글</CommentLabel>
      <CommentList comments={post.comments}></CommentList>

      <BtnWrapper>
        <BasicButton
          title={"댓글 작성하기"}
          onClickItem={() => {
            navigate("/");
          }}
        ></BasicButton>
      </BtnWrapper>
    </>
  );
};

export default PostViewPage;

댓글 작성 부분

댓글 목록 아래에 댓글을 작성하는 부분을 구현해보겠습니다.

글 작성 페이지에서와 똑같이 TextInput 컴포넌트를 import해서 사용하겠습니다.

onClickItem에는 useState() Hook을 사용해 댓글 내용의 변화를 업데이트하는 함수를 넘겨줍니다.

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import React, { useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import { styled } from "@mui/material/styles";
import CommentList from "../list/CommentList";
import TextInput from "../ui/TextInput";
import BasicButton from "../ui/BasicButton";
import data from "../../data.json";

const BtnWrapper = styled("div")`
  margin-left: auto;
`;

const PostContainer = styled("div")`
  padding: 1rem;
  border: 1px solid grey;
  border-radius: 8px;
`;

const TitleText = styled("p")`
  margin: 0;
  font-size: 2rem;
  font-weight: 600;
`;

const ContentText = styled("p")`
  font-size: 1rem;
  line-height: 2rem;
  white-space: pre-wrap;
`;

const CommentLabel = styled("p")`
  font-size: 16px;
  font-weight: 500;
`;

const PostViewPage = () => {
  const navigate = useNavigate();
  const { postId } = useParams();

  const post = data.find((item) => {
    return item.id === Number(postId);
  });

  const [comment, setComment] = useState("");

  return (
    <>
      <BtnWrapper>
        <BasicButton
          title={"뒤로 가기"}
          onClickItem={() => {
            navigate("/Board");
          }}
        ></BasicButton>
      </BtnWrapper>

      <PostContainer>
        <TitleText>{post.title}</TitleText>
        <ContentText>{post.content}</ContentText>
      </PostContainer>

      <CommentLabel>댓글</CommentLabel>
      <CommentList comments={post.comments}></CommentList>

      <TextInput
        rows={5}
        placeholder="댓글 내용"
        value={comment}
        onChange={(event) => {
          setComment(event.target.value);
        }}
      ></TextInput>

      <BtnWrapper>
        <BasicButton
          title={"댓글 작성하기"}
          onClickItem={() => {
            navigate("/");
          }}
        ></BasicButton>
      </BtnWrapper>
    </>
  );
};

export default PostViewPage;

정리

React를 이용해 간단한 미니 블로그를 구현해보았습니다.

다음에는 express 서버 구축해보겠습니다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.