라우팅은 기본적으로 네트워크에서 경로를 선택하는 프로세스를 의미한다.
조금 풀어서 얘기하면, 다양한 주소의 요청이 들어왔을 때 각각 맞는 콘텐츠로 이동시켜 주는 작업이라고 볼 수 있다. 마치 우체국에서 편지를 집 주소에 맞게 배달하는 것처럼!!
출처: 생활코딩 블로그
그냥 a 태그가 있으니 요거로 이동해도 무방한데
굳이 라우터를 따로 정의하고 사용하는 이유가 있나?
-> SPA 사용자 경험 향상의 목적!!
그냥 a 태그를 사용하면 페이지 전체가 새로 로딩된다.
흔히 말하는 화면 깜빡임이 필수적으로 발생하고 이는 사용자 경험을 떨어뜨리는 큰 요인이다..
따라서 라우팅을 통해 부드러운 화면전환을 꾀하는 것!!!
그러면 그냥 SPA 안에서 모든 페이지를 다 렌더링해주는 형식으로 만들면 안되는건가?
이렇게 하면 아래와 같은 다양한 문제가 발생한다...
특정 페이지 즐겨찾기 등록 불가 -> 화면 전환이 되어도 url 은 고정되어 있기 때문에... 내가 원하는 페이지를 특정할 수 없다.
뒤로가기 불가 -> 마찬가지의 이유. 해당 SPA 하나에 url 하나이기 때문에 뒤로 가기를 누르면 이전에 보던 다른 웹사이트로 이동하게 된다.
새로고침 불가 -> 이 또한 마찬가지! 새로고침을 누를 시 맨 처음의 렌더링 페이지로 이동하게 된다...내가 보던 페이지가 아닌 처음의 페이지가 나온다!
SEO -> 검색 엔진에 의해 원치 않는 방식으로 색인될 가능성이 있다...
URL의 해쉬(#)값을 이용하는 라우터
다음과 같은 특징을 가진다.
주소에 해쉬(#)가 붙는다.
검색 엔진이 읽지 못한다...
별도의 서버 설정을 하지 않더라도 새로고침 시 오류가 발생하지 않는다. 이는 해시 라우터가 해쉬(#) 뒤의 값은 브라우저에서만 관리하고(라우팅하는 사실을 서버가 모름) 서버는 기본 url로 서버에 데이터를 요청하기 때문이다.
history API를 사용하지 않기 때문에 동적 페이지에 불리하다.
history API를 사용한다.
별도의 서버 설정이 없다면 새로고침 시 404에러가 발생한다.
서버에서는 기본 라우트 '/'정보만 저장되어 있고, 이외의 모든 하위 라우팅은 이 default 경로를 통해 이루어지기 때문에 이 경로를 제외하고는 서버에서 인식하지 못한다. 만일 필요하다면 해당 주소와 그에 맞는 페이지를 서버에 알려주어야 한다. 그런게 아니라면 일반적으로는 '/*'의 경로로 접근 시 서버에는 '/'로 리다이렉션 해주면 된다.
(404 Error -> index.html)
배포할 때 많이 에러가 나는 부분이므로 제대로 알고 있도록 하자!!
참고 자료
큰 프로젝트에 적합하다.
우리가 사용할 라우터도 이 BrowserRouter이다!!
서버가 존재하고, SEO가 필요한 프로젝트라면 BrowserRouter를 사용하는 것이 좋고, 그 외에 개인적이거나 작은 단위의 프로젝트라면 HashRouter를 사용해도 괜찮다!!
React로 생성된 SPA 내부에서 페이지 이동이 가능하도록 만들어주는 라이브러리!!
yarn add react-router-dom
//ts yarn add @types/react-router-dom
따라서 package.json
에서 버전이 v6~인지 꼭 확인하고 넘어가자.
import { BrowserRouter, Routes, Route } from 'react-router-dom'
가장 많이 사용되는 모듈 3가지이다!!
BrowserRouter
- history API를 활용해 history 객체를 생성한다.
- history API는 내부적으로 stack 자료구조의 형태를 띄기 때문에 사용자가 방문한 url 기록들을 차곡차곡 쌓는 형태로 저장해둔다고 생각하면 된다.
- 라우팅을 진행할 컴포넌트 상위에 BrowserRouter 컴포넌트를 생성하고 감싸주어야 한다!!
Route
- 현재 브라우저의 location(window.href.location 정보를 가져온다) 상태에 따라 다른 element를 렌더링한다.
Route.element
: 조건이 맞을 때 렌더링할 element,
<Element />
의 형식으로 전달된다!!Route.path
: 현재 path값이 url과 일치하는지 확인해 해당 url에 매칭된 element를 렌더링해준다!!
Routes
- 모든
Route
의 상위 경로에 존재해야 하며, location 변경 시 하위에 있는 모든Route
를 조회해 현재 location과 맞는Route
를 찾아준다!
아래와 같은 방식으로 기본적인 설정을 한다.
const Router = () => {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<GalleryPage />} />
<Route path="/gallery" element={<DetailCardPage />}>
<Route path=":cardId" element={<DetailCard />} />
</Route>
</Routes>
</BrowserRouter>
);
};
위와 같은 방식으로 Router 컴포넌트를 생성해준 뒤,
import Router from "./Router";
const App = () => (
<>
<Router />
</>
);
export default App;
상위 렌더링 요소에 컴포넌트를 붙여준다!
Link 컴포넌트는 라우터 내에서 직접적으로 페이지 이동을 하고자 할 때 사용되는 컴포넌트이다.
import React from 'react';
import {Link} from 'react-router-dom';
function Nav(){
return (
<div>
<Link to='/'> Home </Link>
<Link to='/about'> About </Link>
</div>
);
}
export default Nav;
위와 같은 방식으로 간단하게 to
속성에 경로를 넣어주는 방식으로 사용한다!
Link 컴포넌트는 다음과 같은 특징을 갖는다.
./..
과 같은 표현도 사용이 가능하다.true
로 설정해주면 이를 방지할 수 있다!!useLocation
훅과 연계하여 특정 state를 넘겨주는 것도 가능하다.<Link to="new-path" state={{ some: "value" }} />
let { state } = useLocation();
Link는 a태그로 이루어져 있지만, 자체적으로 컴포넌트의 상태를 유지하거나, 화면 전체 리렌더링을 방지하는 등의 기능이 포함된 a 태그의 상위 버전이라고 생각하면 좋을 것 같다!!
그럼 내가 일반 article 요소를 클릭했을 때 이동을 하고 싶으면 어쩌지..? Link로 무작정 감싸야 하나..?
이럴 때 사용되는 게 바로 useNavigate
훅이다.
useNavigate
훅을 사용하면 특정 이벤트(onChange, onClick 등)가 발생했을 때 페이지 이동을 트리거할 수 있다.
import { useNavigate } from "react-router-dom";
const navigate = useNavigate();
const onClick = () => {
navigate('/')
}
위와 같이 사용한다!!
useNavigate
에는 다음과 같은 속성을 추가할 수 있다.
false
이고, true
로 설정한다면 이동 후 뒤로가기가 불가능해진다.navigate("/", { replace: true });
navigate("/", { state: { cardId: cardId } });
const location = useLocation();
const { cardId } = location.state;
개인적으로 React-router-dom 내장 기능 중 가장 유용하게 사용하고 있는 기능이다. 특정 페이지 내에서 하위 페이지를 만들 수 있고, 해당 페이지마다 경로를 이용한 데이터 전달도 가능하다!
또한 중첩 라우팅을 구현할 경우 해당 하위 페이지 이외에는 컨텐츠가 바뀌지 않는다!
<Route path="/about" element={<About />}>
<Route path="location" element={<Location />}></Route>
</Route>
라우터 내부에 위와 같이 자식 요소 Route를 만들어준다. 이렇게만 설정해도 라우터 내부적으로는 /about
주소 하위에 /location
이라는 하위 라우팅이 되었다고 판단한다. 따라서 우리가 /about/location
으로 주소를 이동할 경우, 주어진 Location 컴포넌트가 렌더링되는 것이다(물론 About컴포넌트도 같이 렌더링된다.)!!
물론 라우터에서 위와 같이 설정한 것 만으로는 아무런 변화가 생기지 않는다.
실제로 해당 라우팅이 발생하는 부모 요소인 About 페이지에 하위 라우팅 발생 시 컴포넌트를 렌더링할 자리를 명시해주어야하기 때문이고, 이때 사용되는 것이 Outlet 컴포넌트이다.
import { Outlet } from 'react-router-dom';
function About() {
return (
<div>
<div>
<h2>여기는 About 페이지입니다.</h2>
<p>대충 쇼핑몰 페이지라는 뜻</p>
</div>
<Outlet />
</div>
);
}
이처럼 About 컴포넌트 내부에 Outlet 컴포넌트를 렌더링해주면 라우터에서 이를 인식하고 Outlet 자리에 Location 컴포넌트를 렌더링하게 되는 것이다.(물론 주소가 일치하는 경우)
Outlet 없이도 중첩 라우팅을 구현할 수 있다.
우선 라우터에서 중첩 라우팅을 하고자 하는 주소에 다음과 같이 *
을 추가해주어 중첩 라우팅이 발생할 주소임을 명시해준다.
<Routes>
<Route path="/" element={<Home />}></Route>
<Route path="/about/*" element={<About />}></Route>
<Route path="/products" element={<Products />}></Route>
</Routes>
이후 해당 About 컴포넌트에서 본래 Outlet이 들어갔던 자리에 마치 라우터를 구현했던 것처럼 중첩 라우팅을 진행해주면 된다.
function About() {
return (
<div>
<div>
<h2>여기는 About 페이지입니다.</h2>
<p>대충 쇼핑몰 페이지라는 뜻</p>
</div>
<Routes>
<Route path="/location" element={<Location />}></Route>
</Routes>
</div>
);
}
위와 같이 작성하면 Outlet 과 동일한 기능을 하는 중첩 라우팅을 구현할 수 있다.
주소 경로 내부에 특정 데이터를 넣어 전달하는 것을 말하는데, 크게 url 파라미터와 쿼리스트링으로 나누어진다.
<Route path="/new/:id" element={<NewId />} />
여기에서 :
으로 구분해준 id라는 값은 파라미터로 전달되어 이후 우리가 NewId 컴포넌트 내부에서 useParams
훅을 통해 추출하고 사용할 수 있다. 이때 전달된 값은 1234가 된다!!
?, &
을 기준으로 key와 value를 나눠 데이터를 전달받는다. 이렇게 전달 받은 값은 이후 useLocation
훅을 통해 추출하고 사용할 수 있다.
이전까지는 ?, &
을 직접 분리해 추출해야하는 번거로움이 있었는데, useSearchParams
를 사용하면 쉽게 해결할 수 있다!!
url 파라미터 | 쿼리 스트링 |
---|---|
ID, 이름, 특정 데이터를 조회할때 사용 | 키워드 검색, 페이지네이션, 정렬방식 등 데이터 조회에 필요한 옵션을 전달할 때 사용 |
일반적인 변수, 상수 값들을 전달하기 용이 | key, value 형태의 데이터이므로 json이나 객체 형태의 데이터를 전달하기 용이 |
위에서 말한 것처럼 url 파라미터를 조회할 때 사용한다.
실제 사용 예시를 살펴보자!!
import React from 'react';
import { useParams } from 'react-router-dom';
const NewId = () => {
let { id } = useParams();
return (
<div className="test">
<p>현재 유저의 아이디는 { id } 입니다.</p>
</div>
)
}
export default NewId;
이처럼 http://hello.com/new/1234의 주소로 이동하여 렌더링된 NewId 컴포넌트 내부에서 useParams를 이용해 아이디 값을 받아올 수 있다!!
http://hello.com/new/1234/1212 와 같은 주소에 라우팅을 /new/:id/:lastId
와 같은 방식으로 해준다면 여러 개의 값도 한 번에 전달받을 수 있다!!
위에서 언급한 것처럼 쿼리스트링을 추출하는 데 사용된다. 현재 위치에 대한 url의 쿼리스트링을 읽고 수정할 때 사용한다!!
useState와 사용법이 유사하므로 처음 적용하기 어렵지 않다.
const [serchParams, setSearchParams] = useSearchParams();
searchParams는 URLSearchParams 객체이면서 쿼리스트링을 다루기 위한 다양한 메서드를 내장하고 있다.
setSearchParams는 함수의 인자에 객체와 문자열을 넣어주면 현재 url의 쿼리스트링을 변경하는 기능을 제공한다!!
자주 사용하는 메서드를 살펴보자.
searchParams.get(key)
: 특정한 key의 value를 가져오는 메서드, 해당 key의 value가 두 개 이상이라면 처음의 값을 반환한다.
searchParams.getAll(key)
: 특정 key에 해당하는 모든 value를 가져오는 메서드
searchParams.set(key, value)
: 인자로 전달한 key 값을 value로 설정한다. 만일 기존에 key에 대한 값이 존재했다면 덮어씌운다.
searchParams.append(key, value)
: 기존 값을 변경 혹은 삭제하지 않고 추가한다.
searchParams 를 변경하는 메서드로 값을 변경하더라도 실제 url 에는 이 정보가 반영되지 않는다. 만일 이를 반영하고자 한다면 setSearchParams에 searchParams를 인자로 전달해주어야 한다!
쿼리스트링은 페이지네이션이나 키워드 검색, 정렬 등 꽤 다양한 곳에서 용이하게 사용되므로 위에서 언급한 기본적인 내용들과 대표 메서드들을 꼭 숙달하도록 노력하자!!!
공식문서
리액트 기초, 쿼리 스트링, useSearchParams
+GO SOPT 세미나 자료
혹시
/new/:id/lastId
에서 lastId에는:
이 없는데 불러와 질수가 있나요?