리액트 기초부터 쇼핑몰 프로젝트까지! Part2 #2 리액트라우터
* 페이지 나누기
: 리액트는 single page application라서 index.html 이라는 하나의 html 파일만 사용하기 때문에 react-router-dom이라는 외부 라이브러리 설치해서 사용함.
* 라우터로 페이지 나누는 법
1. react-router-dom 설치
1) 터미널 열고 npm install react-router-dom@6 입력해서 설치 한 뒤
2) index.js 파일로 가서 import 한 뒤 하단과 같이 <App/> 을 <BrowserRouter>로 감싸줌.
import { BrowserRouter } from "react-router-dom";
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
2. 상단에 여러가지 컴포넌트( Routes, Route, Link )를 import한 뒤
3. <Routes>를 만들고 그 안에 <Route> 작성
: Route는 각각의 페이지를 나타냄.
4. <Route path="/url경로" element={ <보여줄html> } /> 작성
// ( App. js ) 에서
import { Routes, Route, Link } from 'react-router-dom'
function App() {
let [shoes] = useState(data)
return (
<div className='App'>
<Routes>
<Route path="/" element={ <div>메인페이지</div> } />
<Route path="/detail" element={ <div>상세페이지</div> } />
</Routes>
* 페이지 꾸미기
- 메인페이지 꾸미기
: 상품목록과 브랜드 대문사진이 보이게 만들어주기
function App() {
let [shoes] = useState(data)
return (
<div className='App'>
<Navbar bg="dark" variant="dark" className="name">
<Container>
<Navbar.Brand href="#home" className="brandName">LERB</Navbar.Brand>
<Nav className="me-auto">
<Nav.Link href="#home">our story</Nav.Link>
<Nav.Link href="#features">shopping</Nav.Link>
<Nav.Link href="#pricing">contact</Nav.Link>
</Nav>
</Container>
</Navbar>
<Routes>
<Route path="/" element={
<>
<div className='main-bg'></div> {/*대문사진*/}
<div className='container'>
<div className='row'>
{
shoes.map(function(a,i){
return (
<Information shoes={shoes[i]} b={i+1}></Information>
)
})
}
</div>
</div>
</> }/>
<Route path="/detail" element={<div>상세페이지임</div>} />
</Routes>
</div>
);
}
- 페이지를 component로 만들기
function App() {
let [shoes] = useState(data)
return (
<div className='App'>
<Navbar bg="dark" variant="dark" className="name">
<Container>
<Navbar.Brand href="#home" className="brandName">LERB</Navbar.Brand>
<Nav className="me-auto">
<Nav.Link href="#home">our story</Nav.Link>
<Nav.Link href="#features">shopping</Nav.Link>
<Nav.Link href="#pricing">contact</Nav.Link>
</Nav>
</Container>
</Navbar>
<Routes>
<Route path="/" element={ <Main b="i" shoes={shoes}></Main> }/>
<Route path="/detail" element={<Detail></Detail>} />
</Routes>
</div>
);
}
function Information(props) {
return(
<div className='col-md-4'>
<img src={"https://codingapple1.github.io/shop/shoes"+ props.b + ".jpg"} width="80%" />
<h4>{props.shoes.title}</h4>
<p>{props.shoes.price}</p>
</div>
)
}
function Main(props){
return(
<>
<div className='main-bg'></div> {/*대문사진*/}
<div className='container'>
<div className='row'>
{
props.shoes.map(function(a,i){
return (
<Information shoes={props.shoes[i]} b={i+1}></Information>
)
})
}
</div>
</div>
</>
)
}
function Detail() {
return(
<div className="container">
<div className="row">
<div className="col-md-6">
<img src="https://codingapple1.github.io/shop/shoes1.jpg" width="100%" />
</div>
<div className="col-md-6">
<h4 className="pt-5">상품명</h4>
<p>상품설명</p>
<p>120000원</p>
<button className="btn btn-danger">주문하기</button>
</div>
</div>
</div>
)
}
- detail component를 file로 분리하기
src 폴더 밑에 pages 라는 폴더를 만들고 Detail.js라는 파일을 생성함.
그 안에 component안에 있던 내용을 붙여 넣은 뒤 export default Detail; 해주고
app.js 가서 import Detail from './pages/Detail.js'; 입력.
* 페이지 이동 버튼 만들기
: 유저들은 주소창에 url 직접 입력해서 들어가지 않고 링크타고 들어가니까 링크를 만들어줘야됨
react-router-dom에서 Link 컴포넌트 import해오고 원하는 곳에서 <Link> 사용.
import { Link } from 'react-router-dom'
<Link to="/">홈</Link>
<Link to="/detail">상세페이지</Link>
-> 이거 못 생겨서 쓰기 싫으면 useNavigate() 사용하면 됨.
* 새로운 컴퍼넌트 추가
import { Routes, Route, Link, useNavigate, Outlet } from 'react-router-dom'
* useNavigate
: 페이지 이동을 도와주는 함수가 들어있는 hook
let navigate = useNavigate()
// navigate 변수에다가 useNavigate를 저장해두고 나중에 씀
사용할 때는 onClick 했을 때 useNavigate가 저장된 navigate변수를 사용함
navigate('/detail') 이런 코드 실행하면 /detail 페이지로 이동함.
navigate(2) 숫자 넣으면 앞으로 2번 가기
-1 넣으면 뒤로 1번 가기
function App() {
let [shoes] = useState(data)
let navigate = useNavigate();
return (
<div className='App'>
<Navbar bg="dark" variant="dark" className="name">
<Container>
<Navbar.Brand onClick={()=>{ navigate('/') }} className="brandName">LERB</Navbar.Brand>
<Nav className="me-auto">
<Nav.Link href="#home">our story</Nav.Link>
<Nav.Link href="#features">shopping</Nav.Link>
<Nav.Link onClick={()=>{ navigate('/detail')}}>Detail</Nav.Link>
</Nav>
</Container>
</Navbar>
* 404 page
: 잘못된 경로로 접속했을 때 없는 페이지라는 걸 알려주는 페이지
<Route path="*" element={<div>없는 페이지유</div>} />
// path="*" 는 위에 만들어둔 경로 외의 모든 것
* Nested Routes
: 페이지 안에 들어가있는 페이지. 라우터 안에 둥지튼 라우터. 서브경로를 만들 수 있음.
<Route path="/about/member" element={ <div>멤버들</div> } />
<Route path="/about/location" element={ <div>회사위치</div> } />
==
<Route path="/about" element={ <About/> } >
<Route path="member" element={ <div>멤버들</div> } />
<Route path="location" element={ <div>회사위치</div> } />
</Route>
<Route>안에 <Route>를 넣은 것.
/about/member로 접속하면 <About> 과 <div>멤버들</div> 모두가 보임.
두 element 모두 표시할 수 있음.
About 함수 만들기
function About(){
return (
<div>
<h4>about페이지임</h4>
<Outlet></Outlet>
</div>
)
}
<Outlet>은 nested routes안의 element들을 어디에 보여줄지 표기하는 곳
( element 를 둘 다 보여줘야되니까 안에 있는 route가 어디에서 보이면 좋을지 정하는 것)
장점
동적인 UI를 만들 수 있음
뒤로가기 버튼을 이용할 수 있음
* Detail 페이지를 꾸며보자
: app.js에서 props로 받아온 데이터를 사용하여 상품명 데이터를 받아옴.
App.js 안에 <Detail> 쓰는 곳에서 props 전송
// (App.js) 에서
function App() {
let [shoes] = useState(data)
let navigate = useNavigate();
return (
<Routes>
<Route path="/" element={ <Main b="i" shoes={shoes}></Main> }/>
<Route path="/detail" element={<Detail shoes={shoes} />} />
<Route path="*" element={<div>없는 페이짐둥</div>} />
</Routes>
</div>
);
}
Detail.js에서 props로 받아온 state 이용하기
<h4 className="pt-5">상품명</h4>
// 상단을 하단처럼 props로 받아온 데이터 이용
<h4 className="pt-5">{props.shoes[0].title}</h4>
상단의 내용을 하단에 추가하여 완성
// (Detail.js)
import { propTypes } from "react-bootstrap/esm/Image";
function Detail(props) {
return(
<div className="container">
<div className="row">
<div className="col-md-6">
<img src="https://codingapple1.github.io/shop/shoes1.jpg" width="100%" />
</div>
<div className="col-md-6">
<h4 className="pt-5">{props.shoes[0].title}</h4>
<p>상품설명</p>
<p>120000원</p>
<button className="btn btn-danger">주문하기</button>
</div>
</div>
</div>
)
}
export default Detail;
나머지 데이터도 props로 받아온 data를 이용하여 데이터 받아옴.
function Detail(props) {
return(
<div className="container">
<div className="row">
<div className="col-md-6">
<img src="https://codingapple1.github.io/shop/shoes1.jpg" width="100%" />
</div>
<div className="col-md-6">
<h4 className="pt-5">{props.shoes[0].title}</h4>
<p>{props.shoes[0].content}</p>
<p>{props.shoes[0].price}</p>
<button className="btn btn-danger">주문하기</button>
</div>
</div>
</div>
)
}
-> 이렇게 하면 detail페이지에 0번 상품만 나옴.
* URL parameter
: route 여러 개 만들어 페이지를 여러 개 만들 때 사용.
path="/detail/:url파라미터"
그래서 위의 <Route>는 /detail/아무거나 입력하면 <Detail>컴포넌트 보여달라는 뜻.
아무거나 자리에 진짜 말그대로 아무거나 입력해도 항상 <Detail> 컴포넌트를 보여줌.
-> /detail/ 이 뒤에 뭘 입력하든 페이지마다 똑같은 내용을 보여줌
<Route path="/detail/:id" element={<Detail shoes={shoes} />} />
* useParams()
: /:파라미터 자리에 유저가 입력한 값을 가져올 수 있음. 페이지마다 다른 내용을 보여줄 때 사용
- 유저가 파라미터 자리에 입력한 숫자마다 페이지를 다르게 설정
[0] 자리에 [id]를 써서 url에 detail/2 이런 식으로유저가 입력한 숫자를 불러와서 2번 상품을 보여주는 페이지를 설정.
<h4 className="pt-5">{props.shoes[0].title}</h4>
// (Detail.js)
function Detail(props) {
let {id} = useParams();
return(
<div className="container">
<div className="row">
<div className="col-md-6">
<img src="https://codingapple1.github.io/shop/shoes1.jpg" width="100%" />
</div>
<div className="col-md-6">
<h4 className="pt-5">{props.shoes[id].title}</h4>
<p>{props.shoes[id].content}</p>
<p>{props.shoes[id].price}</p>
<button className="btn btn-danger">주문하기</button>
</div>
</div>
</div>
)
}
* Styled-component
설치방법
터미널 열고 - npm install styled-components 입력
Detail.js 에 import
import styled from 'styled-components'