React Router?
SPA의 routing문제를 해결하기 위해 사용되는 네비게이션 라이브러리이다.
React Router를 사용하면 앱에서 발생하는 라우팅이 location이나 history와 같은 브라우저 내장 api와 완벽하게 연동이 된다.
> setting
npm install react-router-dom
<BrowserRouter> vs <HashRouter>
: HashRoutersms URL 맨뒤에 /#/이 붙은채로 시작한다.
왜?
원래 브라우저 주소창에 페이지 입력하면 서버한테 특정 페이지좀 보여달라는 요청이다.
근데 우리는 서버없고 그냥 리액트가 라우팅을 담당하고 있으므로, 존재않는 페이지 서버한테 요청해서 에러뜰 수 있다.
안전빵으로 # 붙여준다. #뒤에 붙은것들은 서버로 요청안되니까!
//index.js
import { BrowserRouter } from 'react-router-dom';
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<App/>
</BrowserRouter>
</React.StrictMode>
document.getElementById('root')
);
React Router 핵심 컴포넌트
Link
: <a>와 유사 기능 컴포넌트이다. <a>는 href로 경로를 지정하는 반면 <Link>sms to prop을 통해 경로를 지정한다.
클릭하면 '도메인네임/지정경로'로 갱신된다.
<Link to='/ex'> ex </Link>
Route
: 현재 주소창의 경로와 매치될 경우 보여줄 컴포넌트를 지정하는데 사용된다.
path를 통해서 매치시킬 경로를 지정하고 component prop을 통해서 매치되었을 때 보여줄 컴포넌트를 할당한다.
<Route path="/ex" component={Ex} />
Router
: <Link>와 <Route>가 함게 유기적으로 동작하도록 묶어주는데 사용한다.
<Link>와 <Route>는 DOM트리 상에서 항상 <Router>를 공통상위 컴포넌트로 가져야한다.
<Router>
<Link />
<Route />
</Router>
Routing 예시
import { Route } from "react-router-dom";
/* /datail이라고 적으면 /라는 경로도 포함되서 메인도 보여진다. exact붙여서 해결해준다. */
<Route exact path="/">
<div>메인페이지</div>
</Route>
<Route path="/detail">
<div>상세페이지</div>
</Route>
// 어쩌구 경로로 가면 card컴포넌트를 보여준다.(둘다 같은코드)
<Route path="/어쩌구" component={Card} ></Route>
<Route path="/어쩌구"> <Card/> </Route>
react-router는 각 페이지마다 다른 html을 보여주는게 아니라,
html을 싹 갈아엎어서 다른페이지처럼 보여지게 하는것이다.
Link, Switch, useHistory 페이지 이동
> Link 태그는 새로고침없이 페이지를 바꿔준다.
import { Link, Route, Switch } from "react-router-dom";
function App() {
return (
<Link to="/">home</Link>
....
> NavLink
현재 경로와 Link에서 사용하는 경로가 일치하는 경우 특정 스타일을 적용할 수 있다.
링크가 활성화 되었을 때, activeStyle값을 props로 넣어주면 된다. (css class 적용시 activeClassName)
const activeStyle = {
background : 'black',
color: 'white'
}
return (
...
<NavLink to="/경로" activeStyel={activeStyle} />
...
)
> 이동함수 useHistory 함수 import해서 사용해서 뒤로가기버튼 만들어보자.
// 1. import하고 변수에 함수를 저장한다. 변수에 큰object{}자료가 저장된다.
import {useHistory} form 'react-router-dom';
function App() {
let history= useHistory();
...
//history 저장된 자료중 goBack함수를 활용한다.
<button onClick={() => {history.goBack();}} >
뒤로가기
</button>
> 커스텀페이지로 이동하는 기능은 push()함수를 사용한다.
//push(/가고싶은경로)
<button onClick={() => {history.push("/")}} >
뒤로가기
</button>
> 매치되는 <Route> 전부 보여주지 말고 한번에 하나만 보고싶을 때 <Switch>를 쓴다.
/* /detail로 이동하면 <Detail>이랑 <div>hello</div> 두개 다 보여진다. */
function App(){
return (
<div>
<Route exact path="/">
메인
</Route>
<Route path="/detail">
<Detail/>
</Route>
<Route path="/:id">
<div>hello</div>
</Route>
</div>
)
}
/* <Switch>로 감싸주면 Route가 여러개 매칭되어도 맨위의 Route하나만 보여준다.
exact쓰지않고 해결가능하다. */
function App(){
return (
<Switch>
<Route exact path="/">
어쩌구
</Route>
<Route path="/detail">
<Detail/>
</Route>
<Route path="/:id">
<div>새로 만든 route입니다</div>
</Route>
</Switch>
)
}
URL 파라미터 이용해서 여러 상세페이지 만들기
//App.js 파일에서 상세페이지 3개 만들고 싶어서 복사해서 만들어보았다.
function App(){
return (
<div>
<나머지HTML/>
<Route path="/detail/0">
<Detail shoes={shoes}/>
</Route>
<Route path="/detail/1">
<Detail shoes={shoes}/>
</Route>
<Route path="/detail/2">
<Detail shoes={shoes}/>
</Route>
</div>
> 반복적인부분 반복문이아니라 URL 파라미터 문법으로 축약시켜준다.
:id 자리에 아무 문자나 입력하면 <Detail>컴포넌트 보여준다.
/detail/:id/:name 파라미터 몇개든 추가가능하다.
function App(){
return (
<div>
<나머지HTML/>
<Route path="/detail/:id">
<Detail shoes={shoes}/>
</Route>
</div>
)
}
> 각 URL 접속시 상품명을 다르게 보여주고 싶을 땐 useParams 을 사용한다.
useParams()는 현재 url에 적힌 모든 파라미터를 {파라미터1, 파라미터2, ....} 로 저장해주는 함수이다.
그것을 destructuring문법을 이용해서 따로 변수로 빼서 저장한 것이다.
( /detail/100 으로 접속하면 id라는 변수는 100이 되는 것이다.)
// useparams를 import로 가져오고 변수에 저장한다.
import { useHistory, useParams } from 'react-router-dom';
function Detail(props){
let { id } = useParams();
return (
...
<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>
...
> 데이터의 순서가 변경과 상관없이 페이지를 보여주고 싶다면?
상품의 영구번호, 즉 id값으로 데이터를 불러오고 싶다면
find(), filter() 등을 사용한다.
function Detail(props) {
let { id } = useParams();
//find는 array뒤에 붙이고, 콜백함수가 따라온다.
let 찾은상품 = props.shoes.find((상품) => {
//콜백함수내의 파라미터는(상품) array안의 하나하나의 데이터를 의미한다.
return 상품.id == id;
//return 다음에는 조건식, 이게 참인 데이터만 새로운 변수에 저장한다.
//현재 url의 /:id에 적힌갑소가 상품의 id가 같은지 비교하는 조건식이다.
});
Nested Routing 상세
중첩라우팅이란 라우팅 맵핑을 최상위 컴포넌트뿐만 아니라 여러개의 컴포넌트에 걸쳐서 단계별로 정의하는 라우팅 기법이다.
Route props
중첩라우팅을 구현하려면 <Route> 컴포넌트의 componet prop으로 넘어온 컴포넌트에 prop으로 어떤 값들이 넘어오는지 대해 알아야한다.
<Router>
<Route path="/about" component={About} />
</Router>
react router는 match, lacation, history라는 3개의 prop을 <about>컴포넌트에 넘겨준다.
<About>컴포넌트에서는 이 3개의 prop을 읽어서 각 객체가 어떤 데이터를 담고있는지 렌더링해 볼 수 있다.
import React from "react";
function About({ match, location, history }) {
return (
<>
<h1>About</h1>
<pre>{JSON.stringify(match, null, 2)}</pre>
<pre>{JSON.stringify(location, null, 2)}</pre>
<pre>{JSON.stringify(history, null, 2)}</pre>
</>
);
}
export default About;
중첩 라우팅 구현에는 매칭정보를 담고있는 match props가 사용된다.
match.url은 <Link> 컴포넌트를 위해 사용되고,
match.path는 <Route> 컴포넌트를 위해 사용된다.
match.url은 실제로 매칭된 url 문자열 ( ex. /articles/1 )을 담고있고,
match.path은 매칭에 사용된 경로의 패턴 ( ex. /articles/:id )을 담고있다.
> 중첩라우팅 구현
/users : 유저목록 페이지
/users/유저아이디 : 유저상세 페이지
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/users" component={Users} />
<Route component={NotFound} />
</Switch>
/users경로에 컴포넌트 맵핑하고 컴포넌트 내부에서 /users의 하위 경로에 대한 라우팅을 해준다.
import React from "react";
import { Route } from "react-router-dom";
import UserList from "./UserList";
import UserDetail from "./UserDetail";
function Users({ match }) {
return (
<>
<h1>Users</h1>
<Route exact path={match.path} component={UserList} />
<Route path={`${match.path}/:id`} component={UserDetail} />
</>
);
}
export default Users;
Users 컴포넌트를 작성한다.
Users 컴포넌트는 <Route>의 component prop인자로 넘어갔기 때문에 match, history, location 3개의 porps를 가지고 있다.
이 중 match.path값을 사용한다.
첫 번째 <Route>컴포넌트는 /users경로에 {userlist}를 mapping하고,
두 번째는 /users/:id경로에 {userdetail} 컴포넌트를 mapping한다.
첫 번째 exact는 /users경로를 정확히 매칭하기위해서 사용한다.
exact 없을경우 /users로 시작하는 모든 경로가 매칭되서 userdetail페이지 표시될때 userlist도 표시된다.
:id는 url파라미터 정의할 때 사용하는 react router의 문법이다.
해당 파라미터는 변수화되어 맵핑된 컴포넌트에서 match.params.id로 읽어올 수 있다.
예제)
하위 UserList component에서 Link 사용
import React from "react";
import { Link } from "react-router-dom";
import { users } from "./data.json";
function UserList({ match }) {
return (
<>
<h2>User List</h2>
<ul>
{users.map(({ id, name }) => (
<li key={id}>
<Link to={`${match.url}/${id}`}>{name}</Link>
</li>
))}
</ul>
</>
);
}
export default UserList;
이동할경로는 match.url뒤에 각 유저의 id를 붙여서 <Link>컴포넌트의 to prop에 넘겨준다.
match.path 대신 match.url을 사용하는 이유는 링크를 걸 때 경로 문자열아닌 경로 패턴을 사용하면 url 파라미터가 포함될 수 있기 때문이다.
하위 UserDetail component에서 history prop 사용
import React from "react";
import { users } from "./data.json";
function UserDetail({ match, history }) {
const user = users.find((user) => user.id === match.params.id);
return (
<>
<h2>User Detail</h2>
<dt>id</dt>
<dd>{user.id}</dd>
<dt>name</dt>
<dd>{user.name}</dd>
<button onClick={() => history.goBack()}>Back</button>
</>
);
}
export default UserDetail;
match.params를 통해 경로에 포함되어있는 url 파라미터를 읽어 온다.
( users/1일 경우, match.params에 {id: "1"}이 할당되어서 match.params.id값은 1이되며,
이 값으로 유저를 조회하여 상세정보를 렌더링한다.
유저 목록페이지로 돌아가는 버튼에는 history prop의 goback()함수를 사용한다.
history는 브라우저의 이력정보와 관련 유틸리티 함수를 가지고있다.
404페이지
브라우저에 잘못된 경로가 입력되었을 때, 특정한 404페이지 보여줘야한다.
<Switch>로 모든 <Route> 컴포넌트를 묶어준다.
<Switch>를 사용하면 그 하위에 있는 <Route>컴포넌트 중에 매치되는 제일 첫번째 컴포넌트만 보여주고,
그 이후에 나오는 Route컴포넌트는 매치되더라도 무시된다.
path에 prop이 없는 <Route>컴포넌트 하나를 추가해주면, 이 <Route>는 모든경로에 매치가 가능해지고, 여기에 404를 할당해준다.
그러면 위에있는 Route중 매치된는 것이 없을경우 제일아래까지 내려오고,
이 마지막 <Route>컴포넌트가 매치되어 404페이지가 보여질 것이다.
<main>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route component={NotFound} />
</Switch>
</main>
'Archive' 카테고리의 다른 글
[React] useEffect / useReducer (0) | 2021.10.24 |
---|---|
[React] styled-components / SASS / CSS Module (0) | 2021.10.24 |
[VSC] Prettier 적용방법 (0) | 2021.10.16 |
[DB] Prisma 기초 (0) | 2021.10.10 |
[DB] Prisma (0) | 2021.10.10 |