App.jsx 파일 생성하기
src 폴더에 App.jsx
파일을 작성하겠습니다.
레이아웃 설정
먼저 콘텐츠 영역을 구분하기 위해 Wrapper로 묶어줍니다.
Wrapper에는 max-width와 padding을 설정해주고 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로 감싸주고 padding과 border를 주겠습니다.
그 후, 컨테이너 안에 TitleText와 ContentText를 생성합니다.
각각 제목과 내용을 출력할 요소로 스타일을 바꿔줍니다.
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하고 출력해보겠습니다.
해당하는 글을 구분하기위해 parameter로 postId
를 넘겨줬습니다.
이를 받기위해 useParams Hook을 사용합니다.
useParams Hook은 parameter 정보를 받아옵니다.
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 서버 구축해보겠습니다.