** Header와 Footer 는 라우트에 영향을 받지 않는다.
1. App.js
import { Routes, Route } from 'react-router-dom';
import './App.css';
import Main from './components/Main';
import ProductList from './components/ProductList';
import ProductItem from './components/ProductItem';
import ProductDetail from './components/ProductDetail';
import Header from './components/Header';
import Footer from './components/Footer';
import { BrowserRouter as Router} from 'react-router-dom';
import { useState } from 'react';
function App() {
const [list, setList]= useState([]);
return (
<div className='container'>
<Header/>
<Routes>
<Route path='/' element={<Main/>}></Route>
<Route path ='/list' element = {<ProductList list ={list} setList={setList}/>}/>
<Route path ='/detail/:num' element = {<ProductDetail/>}/>
</Routes>
<Footer/>
</div>
);
}
export default App;
2. App.css
*{
margin: 0;
padding: 0;
}
.container{
height : 100vh;
width: 100vw;
display: block;
}
.container>div{
display: flex;
align-items: center;
}
.header-container{
background-color: lightgray;
height: 10vh;
justify-content: space-between;
font-size: 1.5em;
font-weight: bolder;
padding: 0 5vw;
}
.header-container>a{
color: black;
text-decoration: none;
}
.footer-container{
background-color: lightgray;
height: 10vh;
justify-content: center;
}
.main-container{
/* background-color: #FFFAE0; */
height: 80vh;
padding: 0 5vw;
box-sizing: border-box;
overflow: auto;
flex-direction: row;
flex-wrap: wrap;
justify-content: space-evenly;
}
.main-container>.main-text{
width: 45vw;
font-size: 1.75rem;
}
.main-container>.main-image{
width: 45vw;
display: flex;
justify-content: right;
}
.main-container>.main-image>img{
height: 70vh;
}
.product-container{
background-color: lightyellow;
margin: 2.5vh 0;
height: 75vh;
border-radius: 20px;
font-size: 1.75rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 3vw;
box-sizing: border-box;
/* overflow: hidden; */
transition: 0.5s;
}
.product-container img{
width: 30vw;
}
.product-container:hover{
box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, 0.1);
}
.detail-text>p{
display: flex;
flex-direction: row;
justify-content: space-between;
}
.detail-container{
flex-direction: row;
width: 70vw;
}
.detail-image{
display: flex;
align-items: center;
}
.detail-text{
margin-left: 3vw;
}
3. Folder public -> bestList.json (to send data)
{
"list": [
{
"no" : 1,
"title": "딸기 레터링 케이크 캔들 +머리띠🍓",
"price": 18900,
"delivery": "free",
"src": "https://image.idus.com/image/files/7ad7fcd0400d40319630e1520cac6119_1440.jpg"
},
{
"no" : 2,
"title": "위피베어 침구세트-프리지아",
"price": 18000,
"delivery": "3000",
"src": "https://image.idus.com/image/files/994385ec40fd4ed8aa41381d78456b02_1440.jpg"
},
{
"no" : 3,
"title": "도화 비녀/한복 머리 장식/뒤꽂이",
"price": 16910,
"delivery": "free",
"src": "https://image.idus.com/image/files/35e0882e95bd4f75bc14e70707b11029_1440.jpg"
},
{
"no" : 4,
"title": "미니미 꼬질곰 뜨개 인형 키링🧸💖",
"price": 10100,
"delivery": "3000",
"src": "https://image.idus.com/image/files/93d923d7167e4a428a9771520ddda4cf_1440.jpg"
},
{
"no" : 5,
"title": "꽃에 취한냥🌸주병 선물세트",
"price": 43000,
"delivery": "3000",
"src": "https://image.idus.com/image/files/0d8bdf6b191541e88cdc42892bc7ca23_1440.jpg"
},
{
"no" : 6,
"title": "납작뜨개키링",
"price": 12000,
"delivery": "3000",
"src": "https://image.idus.com/image/files/63b018f447bb46018941598c1b219d6a_1440.jpg"
}
]
}
4. Folder components -> Create
4.1 Footer.jsx
import React from 'react'
const Footer = () => {
return (
<div className='footer-container'>
<strong>전화</strong>1544-9796<br></br>
<strong>고객문의</strong>css@carrot.service.com
</div>
)
}
export default Footer
4.2 Header.jsx
import React from 'react'
import {Link, useNavigate} from 'react-router-dom'
const Header = () => {
const nav = useNavigate();
let auth = true;
return (
<div className='header-container'>
<Link to ='/'> 당근마켓 </Link>
<Link to ='/list'> 상품목록</Link>
</div>
)
}
export default Header
4.3 Main.jsx
import React from 'react'
const Main = () => {
return (
<div className='main-container'>
<div className='main-text'>
<h1>당신 근처의 지역 생활 커뮤니티</h1>
<p>동네라서 가능한 모든 것</p><br></br>
<p>당근에서 가까운 이웃과 함께해요.</p>
</div>
<div className='main-image'><img src ="https://d1unjqcospf8gs.cloudfront.net/assets/home/main/3x/rebranded-image-top-eb44f81acb1938b57ba029196887cdd56fbb66dc46aa5d8c6d8392a7d8c9e671.png" alt = ""></img></div>
</div>
)
}
export default Main
4.4 ProductDetail.jsx
import React from 'react'
import { useParams, useSearchParams } from 'react-router-dom'
const ProductDetail = ({list}) => {
let {num} = useParams();
console.log(list[num]);
return (
<div className='main-container'>
<div className='product-container detail-container'>
<div className='detail-image'>
<img src = {list[num].src} alt = ""></img>
</div>
<div className='detail-text'></div>
<h4>{list[num].title}</h4>
<p>
<span>
Price: {list[num].price} 원
</span><br></br>
<span>
Delivery: {list[num].delivery ==='free'? '무료배송':'배송비'+list[num].delivery+'원'}
</span>
</p>
</div>
</div>
)
}
export default ProductDetail
4.5 ProductItem.jsx
import React from 'react'
import { useNavigate } from 'react-router-dom';
const ProductItem = ({item, index}) => {
console.log('test', item, index);
/*
해당 아이템 div을 클릭했을 때,
아이템에 대한 상세페이지(ProductDetail)로 이동
=> useNavigate 사용
=> 상품별 고유번호 index 사용 : /detail/1, /detail/2
*/
const nav = useNavigate();
return (
<div className='product-container'
onClick={()=>{nav(`/detail/${index}`)}}>
<img src={item.src}/>
<p>{item.title}</p>
<p>{item.price}원</p>
</div>
)
}
export default ProductItem
4.6 ProductList.jsx
import React from 'react'
import { useParams, useSearchParams } from 'react-router-dom'
import ProductItem from './ProductItem'
import Header from './Header'
import Footer from './Footer'
import ProductDetail from './ProductDetail'
import { useEffect } from 'react'
import axios from 'axios'
const ProductList = ({list, setList}) => {
/*
(1) public 안 bestList.json데이터 접근
=> axios, useEffect 이용
(2)가지고 온 데이터 list에 세팅
*/
useEffect(()=>{
axios
.get("http://localhost:3000/bestList.json")
.then((res)=>{
console.log(res.data.list);
setList(res.data.list)
})
.catch()
//api data를 주고 받을 수 있는 문볍 2가지: fetch,axios
// case 1: fetch API
// - 장점: js잦체 내장 라이버라리, 별도의 설치 필요없고 속도가 상대적으로 빠름
//- 단점: 가끔 지원이 되지 않은 브라우저 => 그러다 보니 react-native (어플)에서 많이 사용
// json변환 작업 필요
},[])
return (
<>
<table>
<tbody>
{
list.map((item,index)=>(
<ProductItem key ={item.no} item ={item} index = {index}/>
))
}
</tbody>
</table>
</>
)
}
export default ProductList