Routing?
라우팅은 기본적으로 url 입력 시 네트워크상에 요청하고 url에 해당하는 페이지를 받아와서 뿌려주는 것을 의미한다.
React-Router 사용하는 이유(feat. CSR)
anchor 태그가 있는데 라우터를 정의하는 이유는?
SPA 사용자 경험 향상의 목적이다.
anchor 태그 사용 시 페이지 전체가 새로 로딩 되면서 화면이 깜빡임이 발생하여 사용자 경험을 떨어뜨린다.
반면, 리액트 라우터에서는 새로운 경로 입력 시 서버에게 요청이 아닌 어플리케이션 내에서 필요한 부분만 변경하면서 보여주고, 필요한 데이터가 있을 때 동적으로 서버에게 요청한다 (페이지는 유지하되 필요한 부분만 해당 컴포넌트를 보여주고 하는 것) → CSR(Client Side Routing)이 가능하다
사용하는 이유는?
기본적으로 리액트는 SPA(Single Page Application)이다. 모든 컴포넌트의 변화가 하나의 페이지 안에서 일어난다.
다른 URL로 이동하면 페이지 전체가 교체되는 것이 아니라 한 페이지 내부에서 컴포넌트의 변화만 일어날 뿐이다. → SPA 하나의 url 하나의 !
이런 환경에서 사용자는 원하는 페이지를 즐겨찾기 할 수도 없고, 히스토리가 생기지도 않아서 뒤로가기가 불가능하다. 또한 새로고침 시 사용자가 보던 페이지가 아닌 처음 페이지로 렌더링된다.
🔥 이러한 단점들이 리액트 라우터를 사용하면 보완될 수 있다.
Install & Using
-
Install
- React로 생성된 SPA 내부에서 페이지 이동이 가능하도록 만들어주는 라이브러리 설치
npm install react-router-dom
- React로 생성된 SPA 내부에서 페이지 이동이 가능하도록 만들어주는 라이브러리 설치
-
Using
- typeScript 적용, 함수형 컴포넌트로 작성
- v6 기준
import { BrowserRouter, Route, Routes } from 'react-router-dom' import Video from './pages/Video' import Home from './pages/Home' const App = () => { return ( <BrowserRouter> <Routes> <Route path="/" element={<Home />} /> <Route path="/video" element={<Video />}/> </Routes> </BrowserRouter> ) } export default App
- BrowserRouter
- history API를 활용해 history 객체를 생성한다.
- history API는 내부적으로 사용자가 방문한 url 기록들을 저장한다.
- 라우팅을 진행할 컴포넌트 상위에 BrowserRouter 컴포넌트를 생성하고 감싸주어야 한다.
- Routes
- 모든 Route의 상위 경로에 존재해야 한다.
- location 변경 시 하위에 있는 모든 Route를 조회해 현재 location과 맞는 Route를 찾아준다.
- Route
- 현재 브라우저의 location(window.href.location 정보를 가져온다) 상태에 따라 맞는 element를 렌더링한다.
- props :
path
(string, url 값),element
(component)
페이지 이동
-
Link
- 라우터 내에서 직접적으로 페이지 이동을 하고자 할 때 사용되는 컴포넌트.
import { Link } from 'react-router-dom' const Navbar = () => { return ( <nav> <Link to='/'>Home</Link> <Link to='/video'>Video</Link> </nav> ) } export default Navbar
-
useNavigate()
- 일반 article 요소를 클릭해서 이동할 때 사용하는 훅.
import { useState } from 'react' import { useLocation, useNavigate } from 'react-router-dom' const Video = () => { const navigate = useNavigate() const handleChange = () => { navigate(`/`) } return ( <div> <button onClick={handleClick}>go to home </button> </div> ) } export default Video
navigate(to, { replace: true })
- default값은 false
- true로 설정한다면 이동 후 뒤로가기가 불가능해진다.
navigate(to, { state })
- state 전달
중첩 라우팅
-
Outlet 컴포넌트 사용
- 라우터 정의 시 자식 태그로 중첩하는 라우터를 명시, 중첩 라우터에서 Outlet 컴포넌트 사용
import { BrowserRouter, Route, Routes } from 'react-router-dom' import VideoDetail from './pages/VideoDetail' import Video from './pages/Video' const App = () => { return ( <BrowserRouter> <Routes> <Route path='/video' element={<Video />}> <Route path=':videoId' element={<VideoDetail />} /> </Route> </Routes> </BrowserRouter> ) } export default App //----------------// //Video.tsx import { Link, Outlet } from 'react-router-dom' const Video = () => { return ( <div> <h1>This is Video List</h1> <ul> <li> <Link to='1'>Video #1</Link> </li> <li> <Link to='2'>Video #2</Link> </li> <li> <Link to='3'>Video #3</Link> </li> <li> <Link to='4'>Video #4</Link> </li> </ul> <Outlet /> </div> ) } export default Video
-
Outlet컴포넌트 사용하지 않고, 바로 작성
-
라우터에서 중첩 라우팅을 하고자 하는 주소에 * 을 추가해주어 중첩 라우팅이 발생할 주소임을 명시한다.
import { BrowserRouter, Route, Routes } from 'react-router-dom' import Video from './pages/Video' import Home from './pages/Home' const App = () => { return ( <BrowserRouter> <Routes> <Route path='/' element={<Home />} /> <Route path='/video/*' element={<Video />} /> </Routes> </BrowserRouter> ) } export default App //----------------// //Video.tsx import { Link, Route, Routes } from 'react-router-dom' import VideoDetail from './VideoDetail' const Video = () => { return ( <div> <h1>This is Video List</h1> <ul> <li> <Link to='1'>Video #1</Link> </li> <li> <Link to='2'>Video #2</Link> </li> <li> <Link to='3'>Video #3</Link> </li> <li> <Link to='4'>Video #4</Link> </li> </ul> <Routes> <Route path=':videoId' element={<VideoDetail />} /> </Routes> </div> ) } export default Video
Params
http://localhost:3000/video/4
→ 4를 얻어올려면?
useParams()
훅을 이용해서 경로에 있는 파라미터 얻어오면 된다
import { useParams } from 'react-router-dom'
const VideoDetail = () => {
const { videoId } = useParams()
return <div>detail : {videoId}</div>
}
export default VideoDetail