티스토리 뷰
✔ 모든 본문의 내용은 벨로퍼트님의 리액트 라우터 v6 튜토리얼을 보고 정리했습니다. 상세한 내용을 원하시면 하단의 링크를 통해 확인 가능합니다.
👾토픽마다 이모지 표시를 해두었습니다. 주관적인 느낌이며 참고만 하시면 됩니다.
⭐ -> 유용하지만 개인적으로 어렵거나 생소함
😎 -> normal 쉬움
라우팅이란?
웹 어플리케이션에서 라우팅은 사용자가 요청한 URL에 따라 알맞는 페이지를 보여주는 것을 의미합니다.
여러 페이지로 구성된 웹 어플리케이션을 만들 때 페이지 별로 컴포넌트들을 분리해가면서 프로젝트를 관리하기 위해 필요한 것이 라우팅 시스템입니다.
리액트에서 라우트 시스템을 구축하는 선택지는 크게 두가지 입니다.
- 리액트 라우터(React Router): 해당 라이브러리는 리액트의 라우팅 관련 라이브러리중 가장 오래됐고, 가장 많이 사용되고 있습니다. 컴포넌트 기반으로 라우팅 시스템을 설정할 수 있습니다. (프로덕션에 사용하기에 안정적)
- Next.js: SSR을 가능하게 하는 리액트 프로젝트 프레임워크입니다. 해당 프레임워크는 파일 경로 기반으로 라우팅 시스템을 작동시킵니다. 리액트 라우터의 대안으로 많이 사용되고 있습니다. (따로 추가적인 서드 파티라이브러리 설치가 불필요)
프로젝트 생성 및 라이브러리 설치
yarn create react-app 프로젝트명
yarn add react-router-dom //버전을 기입해주지 않으면 최신버전으로 깔립니다. v6
프로젝트에 라우터 적용
src/index.js 파일에서 react-router-dom에 내장된 BrowserRouter이라는 컴포넌트를 사용하여 App 컴포넌트를 감쌉니다.
해당 컴포넌트는 웹 어플리케이션에 HTML5에 History API를 사용하여 페이지를 새로 불러오지 않고도 주소를 변경하고 현재 주소의 경로에 관련된 정보를 리액트 컴포넌트에서 사용할 수 있도록 해줍니다.
src/index.js
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import { BrowserRouter } from "react-router-dom";
// {Router as BrowerRouter} 이런식으로 컴포넌트 이름을 바꿔서 사용해도 무관합니다.
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
😎Route 컴포넌트로 특정 경로에 원하는 컴포넌트 보여주기
Routes와 Route 컴포넌트 사용 (🔥Route는 Routes 내부에서 사용해야 한다.) from react-router-dom
기본 문법
<Route path="주소규칙" element={보여 줄 컴포넌트 JSX} />
src/App.js
import "./App.css";
import { Route, Routes } from "react-router-dom";
import Home from "./pages/Home";
import About from "./pages/About";
function App() {
return (
<Routes>
<Route path="/" element={<Home />}></Route>
<Route path="/about" element={<About />}></Route>
</Routes>
);
}
export default App;
😎Link 컴포넌트를 사용하여 다른 페이지로 이동하는 링크 보여주기
기본 문법
import { Link } from "react-router-dom";
<Link to="/경로">링크명</Link>
src/pages/Home.js
import React from "react";
import { Link } from "react-router-dom";
function Home() {
return (
<>
<h1>Home</h1>
<p>가장 먼저 보이는 페이지</p>
<Link to="/about">소개</Link>
</>
);
}
export default Home;
기존 웹 페이지는 a href="주소" 를 통해 링크를 보여주는데요. 리액트에서는 a 태그 대신 Link 컴포넌트를 사용합니다.
개발자 툴을 보면 동일하게 Link 컴포넌트로 만들어진 링크도 a 태그로 나타나는데 차이가 있다면 a태그를 직접 기입하는 것은 새로고침이 일어나고 Link 컴포넌트를 사용한 링크는 일어나지 않습니다. 새로고침이 일어나면 브라우저에서 페이지를 새로 불러오기 때문에 리액트 라우터의 Link 컴포넌트를 사용해야겠죠?
import React from "react";
import { Link } from "react-router-dom";
function Home() {
return (
<>
<h1>Home</h1>
<p>가장 먼저 보이는 페이지</p>
<Link to="/about">소개</Link>
</>
);
}
export default Home;
⭐URL 파라미터와 쿼리스트링
페이지 주소를 정의할 때 유동적인 값을 사용해야 할 때 쓰입니다.
- URL 파라미터 예시: /proifle/username112 -> 주로 ID 또는 이름을 사용하여 특정 데이터 조회시
- 쿼리스트링 예시: /articles?**page=1&keyword=react -> 키워드 검색, 페이지네이션, 정렬 방식 등 데이터 조회에 필요한 옵션을 전달할 때 사용
URL 파라미터는 주소의 경로에 유동적인 값(변할 수 있는 값 e.g.) 닉네임 등)을 넣는 형태
쿼리 스트링은 뒷부분에 ? 문자열 이후 key=value로 값을 정의하여 &로 구분하는 형태를 말합니다.
URL 파라미터는 useParams 라는 Hook을 사용하여 객체 형태로 조회가 가능합니다.
URL 파라미터의 이름은 라우트 설정할 때 Route 컴포넌트의 path props 를 통하여 설정해줍니다.
src/App.js
<Route path="/profiles/:username" element={<Profile />} />
:username 이 URL 파라미터가 되겠습니다. :를 사용하여 설정하면 됩니다. 만약 URL 파라미터가 여러개인 경우엔
/profiles/:username/:field 와 같은 형태로 연달아 써주면 됩니다.
src/pages/Profile.js
import { useParams } from 'react-router-dom';
const data = {
velopert: {
name: '김민준',
description: '리액트를 좋아하는 개발자',
},
gildong: {
name: '홍길동',
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;
이 다음 Profile 페이지로 이동할 수 있도록 Home페이지에 Link를 더 만들어줍니다.
src/pages/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/velopert">velopert의 프로필</Link>
</li>
<li>
<Link to="/profiles/gildong">gildong의 프로필</Link>
</li>
<li>
<Link to="/profiles/void">존재하지 않는 프로필</Link>
</li>
</ul>
</div>
);
};
export default Home;
⭐쿼리스트링
라우트에서 쿼리스트링을 사용할 때는 URL 파라미터와 달리 Route 컴포넌트를 사용할 때 별도로 설정할 것은 없습니다.
src/pages/About.js
import React from "react";
import { useLocation } from "react-router-dom";
function About() {
const location = useLocation();
return (
<div>
<h1>소개</h1>
<p>리액트 라우터를 사용해보는 프로젝트입니다.</p>
<p>쿼리 스트링 : {location.search}</p>
</div>
);
}
export default About;
- pathname: 현재 주소의 경로 (쿼리스트링 제외)
- search: 맨 앞의 ? 문자 포함한 쿼리스트링 값
- state: 페이지로 이동할때 임의로 넣을 수 있는 상태 값
- key: location 객체의 고유 값, 초기에는 default 이며 페이지가 변경될때마다 고유의 값이 생성됨
-> 쿼리스트링의 값이 ?부터 출력되는데 ?를 지우고 &문자열로 분리한뒤 key와 value를 파싱하는 작업이 리액트 라우터 v6부터는 useSearchParams라는 Hook을 사용하여 간단하게 파싱할 수 있습니다. (v5를 사용중이시라면 qs 라이브러리를 사용해야 합니다.)
src/pages/About.js
import React from "react";
import { useSearchParams } from "react-router-dom";
function About() {
const [searchParams, setSearch] = useSearchParams();
const detail = searchParams.get("detail");
const mode = searchParams.get("mode");
const onToggleDetail = () => {
setSearch({ detail: detail === "true" ? false : true });
};
const onIncreaseMode = () => {
const nextMode = mode === null ? 1 : parseInt(mode) + 1;
setSearch({ mode: nextMode, detail });
};
return (
<div>
<h1>소개</h1>
<p>리액트 라우터를 사용해보는 프로젝트입니다.</p>
<p>detail: {detail}</p>
<p>mode: {mode}</p>
<button onClick={onToggleDetail}>Toggle detail</button>
<button onClick={onIncreaseMode}>mode + 1</button>
</div>
);
}
export default About;
즉, v6부터는 useSearchParams 훅을 사용하면 URL의 쿼리스트링을 간편하게 조작할 수 있습니다.
해당 훅은 배열 타입의 값을 반환하며, 첫번째 원소는 쿼리파라미터를 조회하거나 수정하는 메서드들이 담긴 객체를 반환합니다. get 메서드를 통해 조회 가능하고, set 메서드를 통해 특정 쿼리파라미터를 수정할 수있습니다.
만약 조회시 쿼리파라미터가 존재하지 않는다면 null이 반환됩니다. (useSearchParams hook은 react의 useState와 비슷하다고 느꼈습니다.)
주의점: 쿼리파라미터를 조회할 때 값은 무조건 "문자열 타입"입니다. 즉, boolean 값을 넣게 된다면 값을 비교할 때 꼭 'true"와 같이 따옴표로 감싸서 비교를 해야하고 숫자를 다룬다면 parseInt를 통해 숫자 타입변환이 필요합니다.
⭐중첩된 라우트
상황: 게시물 목록 페이지에서 게시글을 열었을 때, 게시글의 하단에 목록을 보여줘야 하는 경우(새 페이지를 여는 것이 아니라)
기존의 App.js가 아래와 같은 모양이라면,
<Route path="/article/:id" element={<Article />}></Route>
<Route path="/articles" element={<Articles />}></Route>
다음과 같이 중첩된 형태로 변경해줍니다.
<Route path="/articles" element={<Articles />}>
<Route path=":id" element={<Article />}></Route>
</Route>
그 다음 Articles 컴포넌트에서 Outlet 이라는 컴포넌트를 사용해줍니다. 이 컴포넌트는 Route의 children으로 들어가는 JSX엘리먼트를 보여주는 역할을 합니다.
src/pages/Articles.js 를 다음과 같이 수정해줍니다.
import React from "react";
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은 페이지끼리 공통적으로 보여줘야 하는 레이아웃이 있을 때에도 유용하게 사용됩니다.
페이지 상단에 Header 컴포넌트를 공통적으로 보여줘야 한다면, 보통의 경우 Header 컴포넌트를 따로 만들어 각 페이지 컴포넌트마다 재사용하는 방법을 사용합니다. 이 방법 외에 방금 배운 중첩된 라우트와 Outlet을 활용하여 구현할 수 있습니다. 중첩된 라우트를 사용하는 방식을 채택하면 컴포넌트를 한번만 사용해도되는 장점이 있습니다.
src/Layout.js
import React from "react";
import { Outlet } from "react-router-dom";
function Layout() {
return (
<div>
<header style={{ background: "lightgray", padding: 16, fontSize: 24 }}>
Header
</header>
<main>
<Outlet />
</main>
</div>
);
}
export default Layout;
각 페이지 컴포넌트가 보여져야 하는 부분에 Outlet 컴포넌트를 사용했습니다.
import Home from "./pages/Home";
import About from "./pages/About";
import Article from "./pages/Article";
import Articles from "./pages/Articles";
import Layout from "./Layout";
function App() {
return (
<Routes>
<Route element={<Layout />}>
<Route path="/" element={<Home />}></Route>
<Route path="/about" element={<About />}></Route>
</Route>
<Route path="/articles" element={<Articles />}>
<Route path=":id" element={<Article />}></Route>
</Route>
</Routes>
);
}
export default App;
이렇게 하면 Header가 Home과 About 페이지에 공통 레이아웃으로 들어가게 됩니다.
index props ? Route 컴포넌트에는 index라는 props 가 있습니다. path="/" 와 동일한 의미이며 좀 더 명시적으로 표현이 가능합니다.
//위와 아래는 동일한 의미를 지닙니다.
<Route index element={<Home />}></Route>
<Route path="/" element={<Home />}></Route>
😎useNavigate Hook
Link 컴포넌트를 사용하지 않고 다른 페이지로 이동해야하는 상황에서 쓰는 Hook 입니다.
- 개인적으로 특정 이벤트에 어떤 기능을 실행하고 마지막으로 경로를 변경해줄 때 사용했습니다. Link 는 경로만 바꾸는 용도라면 useNavigate 훅은 다른 기능에 경로 변경 기능을 같이하고 싶을 때 사용합니다.
- navigate 함수를 사용할 때 파라미터가 숫자 타입이라면 앞으로 가거나, 뒤로 가기 기능을한다. (-1, 한번 뒤로 가고 -2 두번 뒤로간다.) + "경로" 를 써주면 해당 경로로 이동한다.
import React from "react";
import { Outlet, useNavigate } from "react-router-dom";
function Layout() {
const navigate = useNavigate();
const goBack = () => {
//이전 페이지로 이동
navigate(-1);
};
const goArticles = () => {
navigate("/articles");
};
return (
<div>
<header style={{ background: "lightgray", padding: 16, fontSize: 24 }}>
Header
<button onClick={goBack}>뒤로가기</button>
<button onClick={goArticles}>게시물 목록</button>
</header>
<main>
<Outlet />
</main>
</div>
);
}
export default Layout;
- 다른 페이지로 이동할 때 replace 옵션이 있다. -> 해당 옵션을 사용하면 페이지를 이동할 때 현재 페이지를 페이지 기록에 남기지 않는다. (유저가 뒤로가기를 눌렀을 때 다시 접속하지 못하도록 기록에서 없애고 싶다면 사용하면 좋을 옵션인듯하다. + 유저의 개인정보가 담긴 마이페이지도 활용가능할 거 같다! )
⭐replace옵션은 라우터 돔의 Navigate 컴포넌트의 prop으로도 사용 가능합니다. Navigate to="경로" replace={true} 로 주면, 이 또한 경로에 기록이 남지 않습니다.
const goArticles = () => {
// navigate("/articles"); 해당 경로로 navigate
navigate("/articles", { replace: true });
};
⭐NavLink
NavLink 컴포넌트는 링크에서 사용하는 경로가 현재 라우트의 경로와 일치하는 경우 특정 스타일 또는 CSS 클래스를 적용하는 컴포넌트이다.
이 컴포넌트를 사용할 때 style 또는 className을 설정하면 {isActive: boolean} 을 파라미터로 전달받는 함수 타입의 값을 전달합니다.
<NavLink
style={({isActive}) => isActive ? activeStyle : undefined}
/>
<NavLink
className={({isActive}) => isActive ? 'active' : undefined}
/>
src/pages/Articles.js 에 적용해봅시다.
import React from "react";
import { Link, Outlet, NavLink } from "react-router-dom";
const Articles = () => {
const activeStyle = {
color: "blue",
fontSize: 20,
};
return (
<div>
<h1>Articles Page</h1>
<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>
<Link to="/articles/3">게시글 3</Link>
</li>
</ul>
</div>
);
};
export default Articles;
->게시글 3은 기존에 Link 컴포넌트로 경로를 지정한 모습이고 게시글 1,2 는 활성화 됐을 때 색상과 폰트 사이즈가 변하도록 NavLink를 통해 구현했습니다. 결과물은 아래와 같습니다.
게시물 코드가 반복되는 것을 확인할 수 있습니다. 리팩토링하면 아래와 같은 모습이 됩니다.
import React from "react";
import { Link, Outlet, NavLink } from "react-router-dom";
const Articles = () => {
return (
<div>
<h1>Articles Page</h1>
<Outlet />
<ul>
<ArticleItem id={1} />
<ArticleItem id={2} />
<ArticleItem id={3} />
</ul>
</div>
);
};
const ArticleItem = ({ id }) => {
const activeStyle = {
color: "blue",
fontSize: 20,
};
return (
<li>
<NavLink
to={`/articles/${id}`}
style={({ isActive }) => (isActive ? activeStyle : undefined)}
>
게시글 {id}
</NavLink>
</li>
);
};
export default Articles;
NotFound 페이지는 경로를 *(와일드 카드)로 설정해주면 만들 수 있습니다.
경로에 *(wild card문자) 뜻? 아무 텍스트나 매칭한다는 의미, 상단에 위치하는 라우트 엘리먼트의 규칙들을 모두 확인하고 일치하는 라우트가 없다면 해당 라우트가 화면에 나타납니다. 보통 최하단에 위치시켜 사용합니다.
<Route path="*" element={<NotFound/>}/>
⭐Navigate 컴포넌트
해당 컴포넌트는 컴포넌트를 화면에 보여주는 순간 다른 페이지로 이동하고 싶을 때 사용하는 컴포넌트입니다.
즉, 페이지를 리다이렉트하고 싶을 때 사용합니다.
예를 들어 사용자의 로그인이 필요한 페이지(e.g. 마이페이지)인데 로그인을 하지 않았다면 로그인 페이지를 보여주어야 할 때
src/pages/Mypage.js
여기서도 replace 속성을 사용할 수 있습니다. 이전에 설명할 때 경로 기록을 남기고싶지 않을 때 사용한다고 했던 것을 기억하시나요? true 값을 주면 기록에 남지않습니다. Navigate 컴포넌트와 같이 쓰였으니 기록에 남기지않고 login 페이지로 리다이렉트해주는 기능을 합니다.
import React from "react";
import { Navigate } from "react-router-dom";
function MyPage() {
const isLogin = false;
if (!isLogin) {
return <Navigate to="/login" replace={true}></Navigate>;
}
return <div>MyPage</div>;
}
export default MyPage;
- isLogin의 값이 false로 고정값이지만 예시를 위해서 해놓은 것으로 보통은 유동적인 값이겠지요.
src/App.js
import "./App.css";
import { Route, Routes } from "react-router-dom";
import Home from "./pages/Home";
import About from "./pages/About";
import Article from "./pages/Article";
import Articles from "./pages/Articles";
import Layout from "./Layout";
import Login from "./pages/Login";
import MyPage from "./pages/MyPage";
import NotFound from "./pages/NotFound";
function App() {
return (
<Routes>
<Route element={<Layout />}>
<Route index element={<Home />}></Route>
<Route path="/about" element={<About />}></Route>
<Route path="/articles" element={<Articles />}>
<Route path=":id" element={<Article />}></Route>
</Route>
</Route>
<Route path="/login" element={<Login />} />
<Route path="/mypage" element={<MyPage />} />
<Route path="*" element={<NotFound />} />
</Routes>
);
}
export default App;
-> 페이지가 로딩되는 순간 mypage로 가지는게 아니라 Login 페이지로 이동(리다이렉트)됩니다.
개인적으로 Navigate 컴포넌트로 로그인 별 Route를 해주어도 괜찮지만 그렇게 되면 매번 같은 코드를 여러번 작성해야 할 거 같아서 HOC(고차 컴포넌트)를 활용하여 로그인 여부에 따라 Route 권한을 주면 좋을 거 같다.
[참고 자료]: 벨로퍼트님 리액트라우터 v6
'Frontend > react.js' 카테고리의 다른 글
[React] 에러 핸들링, 리액트 ErrorBoundary (0) | 2022.10.20 |
---|---|
[React] memoization, fetch, useReducer, contextAPI (0) | 2022.10.20 |
[React JS] 리액트의 렌더링 (1) | 2022.09.23 |
[React JS] 고차 컴포넌트(HOC, Higher Order Component), React 메모이제이션 기능 (0) | 2022.09.22 |
[React JS] 리액트 기본 개념과 비동기 처리 복습 (1) | 2022.09.13 |
- Total
- Today
- Yesterday
- float 레이아웃
- 항해99추천비추천
- getStaticPaths
- 부트캠프항해
- 원티드 3월 프론트엔드 챌린지
- D 플래그
- 원티드 프리온보딩 FE 챌린지
- 형제 요소 선택자
- nvm경로 오류
- 원티드 프리온보딩 프론트엔드 챌린지 3일차
- is()
- 프리렌더링확인법
- getServerSideProps
- 프리온보딩 프론트엔드 챌린지 3월
- 항해99프론트후기
- fs모듈 넥스트
- reactAPI
- 항해99프론트
- ~ ^
- grid flex
- nvm 설치순서
- 타입스크립트 장점
- 원티드 FE 프리온보딩 챌린지
- && 셸 명령어
- 타입스크립트 DT
- text input pattern
- Prittier
- aspect-ratio
- 틸드와 캐럿
- tilde caret
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |