https://www.udemy.com/course/react-next-master/
page
routing이란 user가 web browser에 특정 url로 page를 요청하고 browser는 web server에 해당 page를 요청해 화면에 렌더링해준다.
이러한 고전적인 page routing방식을 MPA(multi page application)이라고 한다.
-------------- ------------
| |------/blog 요청-----> | | (index.html)
|Web Browser | |Web server | (blog.html)
| |<-----/blog.html반환-- | | (setting.html)
-------------- -------------
모든 page변경이 이런식으로 동작한다. 그런데 이렇게 MPA방식으로 개발된 웹 어플리케이션은 page routing이 새로운 page를 받아 렌더링하는 방식이기 때문에 매우 비효율적이기도 하고, 사용자 경험에 있어 좋지 않다.
가령, 버튼을 눌러 일부 화면만 바뀐다하더라도 server로부터 새로운 page를 받아와 새로 렌더링해주어야 한다는 것이다. 그렇게되면 page가 깜빡거리면서 새로 리로드되고 사용자는 약간의 시간동안 delay가 생길 수 밖에 없다.
그래서, react는 SPA(single page application)방식으로 하나의 page로 모든 것을 해결하도록 한다.
--------------Web Browser----------------------
| |------React Web----------------------| |
| | - index.html | |
| | - React Web js.files(ex, App.jsx) | |
| |-------------------------------------| |
------------------------------------------------
즉, index.html
파일 하나만 web browser에 전달되고, 사용자의 요청에 따라 js파일에 맞게 index.html
의 component들을 변경하는 것이다. 이때 우리가 만든 js파일들을 하나의 js파일로 번들링하여 전달한다. 이 번들링에 쓰이는 도구가 바로 vite이다.
처음 user가 요청을 보내면 다음의 code가 실행되는 것이다.
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'
ReactDOM.createRoot(document.getElementById('root')).render(
<App />
)
index.html
이 보내지고 root
id를 가진 tag 아래로 react component들을 계속 렌더링하는 것이다.
때문에 이제 server를 통해서 user가 요청을 보내고 응답을 받는 것이 아니라, user가 버튼을 누르면 browser내부의 js파일이 실행되어 component를 리렌더링해주는 것이다.
따라서, /blog
에서 /setting
으로 페이지를 이동시키려고 한다면, MPA
방식에서는 blog.html
에서 setting.html
파일을 전달하여 리렌더링한다면, SPA
(single page rendeing)인 react에서는 다음과 같이, 일부 component만 js파일을 통해 리렌더링되는 것이다.
----------------- -----------------
| -------- | | -------- |
| |Header| | ------> | |Header| |
| -------- | | -------- |
| ------ | | --------- |
| |blog| | | |Setting| |
| ------ | | --------- |
----------------- -----------------
이렇게 server를 통해서 page를 받고, browser에서 렌더링이 이루어지는 것이 아니라, web browser자체에서 js파일을 통해 렌더링이 이루어지는 것을 CSR
(client side rendering)이라고 한다. 그래서 새로운 page를 렌더링하지 않기 때문에, 깜빡임 현생없이 page가 이동되는 것처럼 보이기 때문에 사용자 경험이 매우 훌륭하다.
react는 CSR를 통해서 마치 page가 교체되는 것처럼 component들을 변경하여 새로운 page를 렌더링한다고 했다. 그러나, code르 개발하고 project를 관리하는 입장에서는 page단위로 code를 나누어 component를 분리하는 것이 매우 좋은데, 이를 가능하게 해주는 것이 바로, react-router
이다. react-router
는 별도의 react library로써 사용자가 마치 page를 바꾸어 다른 page로 넘어가는 것처럼 보이게 하는 기능을 제공한다. 또한, 개발자 역시도 여러 개의 page를 바꾸어 application을 구동하는 것처럼 보이도록 도와준다.
다음의 링크를 통해서 자세한 내용을 볼 수 있다. https://reactrouter.com/en/main
다음의 명령어를 통해서 설치가 가능하다.
npm install react-router-dom
react-router-dom
의 BrowserRouter
component의 child에 있는 component들은 현재의 browser 주소와 정보들을 알 수 있다. 따라서 모든 component들에 적용시켜주기 위해서 main.jsx
의 App
부모 component로 만들어주도록 하자.
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'
import { BrowserRouter } from 'react-router-dom'
ReactDOM.createRoot(document.getElementById('root')).render(
<BrowserRouter>
<App />
</BrowserRouter>
)
사실, BrowserRouter
component는 하나의 Context
API로 보면 된다. browser url에 필요한 정보들을 context로 담고 있고, 이를 component들이 사용할 수 있도록 하는 것이 전부이다.
react
에서 page를 관리하는 방법의 관례로는 src
아래에 pages
라는 directory를 만들어 jsx파일을 만들면 된다.
./src/
├── App.css
├── App.jsx
├── assets
├── index.css
├── main.jsx
└── pages
├── Country.jsx
├── Home.jsx
├── NotFound.jsx
└── Search.jsx
이런 식이 된다. 여기서는 Home
, Country
, Search
, NotFound
page component들이 있는 것이다. page component들의 내부 구현은 일반 react component들과 별반 다를 바 없다.
export default function Home() {
return <div>Home</div>
}
다른 page component들도 이름과 return
value만 다르고 나머지는 전부 동일하다.
이제 react-router-dom
을 활용하여 원하는 url에 우리의 page component가 렌더링되도록 만들어보자.
import './App.css'
import { Routes, Route } from 'react-router-dom'
import Home from './pages/Home'
import Search from './pages/Search'
import Country from './pages/Country'
import NotFound from './pages/NotFound'
function App() {
return (
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/search" element={<Search/>}/>
<Route path="/country" element={<Country/>}/>
<Route path="*" element={<NotFound/>}/>
</Routes>
)
}
export default App
원하는 url
로 우리의 component를 렌더링하기 위해서는 Routes
와 Route
가 필요하다. Routes
안에 Route
가 있는 형식인데, Routes
는 하나의 Route
그룹을 하기 위해 존재한다고 생각하면 된다. 가령, prefix를 만들거나 할 때 사용된다.
Route
안에는 path
attribute로 원하는 url를 쓰고 element
에 해당 url로 user가 접근 시 어떤 page component를 보여줄 지 쓰면 된다. 참고로 와일드 카드인 *
는 맨 마지막에 쓸 경우, 위에서 일치하는 page가 없을 경우 무조건 실행된다. 따라서, 우리가 정의하지 않은 url에 접근하게 되면 NotFound
page component가 렌더링된다.
만약 header bar같은 것이 있어서, home
page ,search
page, country
page로 이동할 수 있도록 하고싶다면 어떻게 해야할까?? 즉, <a>
태그처럼 클릭하면 정해진 url
이 실행되어 해당 page를 가져오도록 하고 싶다면 어떻게해야할까??
<a>
tag를 사용해서 호출해도 된다. 즉, a
태그로 /search
, /
, /country
에 접속하도록 할 수 있지만, 이는 CSR에 최적화된 방식이 아니기 때문에 버튼을 누르면 깜빡거리는 현상이 생기는 것을 알 수 있다.
이를 해결해주는 것이 바로 react-router-dom
의 Link
으로 Link
를 사용하면 CSR
방식에 최적화된 routing을 도움 받을 수 있다. 즉, a
태그는 실제 server에 browser가 요청을 보내지만, Link
는 이것이 react임을 알고 browser자체에서만 이를 해결하도록 한다.
import './App.css'
import { Routes, Route, Link } from 'react-router-dom'
import Home from './pages/Home'
import Search from './pages/Search'
import Country from './pages/Country'
import NotFound from './pages/NotFound'
function App() {
return (
<div>
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/search" element={<Search/>}/>
<Route path="/country" element={<Country/>}/>
<Route path="*" element={<NotFound/>}/>
</Routes>
<Link to={"/"}>Home</Link>
<Link to={"/search"}>Search</Link>
<Link to={"/country"}>Country</Link>
</div>
)
}
export default App
사용 방법은 매우 단순하다. Link
에 to
로 이동할 url를 넣어주기만 하면 된다. 해당 tag를 a
태그로 바꾸어 사용해도 문제없이 동작하지만, 화면이 깜빡거리는 현상을 피할 수 없을 것이다.
추가적으로 Link
와 같은 react-router-dom component말고 기본 html tag에 CSR routing을 지원하도록 하는 함수를 넣을 수 있다. 이는 react-router-dom의 useNavigate
hook을 통해서 사용할 수 있다.
import './App.css'
import { Routes, Route, Link, useNavigate } from 'react-router-dom'
import Home from './pages/Home'
import Search from './pages/Search'
import Country from './pages/Country'
import NotFound from './pages/NotFound'
function App() {
const nav = useNavigate()
const onClick = () => {
nav("/search")
}
return (
<div>
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/search" element={<Search/>}/>
<Route path="/country" element={<Country/>}/>
<Route path="*" element={<NotFound/>}/>
</Routes>
<Link to={"/"}>Home</Link>
<Link to={"/search"}>Search</Link>
<Link to={"/country"}>Country</Link>
<button onClick={onClick}>서치 페이지로 이동</button>
</div>
)
}
export default App
사용방법 역시도 매우 단순하다. useNavigate
를 사용하여 nav
함수를 하나 반환하는데, 이 함수의 첫번째 인자로 url
을 입력하면 해당 url
로 CSR routing을 해준다. 따라서 일반 html tag도 깜빡거림 현상없이 page를 이동시킬 수 있는 것이다.
/search?q=united
와 같은 path로 요청을 하고싶다면 어떻게해야할까? 즉, query string를 주고싶을 때는 어떻게해야할까?
또한, /country/{country-name}
처럼 나라이름에 따라 경로 일부분이 바뀌는 path parameter(URL parameter)를 어떻게해야할까?
이를 dynamic path
라고 한다.
react-router역시도 dynamic routing을 지원한다.
먼저 query string
을 사용하는 방법에 대해서 알아보자. 굉장히 간단한데, 의외로 별다른 설정없이 react component를 통해 직접 사용하면 된다.
import { useSearchParams } from "react-router-dom"
export default function Search() {
const [searchParams, setSearchParams] = useSearchParams()
return <div>Search {searchParams.get("q")}</div>
}
react-router-dom의 useSearchParams
을 사용하면 두개의 반환값을 내는데, 첫번째는 query string값을 담은 객체이고, 두번째는 query string값을 변경하는 setter함수이다. 사실 유저가 입력한 query string을 바꾸는 일은 거의없기 때문에 setter인 setSearchParams
는 거의 사용하지 않는다.
searchParams
객체에 .get
method를 통해서 query string 값을 가져올 수 있는데, 첫번째 인자로 key값을 입력하면 된다. 만약 우리가 /search?q=kor
이라고 url에 입력했다면 q
가 key가 되고 kor
이 value가 되어 searchParams
get으로 얻어올 수 있다.
이제 URL parameter인 path parameter를 설정하는 방법이다. path parameter는 Route
를 수정해하여 path parameter가 쓰이는 것을 표시해주어야 한다. 가령 /country/KOR
에서 KOR
이 동적으로 바뀌는 path parameter라고 하자. 따라서, /country/KOR
, /country/JPN
등으로 변할 수 있는데, 시스템은 이 path parameter와 고정으로 path를 지정한 /country/KOR
, /country/JPN
path를 구분할 수 없다.
따라서, :
를 통해서 해당 부분은 path parameter라는 것을 표시해주어야 한다.
function App() {
const nav = useNavigate()
const onClick = () => {
nav("/search")
}
return (
<div>
<Routes>
<Route path="/" element={<Home/>}/>
<Route path="/search" element={<Search/>}/>
<Route path="/country/:code" element={<Country/>}/>
<Route path="*" element={<NotFound/>}/>
</Routes>
<Link to={"/"}>Home</Link>
<Link to={"/search"}>Search</Link>
<Link to={"/country"}>Country</Link>
<button onClick={onClick}>서치 페이지로 이동</button>
</div>
)
}
export default App
<Route path="/country/:code" element={<Country/>}/>
처럼 뒤에 :code
로 표시해두면 :
뒤가 path parameter라는 것이 확정이 되어 system도 이를 인식할 수 있다. 만약 :code
를 안써주고 /country/KOR
에 접속하면 Not Found
가 나올 것이다.
다음으로, component에서 path parameter를 가져오는 방법인데 react-router-dom의 useParam
을 사용하면 된다.
import { useParams } from "react-router-dom"
export default function Country() {
const params = useParams()
console.log(params)
return <div>Country</div>
}
useParams
를 사용하면 path parameter들이 담긴 object를 반환하고, 이 object의 key
값에 data가 저장된다. 가령, /country/:code
에서 code
key로 path parameter data가 들어가는 것이다.
만약 /country/KOR
로 요청을 보내면 code
path parameter는 KOR
이 되어, 다음의 log가 찍힐 것이다.
{code: 'KOR'}