mini-blog 만들기

OwlSuri·2022년 7월 18일
0

React

목록 보기
16/20

프로젝트 생성

npx create-react-app mini-blog(프로젝트이름)

프로젝트에 필요한 패키지 설치

  • router, styled-component
npm install --save react-router-dom styled-components

컴포넌트 구성

필요한 기능

  • 글목록 리스트 : PostList, PostListItem
  • 글 보기 : Post
  • 댓글 보기 : CommentList, CommentListItem
  • 글 쓰기 : PostWrite
  • 댓글쓰기 : CommentWrite

폴더구성


1. UI Component 구성하기

  • 사용자가 입력을 할 수 있게 해주는 component
  • bottom up 방식으로 작은 부분부터 구현

button 컴포넌트 만들기

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;
`;

export default function Button(props) {
  const { title, onClick } = props;

  return <StyledButton onClick={onClick}>{title || "button"}</StyledButton>;
}

ui 폴더에 TextInput.jsx

import React from "react";
import styled from "styled-components";

const StyledTextarea = styled.input`
  width: calc(100% - 32px);
  ${(props) => props.height && `height : ${props.height}px`};
  padding: 16px;
  font-size: 16px;
  line-height: 20px;
`;

export default function TextInput(props) {
  const { height, value, onChange } = props;

  return <StyledTextarea height={height} value={value} onChange={onChange} />;
}

2. List Component 구성하기

  • 작은 것부터
    list 폴더에 PostListItem.jsx
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;
`;

export default function PostListItem(props) {
  const { post, onClick } = props;
  return (
    <Wrapper onClick={onClick}>
      <TitleText>{post.title}</TitleText>
    </Wrapper>
  );
}

list 폴더에 PostList.jsx

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;
    }
  }
`;

export default function PostList(props) {
  const { posts, onClickItem } = props;

  return (
    <Wrapper>
      {posts.map((post, index) => {
        return (
          <PostListItem
            key={post.id}
            post={post}
            onClick={() => {
              onClickItem(post);
            }}
          />
        );
      })}
    </Wrapper>
  );
}

list 폴더에 CommentListItem

import React from "react";
import styled from "styled-components";
import PostListItem from "./PostListItem";

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;
  cursor: pointer;
  background: white;
  :hover {
    background: lightgray;
  }
`;

const ContentText = styled.p`
  font-size: 14px;
`;

export default function CommentListItem(props) {
  const { comment } = props;

  return (
    <Wrapper>
      <ContentText>{comment.comment}</ContentText>
    </Wrapper>
  );
}

list 폴더에 CommentList.jsx

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;
    }
  }
`;

export default function CommentList(props) {
  const { comments } = props;

  return (
    <Wrapper>
      {comments.map((comment, index) => {
        return <CommentListItem key={comment.id} comment={comment} />;
      })}
    </Wrapper>
  );
}

가짜 데이터 만들기

  • DB와 서버가 없기때문에
  • src - data.json 이라는 파일을 만들고 가짜 데이터를 만든다

3. Page 컴포넌트 구성하기

page 폴더에 postWrite

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

export default 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>
  );
}

page폴더에 MainPage.jsx

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

export default function MainPage(props) {
  const {} = props;
  const navigate = useNavigate();

  return (
    <Wrapper>
      <Container>
        <Button
          title="글 작성하기"
          onClick={() => {
            navigate("post-write");
          }}
        ></Button>
        <PostList
          posts={data}
          onClickItem={(item) => {
            navigate(`/post/${item.id}`);
          }}
        />
      </Container>
    </Wrapper>
  );
}

Page폴더에 PostViewPage.jsx

import React, { useState } from "react";
import { useNavigate, useParams } from "react-router-dom";
import styled from "styled-components";
import Button from "../ui/Button";
import { data } from "../../data.json";
import CommentList from "../list/CommentList";
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 gray;
  border-radius: 8x;
`;

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;
`;

export default 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>
  );
}

각 페이지별로 경로 구성하기

react-router-dom

  • 리액트 라우팅 라이브러리
  • 원하는 경로로 보내는 과정
  • BrowserRoute

    - 웹브라우저에서 리액트 라우터를 사용하여 라우팅을 할 수 있도록 해주는 컴포넌트
    - 히스토리를 이용해서 이전페이지 찾을 수 있음
  • Routes와 Route는 실제로 라우팅 경로를 구성할 수 있게해줌

페이지 사이 이동

  • useNavigate() 사용

App.js 파일 수정하기

import React from "react";
import styled from "styled-components";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import MainPage from "../src/component/page/MainPage";
import PostWritePage from "../src/component/page/PostWritePage";
import PostViewPage from "../src/component/page/PostViewPage";

const MainTitleText = styled.p`
  font-size: 24px;
  font-weight: bold;
  text-align: center;
`;

export default function App(props) {
  return (
    <BrowserRouter>
      <MainTitleText>Suri의 미니 블로그</MainTitleText>
      <Routes>
        <Route index element={<MainPage />} />
        <Route path="post-write" element={<PostWritePage />} />
        <Route path="post/:postId" element={<PostViewPage />} />
      </Routes>
    </BrowserRouter>
  );
}

애플리케이션 실행하기

npm start

Product Build

  • 코드와 애플리케이션이 사용하는 이미지, CSS파일 등의 파일을 모두 모아서 패키징하는 과정
npm run build


build가 완료되면 이렇게 build폴더가 생김

serve 설치하기

  • static 파일들 serving하는 역할
  • global 모드로 설치
npm install -g serve
  • 실핼
serve -s build

배포

  • 빌드를 통해 생성된 정적인 파일들을 배포하려는 서버에 올리는 과정
profile
기억이 안되면, 기록을 -

0개의 댓글