이 글은 김민준(velopert)님의 리액트를 다루는 기술을 참조하였습니다.
목차
1. What is Routing?
2. What is SPA(Single Page Application)?
3. React Router v6
4. URL Parameter & Querystring
5. 중첩된 라우트
6. Router Add-ons
1. What is Routing?
구글에 라우터라고 검색해보면 웬 하드웨어 장치가 나오는데 사진을 보면 대충 뭔지 알 것이다.
물론 소프트 웨어에서 말하는 라우터가 이 장치를 의미하진 않지만 "최적의 경로를 지정하며, 이 경로를 따라 ~을 전달한다."라는 개념에서는 같다.
라우팅이란 사용자가 요청한 URL에 알맞는 페이지를 보여주는 것을 의미한다.
"엥, 이거 완전 HTML의 a태그랑 같은 기능 아닌가요?"라고 한다면 그렇지 않다.
이 부분은 SPA에 대한 설명과 같이 하도록 하자.
리액트에서 주로 사용하는 라우팅 시스템은 React Router 라이브러리와 Next.js 프레임 워크가 있다.
(라우팅 기능은 리액트 라이브러리에서 공식 지원하는 것이 아니기 때문에 별도의 설치를 요구한다.)
포스팅에서는 React Router을 이용해 라우터를 다룰 것이다.
2. What is SPA?
SPA(Single Page Application)이란 의미 그대로 하나의 페이지로 구성된 애플리케이션을 의미한다.
기존의 HTML의 a태그로 이동하던 MPA(Multi Page Application, 다중 페이지 응용프로그램)을 떠올려보자.
main.html에서 a태그를 사용해 content.html로 이동할 때, 브라우저는 서버에 content.html을 요청하고 응답받아 이동한다.
페이지 이동시에 화면이 깜빡 거리거나, 화면에 그려야 할 정보가 많으면 로딩화면이 길어지기도 하는데 아무 내용도 없는 빈 화면이 오래 떠있는 것은 사용자에게 불쾌한 경험을 선사한다.
특히 여기서 정말 중요한 건, HTML을 새로 불러옴으로써 전체 페이지가 새로고침 된다는 것이다
우리가 지금까지 리액트를 다룰 때를 생각해보자.
컴포넌트 별로 변화가 필요한 놈들만 골라서 렌더링 시키기 때문에,
정보 갱신이 필요없거나 유지가 필요한 컴포넌트는 렌더링 하지 않고 있었다.
JSP를 공부해본 사람은 알겠지만 login.html, logout.html, register.html 전부 만들어주어야 한다..
사이트의 계층 구조가 복잡해질 수록 관리가 힘들어지기 때문에 검색 엔진 최적화를 노리기 위함이 아니라면 MPA 구조는 좋은 방법이 아니다.
MPA의 본질적 문제는 백엔드와 프론트 엔드의 결합 때문이었는데, 챕터 1에서 리액트가 뭐라고 했었던가?
바로 얘는 View만 잡고 패는 라이브러리였다는 점을 상기시켜보자.
SPA는 하나의 html에서 JS를 이용해 동적으로 필요한 데이터만 갱신하는 구조다.
전체 렌더링을 하고 나면, 그 뒤에는 필요한 부분만 조화 과정을 거침으로써 렌더링한다면 성능이 현저히 올라간다.
하지만 사용자가 느끼기에는 여러 페이지가 존재한다고 느낄 수 있는데, 라우팅 시스템을 이용해보면 주소창의 값이 계속 바뀌기 때문이다.
하지만 SPA에서 주소창은 내가 불러와야 할 값의 위치이지 실제 페이지가 이동한 것은 아니다.
SPA의 문제점으로 부각되었던 것은 실제 페이지가 이동하는 것이 아니기 때문에 주소창의 값이 바뀌지 않아서
사용자 정보를 유지할 수 없기 때문에 SEO(검색 엔진 노출) 이슈가 지속적으로 제기되어 왔었지만,
리액트는 이것도 SSR(서버 사이드 렌더링)이란 걸로 잘 해결했다고 한다...아 네...역시 대기업.
3. React Router v6
프로젝트 내에서 npm install react-router-dom으로 리액트 라우터 라이브러리를 설치하자.
이걸 사용하기 전에 index.js에서 한 가지 설정을 해주어야 하는데 BrowserRouter라는 놈으로 감싸주어야 한다.
1. BrowserRouter
import { BrowserRouter } from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
BrowserRouter는 HTML5를 지원하는 브라우저의 주소를 감지한다.
History API를 사용해 페이지를 새로 불러오지도 않으면서 주소 변경과 경로에 따른 데이터를 서버에 요청한다.
SPA의 단점이었던 주소가 바뀌지 않아 발생한 SEO 이슈를 해결할 수 있게 도와주는 것이다.
이제부터 예시로 들 코드는 velopert님의 코드를 따라 작성할 것이다.
src/component/Home.js
const Home = () => {
return (
<div>
<h1>홈</h1>
<p>홈페이지 입니다.</p>
</div>
)
}
export default Home;
src/comppnent/About.js
const About = () => {
return (
<div>
<h1>소개</h1>
<p>리액트 라우터를 사용해 보는 프로젝트입니다.</p>
</div>
)
}
export default About;
이전까지는 App.js에 컴포넌트를 import해서 <Home /> 이런 식으로 작성했었지만, 이젠 Route를 사용해서 주소 경로를 설정해줄 것이다.
2. Route
<Routes>
<Route path="주소규칙" element={컴포넌트 JSX} />
</Routes>
Route의 기본 사용법은 위와 같다. 참고로 Route는 Routes 컴포넌트 내부에서 사용되어야 한다.
import { Route, Routes } from 'react-router-dom';
import Home from './component/Home'
import About from './component/About'
const App = () => {
return (
<Routes>
<Route path="/" element={<Home/>} />
<Route path="/about" element={<About/>} />
</Routes>
)
}
export default App;
App.js에 이렇게 입력하고 실행 결과를 확인해보자.
주소 변화에 따라 페이지 정보가 바뀌는 것을 알 수 있다.
하지만 사용자에게 직접 주소창을 통해 이동하도록 할 수는 없으니 페이지를 이동하는 Link 컴포넌트를 사용해보자.
3. Link
<Link to="경로">경로 이름</Link>
Link 컴포넌트는 HTML에서 사용하던 a태그와 비슷한 역할을 하지만 위에서 언급했었다시피 a태그의 이동은 페이지를 새로 불러온다.
Link 컴포넌트는 내부적으로 a태그를 사용하긴 하지만 History API를 통해 페이지를 새로 불러오는 것을 막고 주소의 경로만 바꿔 데이터를 얻어낼 수 있다.
Home.js
import {Link} from 'react-router-dom';
const Home = () => {
return (
<div>
<h1>홈</h1>
<p>홈페이지 입니다.</p>
<Link to="/about">소개</Link>
</div>
)
}
export default Home;
Home에서 소개 페이지를 띄우기 위해서 Link 컴포넌트를 추가하고 경로를 명시해주면 직접 기능을 확인해볼 수 있다.
4. URL Parameter & Querystring
페이지를 이동할 때, 경로에 따라 값을 유동적으로 넣어주어야 할 때가 있다.
블로그를 예로 들면 게시물 하나하나당 모든 컴포넌트를 만들지는 않을 테니 보통 Post 컴포넌트 하나에
내가 선택하여 보고자 하는 Post의 정보를 받아와서 주소를 이동해 서버로부터 데이터를 얻어와야 한다.
물론 그 '유동적인 데이터'도 종류별로 나뉘기 때문에 2가지 방법을 모두 알고 있어야 한다.
- URL Parameter
- Querystring
1. URL Parameter (useParams)
예를 들어, 어떤 사용자 정보를 통해서 데이터를 조회하거나 topics에서 url에 각각의 topic id를 이용하여 특정 주제만 보여주고 싶을 때 사용한다.
App.js
import { Route, Routes } from 'react-router-dom';
import Home from './component/Home';
import About from './component/About';
import Profile from './component/Profile';
const App = () => {
return (
<Routes>
<Route path="/" element={<Home/>} />
<Route path="/about" element={<About/>} />
<Route path="/profiles/:username" element={<Profile />} />
</Routes>
)
}
export default App;
이번에는 경로가 조금 특이하다. /profiles까진 알겠는데 "/:username"은 뭐지?
이게 바로 URL 파라미터를 사용하는 방법이다. 경로의 끝에 콜론(:)을 사용하면 username이라는 path props가 Profile에게 넘어간다.
그렇다면 Profile에서 path props를 가지고 객체를 조회해서 원하는 user의 정보를 출력시키면 되지 않을까?
src/component/Profile.js
import { useParams } from 'react-router-dom';
const data = {
user1: {
name: "사용자1",
description: "지나가던 사람",
},
user2: {
name: "사용자2",
description: "피곤한 사람",
},
};
const Profile = () => {
const params = useParams();
const profile = data[params.username];
return (
<div>
<h1>사용자 프로필</h1>
{profile ? (
<div>
<h2>{profile.name}</h2>
<p>{profile.description}</p>
</div>
) : (
<p>존재하지 않는 프로필입니다.</p>
)}
</div>
);
};
export default Profile;
useParams는 path props를 객체 형태로 받아주는 Hooks이다.
파라미터의 이름은 /:username으로 보냈다면 "username"으로 넘어오게 된다.
Profile 컴포넌트는 이 데이터를 이용해 기존의 정보에서 원하는 정보를 화면에 띄워주면 되는 것이다.
Home.js
import {Link} from 'react-router-dom';
const Home = () => {
return (
<div>
<h1>홈</h1>
<p>홈페이지 입니다.</p>
<ul>
<li><Link to="/about">소개</Link></li>
<li><Link to="/profiles/user1">user1의 프로필</Link></li>
<li><Link to="/profiles/user2">user2의 프로필</Link></li>
<li><Link to="/profiles/void">존재하지 않는 프로필</Link></li>
</ul>
</div>
)
}
export default Home;
이동하기 편하기 위해 Home.js에서 user마다 이동할 수 있는 Link를 만들어주자.
2. Querystring (useLocation & useSearchParams)
About.js
import { useLocation } from 'react-router-dom';
const About = () => {
const location = useLocation();
return (
<div>
<h1>소개</h1>
<p>리액트 라우터를 사용해 보는 프로젝트입니다.</p>
<p>쿼리스트링: {location.search}</p>
</div>
)
}
export default About;
useLocation Hook은 리턴값으로 현재 사용자가 보고 있는 페이지의 정보를 제공한다.
- pathname: 쿼리스트링을 제외한 현재 주소 경로
- search: 맨 앞의 ? 문자를 포함한 쿼리스트링 값
- hash: 주소의 # 문자열 뒤의 값 (주로 구형 브라우저에서 클라이언트 라우팅을 사용할 때 쓰는 해시 라우터에서 사용)
- state: 페이지 이동 시 임의로 넣는 상태 값
- key: location 객체의 고유값, default에서 페이지가 변경되면 고유 값이 생성된다.
무슨 소린지 모르겠어서 직접 console창에 location을 출력해보기로 했다.
다른 건 모르겠고 pathname이 현재 주소를 가리키고 있음을 알고 있다.
그럼 주소창에 ?detail=true&mode=1 이라고 직접 입력한다면 search 속성 설명에 따라 값이 나올 것이다.
예상대로 location.search를 출력시켰으므로 쿼리 스트링이 그대로 화면에 노출된다.
search로 넘겨받은 querystring을 사용하기 위해서는 ?를 제거하고, & 문자열로 분리하여 key와 value로 파싱하는 가공 과정이 필요하다.
물론, 리액트 v6에서는 useSearchParams라는 Hook이 이 작업을 대신 수행해준다.
About.js
import { useSearchParams } from 'react-router-dom';
const About = () => {
const [seachParams, setSearchParams] = useSearchParams();
const detail = seachParams.get('detail');
const mode = seachParams.get('mode');
const onToggleDetail = () => {
setSearchParams({ mode, detail: detail === 'true' ? false : true});
}
const onIncreaseMode = () => {
const nextMode = mode === null ? 1 : parseInt(mode) + 1;
setSearchParams({ mode: nextMode, detail });
}
return (
<div>
<h1>소개</h1>
<p>리액트 라우터를 사용해 보는 프로젝트입니다.</p>
<p>detail: {detail}</p>
<p>nide: {mode}</p>
<button onClick={onToggleDetail}>Toggle detail</button>
<button onClick={onIncreaseMode}>mode + 1</button>
</div>
)
}
export default About;
useSearchParams Hook은 배열 타입 값을 반환하는데
첫 번째 원소는 queryparam을 조회·수정하는 메서드가 담긴 객체를 반환한다.
그 중에서 get이란 메서드를 사용해서 특정한 쿼리 파라미터 정보를 가져올 수 있다.
두 번째 원소는 queryparam을 객체 형태로 업데이트 시켜주는 함수에 해당한다.
queryparam은 어쨌든 주소로 넘어오는 문자열 형태이기 때문에 반드시 'true'나 'false'처럼 따옴표로 감싸야 하고
숫자의 경우에는 parseInt 함수를 이용해 조작해야 한다.
5. 중첩된 라우트
시작에 앞서, 컴포넌트를 생성해주자.
src/component/Articles.js
import { Link } from 'react-router-dom';
const Articles = () => {
return (
<ul>
<li><Link to="/articles/1">게시글 1</Link></li>
<li><Link to="/articles/2">게시글 2</Link></li>
<li><Link to="/articles/3">게시글 3</Link></li>
</ul>
);
};
export default Articles;
src/component/Article.js
import { useParams } from 'react-router-dom';
const Article = () => {
const { id } = useParams();
return (
<div>
<h2>게시글 {id}</h2>
</div>
);
};
export default Article;
App.js
(...)
import Article from './component/Article';
import Articles from './component/Articles';
const App = () => {
return (
<Routes>
<Route path="/" element={<Home/>} />
<Route path="/about" element={<About/>} />
<Route path="/profiles/:username" element={<Profile />} />
<Route path="/articles" element={<Articles/>} />
<Route path="/articles/:id" element={<Article/>} />
</Routes>
)
}
export default App;
Home.js
import {Link} from 'react-router-dom';
const Home = () => {
return (
<div>
<h1>홈</h1>
<p>홈페이지 입니다.</p>
<ul>
<li><Link to="/about">소개</Link></li>
<li><Link to="/profiles/user1">user1의 프로필</Link></li>
<li><Link to="/profiles/user2">user2의 프로필</Link></li>
<li><Link to="/profiles/void">존재하지 않는 프로필</Link></li>
<li><Link to="/articles">게시글 목록</Link></li>
</ul>
</div>
)
}
export default Home;
이걸 실행시켜보면 다음과 같이 작동한다.
잘 작동하는 것 같은데 무슨 상관이지? 라고 생각할 수 있지만
만약 게시글 1,2,3의 목록은 위에 계속 띄워둔 채로 내용을 밑에 확인하고 싶다면 어떻게 해야할까?
지금까지의 방법으로는 list를 다루는 컴포넌트를 하나 더 만들어서 각각의 페이지에 해당 컴포넌트를 import 해야만 한다.
하지만 중첩 라우터를 사용해서 더 간결하게 표현할 수 있다.
Outlet
App.js에서 이렇게 수정해주자.
<Route path="/articles" element={<Articles/>}>
<Route path=":id" element={<Article />} />
</Route>
이렇게 되면 Articles는 부모 컴포넌트로써 유지되고, Article은 자식 컴포넌트로써 path props에 따라 요청된 페이지만을 띄울 것이다.
리액트 컴포넌트 파트에서 children을 화면에 출력하는 방법에 대해서는 배웠었는데, path props로 children을 선택하는 경우에는 어떻게 해야할까?
엄청 간단하다. Outlet Hook을 사용하면 된다.
import { Link, Outlet } from 'react-router-dom';
const Articles = () => {
return (
<div>
<Outlet />
<ul>
<li><Link to="/articles/1">게시글 1</Link></li>
<li><Link to="/articles/2">게시글 2</Link></li>
<li><Link to="/articles/3">게시글 3</Link></li>
</ul>
</div>
);
};
export default Articles;
Outlet은 Route의 children으로 들어가는 컴포넌트의 JSX 엘리먼트를 보여준다.
path props를 통해 선택된 데이터를 Article이 들고 나오면 Outlet 컴포넌트가 위치한 자리에 자식 컴포넌트가 출력되는 것이다.
중첩 라우팅을 사용하면 페이지에서 공통적으로 보여주어야 하는 화면들의 레이아웃 구성을 짤 때도 편리하다.
예를 들어, 현재 내 블로그만 해도 웹 상태에서 왼쪽의 네비게이션 바가 계속해서 따라다니는데
모든 컴포넌트에 네비게이션 컴포넌트를 다 넣어주려면 귀찮은 건 둘 째치고, Header, Footer 등등 여러 개의 중복되는 컴포넌트가 있다면 관리가 힘들 것이다.
그럴 때에 모든 페이지에 공통적으로 나오는 부분을 Layout컴포넌트로 지정해주면 된다.
src/Layout.js
import { Outlet } from "react-router-dom";
const Layout = () => {
return (
<div>
<header style={{ background: 'lightgray', padding: 16, fontsize: 24}}>Header</header>
<main>
<Outlet />
</main>
</div>
)
}
export default Layout;
App.js
import { Route, Routes } from 'react-router-dom';
import Layout from './Layout';
import Home from './component/Home';
import About from './component/About';
import Profile from './component/Profile';
import Article from './component/Article';
import Articles from './component/Articles';
const App = () => {
return (
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<Home/>} />
<Route path="/about" element={<About/>} />
<Route path="/profiles/:username" element={<Profile />} />
</Route>
<Route path="/articles" element={<Articles/>}>
<Route path=":id" element={<Article />} />
</Route>
</Routes>
)
}
export default App;
이렇게 하면 Layout을 부모 컴포넌트로 가지지 않는 Articles 컴포넌트 이외에는 헤더가 계속해서 따라다니게 된다.
💡 Route 컴포넌트의 index props
확실히 전에 리액트를 너무 알짜배기로 공부했어서 모르는 개념이 너무 많이 나온다.
가장 최상단 페이지는 보통 path props로 path="/"라고 경로를 설정해준다.
그런데 기서 대신에 index만 써놔도 여전히 해당 경로에 잘 찾아간다.
"index props는 상위 라우트 경로와 일치하지만, 이후 경로가 주어지지 않았을 때 보여지는 Route를 설정할 수 있다."
라고 책에 적혀 있는데 뭔 소린지 모르겠어서 검색해봤는데 딱히 나오는 정보가 없다.
그냥 path="/"과 동일한 기능인데, 좀 더 명시적인 역할 정도를 수행하는 듯 하다.
6. Router Add-ons
React Router에서 라우팅 작업을 편하게 만들어주는 유용한 API를 제공하는데 몇 가지만 알아보자.
1. useNavigate
※ 원래 useHistory 였는데 v6에서 이름이 바뀌었다. history(페이지 방문 기록)를 이용한 navigate라고 보면 된다.
useNavigate는 Link 컴포넌트를 사용하지 않고 이동할 때 쓴다. 그런데 왜 굳이 같은 기능을 담당하는 컴포넌트가 2개 있을까?
Layout.js를 다음처럼 수정해보자
import { Outlet, useNavigate } from "react-router-dom";
const Layout = () => {
const navigate = useNavigate();
const goBack = () => {navigate(-1);}
const goArticles = () => {navigate('/articles');}
return (
<div>
<header style={{ background: 'lightgray', padding: 16, fontsize: 24}}>
<button onClick={goBack}>뒤로가기</button>
<button onClick={goArticles}>게시글 목록</button>
</header>
<main>
<Outlet />
</main>
</div>
)
}
export default Layout;
useNavigate는 페이지 이동을 가능하게 해주는 함수를 반환한다.
Link 컴포넌트는 지정 경로로 바로 이동시키는 반면, useNavigate는 함수를 실행시켜야 넘어가기 때문에
몇 가지 조건을 걸 수 있다. 예를 들어 로그인 여부에 따른 페이지 이동 경로에 차별화 둘 수 있다.
const goArticles = () => {
if (response.message === "valid user") {
navigate('/articles');
} else {
alert("로그인을 필요로 하는 서비스 입니다.");
navigate('/login');
}
}
하나의 예시로써 작성해보았다.
물론 지금 상태에선 이렇게 코드를 짜도 작동하지 않는다.
2. NavLink
NavLink 컴포넌트는 사용 경로가 현재 라우트 경로가 일치하면 특정 스타일 혹은 CSS 클래스를 적용한다.
import { NavLink, Outlet } from 'react-router-dom';
const Articles = () => {
const activeStyle = {
color: 'green',
fontSize: 21,
} ;
return (
<div>
<Outlet />
<ul>
<li><NavLink
to="/articles/1"
style={({isActive}) => (isActive ? activeStyle : undefined)}
>게시글 1</NavLink></li>
<li><NavLink
to="/articles/2"
style={({isActive}) => (isActive ? activeStyle : undefined)}
>게시글 2</NavLink></li>
<li><NavLink
to="/articles/3"
style={({isActive}) => (isActive ? activeStyle : undefined)}
>게시글 3</NavLink></li>
</ul>
</div>
);
};
export default Articles;
현재 articles/1에 들어가 있기 때문에 NavLink에 정해진 CSS 속성이 입혀진 것을 알 수 있다.
반복되는 NavLink 때문에 코드 가독성이 너무 떨어지는 관계로 ArticleItem 컴포넌트를 따로 분리하자.
import { NavLink, Outlet } from 'react-router-dom';
const Articles = () => {
return (
<div>
<Outlet />
<ul>
<ArticleItem id={1} />
<ArticleItem id={2} />
<ArticleItem id={3} />
</ul>
</div>
);
};
const ArticleItem = ({ id }) => {
const activeStyle = {
color: 'green',
fontSize: 21,
};
return (
<li>
<NavLink
to={`/articles/${id}`}
style={({ isActive }) => (isActive ? activeStyle : undefined)}
>
게시글 {id}
</NavLink>
</li>
);
};
export default Articles;
3. Wildcard(*)
wildcard는 프로그래밍에서 보통 전체를 의미한다. (예전에 42seoul에서 혼자 별표, 별표 거리고 다녔었다.)
Route의 path props에 whildcard를 던져주면 이전의 Route에서 걸러지지 못한.
즉, 존재하지 않는 경로에 접근하는 경우 해당 Route 화면을 출력시킨다.
src/component/NotFound.js
const NotFound = () => {
return (
<div
style = {{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: 64,
position: 'absolute',
width: '100%',
height: '100%',
}}>404 Not Found</div>
)
}
export default NotFound;
App.js
import NotFound from './component/NotFound';
const App = () => {
return (
<Routes>
<Route element={<Layout />}>
<Route index element={<Home/>} />
<Route path="/about" element={<About/>} />
<Route path="/profiles/:username" element={<Profile />} />
</Route>
<Route path="/articles" element={<Articles/>}>
<Route path=":id" element={<Article />} />
</Route>
<Route path="*" element={<NotFound />}/>
</Routes>
)
}
export default App;
이렇게 설정하고 주소창에 존재하지 않는 경로를 입력하면 404 에러창이 출력된다.
4. Navigate
Navigate 컴포넌트는 렌더링될 때 현재 위치를 변경한다. 즉, 페이지를 리다이렉트 하는 것이다.
src/component/Login.js
const Login = () => {
return <div>로그인 페이지</div>
}
export default Login;
src/component/MyPage.js
import { Navigate } from "react-router-dom";
const MyPage = () => {
const isLoggedIn = false;
if (!isLoggedIn) {
return <Navigate to="/login" replace={true} />;
} else {
return <div>마이 페이지</div>
}
}
export default MyPage;
App.js
import { Route, Routes } from 'react-router-dom';
import Layout from './Layout';
import Home from './component/Home';
import About from './component/About';
import Profile from './component/Profile';
import Article from './component/Article';
import Articles from './component/Articles';
import NotFound from './component/NotFound';
import MyPage from './component/MyPage';
import Login from './component/Login';
const App = () => {
return (
<Routes>
<Route element={<Layout />}>
<Route index element={<Home/>} />
<Route path="/about" element={<About/>} />
<Route path="/profiles/:username" element={<Profile />} />
</Route>
<Route path="/articles" element={<Articles/>}>
<Route path=":id" element={<Article />} />
</Route>
<Route path="/login" element={<Login />} />
<Route path="/mypage" element={<MyPage />} />
<Route path="*" element={<NotFound />}/>
</Routes>
)
}
export default App;
이러면 /mypage 로 접근했을 때, 로그인 되어 있지 않기 때문에 login 창이 리다이렉트 된다.
그럼 useNavgate와 Navigate의 차이는 무엇이고 언제 써야할까?
Note: This API is mostly useful in React.Component subclasses that are not able to use hooks. In functional components, we recommend you use the `useNavigate` hook instead.
-참고 : 이 API는 대부분 후크를 사용할 수 없는 React.Component 하위 클래스에서 유용합니다. 기능적 구성 요소에서는 대신 `useNavigate` 후크를 사용하는 것이 좋습니다.-
공식 홈페이지에서 대놓고 웬만하면 useNavigate를 쓰라고 한다.
차이라고 해봐야 컴포넌트냐 함수로써 다루느냐에, Navigate 컴포넌트는 리다이렉트 되어버린다는 점 정도지 싶다.