ㅡㅡㅡ
●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 |