✅ react-router 기본개념 및 설치
✅ navigate,nested routes, outlet
✅ react-router로 제품 상세페이지 만들기
react-router는 왜 사용하는가?
- react-router는 싱글페이지 어플리케이션인 리액트의 장점을 편리하게 만들어주는 라이브러리이다.
- 각각의 페이지를 구분하고 이동할 때 도움을 준다.
- react-router에는 코어를 모두 포함하고 있고, v4 버전 이후에는 웹 개발자용 react-router-dom과 앱개발자용 react-router-native로 나뉘어서 릴리즈 되고 있다.
- 현재 웹을 개발 하고 있기 때문에 react-router-dom을 설치하고 진행할 예정
react-router-dom 기본 설정
import { BrowserRouter } from "react-router-dom"; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <Provider store = {store}> <React.StrictMode> <BrowserRouter> <App /> </BrowserRouter> </React.StrictMode> </Provider> );
- index.js 파일에 를 선언하고 앱을 감싸고 import를 해주면 된다.
Routes,Route만들기
import {Routes, Route, Link} from 'react-router-dom' <ComponentNavbar/> <Routes> <Route path="/" element={}/> <Route path="/detail" element={<div>상세페이지임</div>}/> <Route path="/about" element={<About/>}> </Routes>
- 부모 요소에 Routes를 선언하고, Route라는 컴포넌트를 만들면 새로운 페이지가 만들어짐
- path는 해당 홈페이지의 경로를 이야기하고, element는 해당 홈페이지에 들어가면 보여질 컴포넌트를 이야기함
- 메인페이지는 "/"하나만 있으면 메인페이지임
- 만약 라우터로 페이지를 이동해도 navbar처럼 계속 남아있어야하는 컴포넌트가 있다면 Route위나 아래에 독립적으로 넣어주면 됨.
- 페이지도 컴포넌트를 만들면 좋음.
Link 태그로 이동하기
import {Routes, Route, Link} from 'react-router-dom' <ComponentNavbar/> <Link to ="/">홈</Link> <Link to ="/detail">상세페이지</Link> <Routes> <Route path="/" element={}/> <Route path="/detail" element={<div>상세페이지임</div>}/> <Route path="/about" element={<About/>}> </Routes>
- 위와 같이 Link를 만들면 해당 페이지로 클릭하면 넘어갈 수 있는 버튼이 만들어짐
navbar에 Link 적용하기(useNavigate)
import {useNavigate,Outlet,useParams} from "react-router-dom"; export default function ComponentNavbar() { let navigate = useNavigate(); <Nav.Link onClick={() => {navigate("/");}}>홈</Nav.Link> <Nav.Link onClick={() => {navigate("/detail");}}>상세페이지</Nav.Link> <Nav.Link onClick={() => {navigate("/cart");}}>장바구니</Nav.Link> <Nav.Link onClick={() => {navigate(-1);}}>이전페이지</Nav.Link> <Nav.Link onClick={() => {navigate(-1);}}>이전페이지</Nav.Link>
- navigate를 사용하고 싶은 컴포넌트 내에서 useNavigate훅을 import하고 함수 안에서 해당 훅을 선언함.
- Nav 엘리먼트에서 onClick 함수 만들어서 선언한 훅에 위와같이 해당 홈페이지 경로를 작성하면 해당 홈페이지로 이동하게 됨
- 이전페이지로 이동하고 싶다면, -1이라는 경로를 만들어주면 이동하게 됨.
404 페이지 만들기
<Route path="*" element={<div>없는페이지요</div>}/>
- 부모컴포넌트에서 Routes안에 route path로 "*" 을 입력하면 위에 path에 적힌 페이지 외의 모든 것을 이야기함.
- 따로 path를 한 페이지 외에는 따로 만들어둔 것이 없기 때문에 해당 페이지에는 404페이지 관련 안내를 적을 수 있음.
import { Navigate } from "react-router-dom"; <Route path="/hi" element={<Hi />} /> <Route path="*" element={<Navigate to="/hi" replace />} />
- 404페이지에서 특정 홈페이지 주소로 넘어가고 싶다면 Navigate to를 사용할 수 있음.
- 만약 404페이지에 갔다가 뒤로가기버튼을 눌러서 초기 페이지로 이동을 하고 싶다면 replace를 꼭 사용해야함
nested Route와 Outlet
<Route path="/about" element={<About/>}> <Route path="member" element={<div>멤버임</div>}/> <Route path="location" element={<div>로케이션임</div>}/> </Route> <Route path="/about" element={<About/>}> <Route path="/about/member" element={<div>멤버임</div>}/> <Route path="/about/location" element={<div>로케이션임</div>}/> </Route>
- 만약 Route안에 Route를 만들어서 상세 페이지나 배너같은 것들을 보여주고 닫고 하고 싶다면?
- 아래에 있는 Route와 위에 있는 Route path는 같다. 즉, 해당 컴포넌트안에 종속되는 컴포넌트를 만들고 싶을 때는 path를 따로 만들어줄 수 있지만, route의 자식컴포넌트로 만들어주면 간단하게 만들 수 있음
- 이렇게 컴포넌트에 자식요소로 만들어주는 것을 nested Route라고 부른다.
Outlet
import { Outlet } from 'react-router-dom' <Route path="/about" element={<About/>}> <Route path="member" element={<div>멤버임</div>}/> <Route path="location" element={<div>로케이션임</div>}/> </Route> <Route path="/about" element={<About/>}> <Route path="/about/member" element={<div>멤버임</div>}/> <Route path="/about/location" element={<div>로케이션임</div>}/> </Route> const About = () => { return ( <div> <h4>회사정보임</h4> <Outlet></Outlet> </div> ) }
- 그런데 만약에 nested Route에 부모 element와 자식 element를 같이 보여주고 싶을 때는 어떻게 할 수 있을까?
- 해당 기능을 간편하게 해주는 것이 바로 Outlet이다. 부모 엘리먼트에 outlet을 적어주면 해당 자식 컴포넌트들이 보이게 됨
- 따라서 nested Route는 모달창을 보여주거나 탭 ui를 만드는 등 새로운페이지인데 일부 요소만 바뀐 페이지를 기획할 때 사용할 수 있음.
const About = () => { let navigate = useNavigate() return ( <div> <h4>회사정보임</h4> <div onClick={()=>{navigate("/about/member");}}> 멤버 보여주세요</div> <div onClick={() => {navigate("/about/location");}}> 로케이션 보여주세요</div> <Outlet></Outlet> </div> ) }
- 위와 같이 어떤 버튼을 클릭시 보여주려면 h4에 onclick navigate 설정해서 하위 페이지로 이동 후, outlet을 부모요소에 설정을 해주면 해당 페이지를 열었다 닫았다 할 수 있음.
props 활용한 상세페이지 만들기
<Route path="/detail/:id" element={<Detail product={product}/>} /> unction Detail(props) { return ( <img src={process.env.PUBLIC_URL + `${props.product[0].image}`}></img> <div>{props.product[0].price}</div> <div>{props.product[0].title}</div> <button>결제하기</button> ) }
- 이렇게 props를 활용하면 해당 내용의 상세페이지를 불러올 수 있음
- 근데 여기서 문제는 상세페이지 각각의 데이터를 눌렀을 때마다 바뀌게 해주는 기능을 추가해야만 100개가 넘는 상세페이지를 만들더라고 간편하게 만들 수 있음
페이지를 여러개 만드는 방법(useParams)
- 이런 문제를 해결하기 위해서는 상세페이지를 만드는 컴포넌트에서 각 항목마다의 id 값을 불러와서 적용할 수 있어야함
- 이렇게 하기 위해서는 아래와 같은 절차로 useParams를 이용해서 id 값을 불러올 수 있다.
부모 루트에 :id를 부여하기
<Route path="/detail/:id" element={<Detail product={product}/>} />
- 부모 루트에 path로 id를 설정하게 되면 detail뒤에 어떤 변수가 와도 상세페이지로 넘어가게 된다.
- 그렇다면 무한개의 상세페이지는 만들어진 셈이지만, 모두 동일한 상세내용이 나올 것
- 이후에는 id 값에 맞춰서 데이터를 넣어줘야함.
해당 id에 맞게 데이터 넣어주기
import { useParams } from "react-router-dom"; export default function Detail(props) { let { id } = useParams(); if (id < props.product.length) { return ( <> <img src={process.env.PUBLIC_URL + `${props.product[id].image}`} width={"50%"} </img> <div>{props.product[id].price}</div> <div>{props.product[id].title}</div> <button>결제하기</button> </> ); } else { return <div>잘못된 페이지입니다</div>; } }
- 해당 컴포넌트에서 useParams를 import하고 id를 hook을 통해 선언해주면, 해당 통해 id 불러오기
- 예를 들어 /detail/0 하면 0번째 데이터, /detail/1하면 1번째 데이터가 들어가게끔 해주면 됨
- 이를 props를 통해 구현해주면 됨.
- 이 때, /detail/304 나 /detail/아무문자 이런식으로 데이터가 없는 페이지로 이동되었을 때 특정값을 보여주고 싶다면 if문을 통해서 return을 해주면 됨
-아래에서 보듯이 url에 따라 다른 데이터들이 들어감
제품 컴포넌트에 click이벤트 부여하기
- 자 그러면 모두 준비가 되었으니, 이제 제품을 클릭했을 때 해당 url로 넘어가기만 하면 끝남.
- 이를 위해서는 useNavigate를 통해서 해당 이미지에 온클릭 이벤트를 넣어주면 됨.
import { useNavigate } from "react-router-dom"; export default function Product(props) { let navigate = useNavigate(); const cardTotal = props.product.map((a, i) => { return ( <Col id="i" key={i}> <img src={process.env.PUBLIC_URL + `${props.product[i].image}`} width={"80%"} onClick={() => { navigate(`/detail/${i}`); }} </img> <h4>{props.product[i].title}</h4> <p>{props.product[i].content}</p> </Col> ); }); return ( <Container> <Row>{cardTotal}</Row> </Container> ); }
- 위와 같이 맵 함수로 제품 컴포넌트들이 자동으로 만들어지는데 이 때 이미지에 온클릭이벤트로 navigate(
/detail/${i}
)를 통해서 각 이미지 값에 고유한 onClick을 부여할 수 있음
데이터가 바뀌어도 id로 해당 데이터를 끌어오려면?
let { id } = useParams(); let 찾은상품 = props.shoes.find(function(x){ return x.id == id }); 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 mt-4"> <h4 className="pt-5">{찾은상품.title}</h4> <p>{찾은상품.content}</p> <p>{찾은상품.price}원</p> <button className="btn btn-danger">주문하기</button> </div> </div> </div> ) };