8.React/1)개념_React

React_개념_Day_03

구이제이 2024. 4. 18. 19:32

ㅡㅡㅡ

 

●1.

 

#

–save

package.json안에 의존성이 기록이 됩니다.

이 파일을 가져가면, 그대로 처리할수있습니다.

 

npm install react-router-dom

npm install –save react-router-dom

 

#

styled -components

 

#router

최적의 경로를 찾아서 보내는 것

 

#react-router-dom

리액트를 위한 routing 라이브러리

문서안에서 길 안내,

브라우저에서 앞,뒤 버튼 동작을 인식해서, 처리할수있도록 하는 역할



#웹사이트에서 라우팅 : 사용자가 원하는 경로로 보내는 과정



 

 

sts3 : 스프링툴

인텔리제이에서 스프링을…

 

#React Router

#next.js (배우면 좋은것)



#PostWritePage.jsx

 

import React, { useState } from 'react';

import { useNavigate } from 'react-router-dom';

import styled from 'styled-components';

import TextInput from '../ui/TextInput';

import Button from '../ui/Button';

 

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] = userState('');

    const [content, setContent] = useState('');

 

    return(

        <Wrapper>

                <Container>

 

                    {/* 제목 */}

                    <TextInput height={20} value={title} onChange={(event) =>{

                        setTitle(event.target.value);

                    }}

                    />

                    {/* 자바스크립에서는 클래스,  여기서는 클래스네임

                                                onchange, 여기서는 onChange

                                                함수따로불러서, 간략한것은 이안에서 함수만드렁서 처리 */}

       

                {/* 내용 */}

                <TextInput height={480} value={content} onChange={(event) =>{

                        setContent(event.target.value);

                    }}

                    />

 

                {/* 버튼, 글 작성하기 */}

                <Button title="글 작성하기" onClick={ () => {navigate("/")}} />

                </Container>

        </Wrapper>

 

    );

 

 

}

 

export default PostWritePage;

 






●2.




#PostViewPage.jsx

 

import React, { useState } from 'react';

import { useNavigate, useParams } from 'react-router-dom';

import styled from 'styled-components';

import CommentList from '../list/CommentList';

import TextInput from '../ui/TextInput';

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;

    }

`;

 

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){

    //url 파라미터로 전달받은 글의 아이디를 이용하여 전체 데이터에

    //해당되는 글을 찾기 위한 작업

    // 찾을 글의 제목, 내용, 댓글을 화면에 렌더링 할 수 있도록 준비

            const navigate = useNavigate();

            const { postId } = useParams();

 

            //익명함수(화살표 함수)

            const post = data.find((item) => {

                return item.id == postId;

            });

 

            //자바스크립트는 쌍따옴표, 홑따옴표 구분안하지만, 다른곳에서는 구분합니다.  

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

 

 

 

return(

    <Wrapper>      

        {/* Wrapper,Container 라는 컴포넌트 */}

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

 



#가장처음 렌더링되는 App.js







●3.



#App.js



// import logo from './logo.svg';

// import './App.css';

 

import React from "react";

import { BrowserRouter, Routes, Route } from "react-router-dom";

import styled from "styled-components";

 

import MainPage from "./component/page/MainPage";

import PostWritePage from "./component/page/PostWritePage";

import PostViewPage from "./component/page/PostViewPage";

 

const MainTitleText = styled.p`

        font-size : 24px;

        font-weight : bold;

        text-align : center;

`;

 

//가장 처음 렌더링 되는 컨포넌트

 

// function App() {

//   return (

//     <div className="App">

//       <header className="App-header">

//         <img src={logo} className="App-logo" alt="logo" />

//         <p>

//           Edit <code>src/App.js</code> and save to reload.

//         </p>

//         <a

//           className="App-link"

//           href="https://reactjs.org"

//           target="_blank"

//           rel="noopener noreferrer"

//         >

//           Learn React

//         </a>

//       </header>

//     </div>

//   );

// }

 

function App(props){

  return(

    <BrowserRouter>

      {/* 페이지 제목, 웹사이트 제일 상단에 제목이 표시 되도록 */}

      <MainTitleText>미니 프로젝트 연습</MainTitleText>

      <Routes>

         <Route index element={<MainPage />} />

         <Route path="post-write" element={<PostWritePage />} />

         <Route path="post/:postId" element={<PostViewPage />} />

        {/*

        path에 콜론(:)을 사용하고 아이디를 입력하면 실제 컴포넌트에서

        useParams() HOOK을 사용해서 아이디로 해당 값을 가져올 수 있음

        동적으로 변하는 파라미터를 위한 값

         */}

      </Routes>

    </BrowserRouter>

  );

}

 

//post/:postId : 콜론이 들어간다는 것은 값이 동적으로 변화가 있다.

//정적변화의 파라미터를 콜론을 넣어서 동적으로 처리합니다.

 

 

export default App;







●4.

 

#data.json

 

[

{

    "id": 1,

    "title" : "react 테스트1",

    "content" : "hello~~\n",

    "comments": [

        {

            "id" : 11,

            "content" : "json 데이터 댓글 연습11"

        },

        {

            "id" : 12,

            "content" : "json 데이터 댓글 연습12"

        },

        {

            "id" : 13,

            "content" : "json 데이터 댓글 연습13"

        },

        {

            "id" : 14,

            "content" : "json 데이터 댓글 연습14"

        },

        {

            "id" : 15,

            "content" : "json 데이터 댓글 연습15"

        }

    ]

},

{

    "id": 2,

    "title" : "react 테스트2",

    "content" : "hello~~\n",

    "comments": [

        {

            "id" : 21,

            "content" : "json 데이터 댓글 연습21"

        },

        {

            "id" : 22,

            "content" : "json 데이터 댓글 연습22"

        },

        {

            "id" : 23,

            "content" : "json 데이터 댓글 연습23"

        },

        {

            "id" : 24,

            "content" : "json 데이터 댓글 연습24"

        },

        {

            "id" : 25,

            "content" : "json 데이터 댓글 연습25"

        }

    ]

}

 

 

]

 

 

#

 





●5.

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

    }

`;

 

function CommentList(props) {

    const { comments } = props; //  const { comments } : 배열로 들어오고 있습니다.

 

    return (

        <Wrapper>

            { /* 배열.map() : 각 댓글 객체를 CommentListItem */}

            {comments.map((comment, index) => {

                // 맵 키와 밸류으로 처리합니다.

                return (

                    <CommentListItem key={comment.id} comment={comment} />

                );

            })}

        </Wrapper>

    );

}

 

export default CommentList;

 

#CommentListItem.jsx

 

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: lightgrey;

    }

`;

 

const ContentText = styled.p`

    font-size: 16px;

    white-space: pre-wrap;

`;

 

function CommentListItem(props) {

    //props에서 comment 객체 하나만 사용

    // comment : 사용자가 작성한 댓글 내용이 들어 있음

    const { comment } = props;

 

    return (

        <Wrapper>

             {/* styled-components를 통해 만든 ContentText 컴포넌트를 이용해서

                    화면에 표시

             */}

            <ContentText>{comment.content}</ContentText>

        </Wrapper>

    );

}

 

export default CommentListItem;



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

    }

`;

 

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.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: lightgrey;

    }

`;

 

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;



#MainPage.jsx

 

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){

    // react-router-dom의 useNavigate();  : 훅입니다.

    // 페이징 이동을 위해 사용

    const navigate = useNavigate();

 

    return(

        <Wrapper>

            {/* 대문자 : 첫글자가 대문자로나오면 사용자정의태그(xml태그)

 

            소문자 :  */}

 

            <Container>

                {/* 글을 작성할 수 있는 버튼 */}

                <Button title="글 작성하기" onClick={() => {

                            navigate('/post-write');

                }} />

 

 

 

                {/* 글 목록을 보여주는 부분 */}

                <PostList

                    posts={data} onClickItem={(item)=>{

                        navigate(`/post/${item.id}`);

                    }}

                />

 

            </Container>

        </Wrapper>

 

    );

 

 

}

 

export default MainPage;

 

 

/*

react-router-dom : 리액트를 위한 routing 라이브러리

웹사이트에서 라우팅 : 사용자가 원하는 경로로 보내는 과정

https:reactrouter.com/main

컴포넌트 구성 : BrowserRouter, Routes, Route

          ㅡ  BrowserRouter : 웹브라우저에서 react-router를 사용하여 라우팅을 할 수 있도록 해주는 컴포넌트

 

          ㅡ  Routes : 여러 개의 Route 컴포넌트를 children으로 가짐

                         (자식들을 살펴보아서, 엘리멘트에가서 뷰로 띄어준다.)

           

          ㅡ  Route : Routes컴포넌트의 하위 컴포넌트

                            path와 element라는 props를 가지고 있음

         ㅡ   path : 경로

         ㅡ   element : 경로가 일치할 경우 렌더링을 할 리액트의 엘리먼트를 의미, 뷰페이지

           

브라우저안에서 내장되어있는 것, 경로탐색하는 것이 브라우저라우터컴포넌트

이전페이지 찾아가지고, 이것을 이동 브라우저라우터

 

 

 

*/



#PostViewPage.jsx

 

import React, { useState } from 'react';

import { useNavigate, useParams } from 'react-router-dom';

import styled from 'styled-components';

import CommentList from '../list/CommentList';

import TextInput from '../ui/TextInput';

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;

    }

`;

 

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){

    //url 파라미터로 전달받은 글의 아이디를 이용하여 전체 데이터에

    //해당되는 글을 찾기 위한 작업

    // 찾을 글의 제목, 내용, 댓글을 화면에 렌더링 할 수 있도록 준비

            const navigate = useNavigate();

            const { postId } = useParams();

 

            //익명함수(화살표 함수)

            const post = data.find((item) => {

                return item.id == postId;

            });

 

            //자바스크립트는 쌍따옴표, 홑따옴표 구분안하지만, 다른곳에서는 구분합니다.  

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

 

 

 

return(

    <Wrapper>      

        {/* Wrapper,Container 라는 컴포넌트 */}

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



#PostWritePage.jsx

 

import React, { useState } from 'react';

import { useNavigate } from 'react-router-dom';

import styled from 'styled-components';

import TextInput from '../ui/TextInput';

import Button from '../ui/Button';

 

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

                    }}

                    />

                    {/* 자바스크립에서는 클래스,  여기서는 클래스네임

                                                onchange, 여기서는 onChange

                                                함수따로불러서, 간략한것은 이안에서 함수만드렁서 처리 */}

       

                {/* 내용 */}

                <TextInput height={480} value={content} onChange={(event) =>{

                        setContent(event.target.value);

                    }}

                    />

 

                {/* 버튼, 글 작성하기 */}

                <Button title="글 작성하기" onClick={ () => {navigate("/")}} />

                </Container>

        </Wrapper>

 

    );

 

 

}

 

export default PostWritePage;

 



#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) {

    // props로 받은 title이 버튼에 표시되도록

    // props로 받은 onClick은 StyledButtond의 onClick에 넣어 줌으로써

    // 클릭 이벤트를 상위 컴포넌트에서 받을 수 있도록 설정

   

 

    const { title, onClick } = props;

 

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

}

 

export default Button;




#TextInput.jsx

 

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, value, onChange } = props;

 

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

}

 

export default TextInput;

 

'8.React > 1)개념_React' 카테고리의 다른 글

React_개념_Day_05_02  (0) 2024.04.25
React_개념_Day_05_01  (1) 2024.04.25
React_개념_Day_04  (0) 2024.04.24
React_개념_Day_02  (0) 2024.04.17
React_설치와개념_Day_01  (0) 2024.04.16