Today I Learned
- React SPA
- React Router
서버로부터 완전한 새로운 페이지를 불러오지 않고 페이지 갱신에 필요한 데이터만 받아 그 정보를 기준으로 현재의 페이지를 업데이트함으로써 사용자와 소통하는 웹 애플리케이션이나 웹 사이트
전통적은 웹사이트는 페이지 이동 시 매번 HTML 파일로 된 페이지 전체를 불러와야만 했다. 웹사이트가 복잡해지고 애플리케이션의 형태를 가지게 되면서, 사용자와 서비스 사이에 더 많은 상호작용이 일어나게 되었다. 이때마다 Header나 Navigation Bar 등과 같이 중복되는 요소들을 매번 불러오는 것이 서버와의 불필요한 트래픽을 발생시켜 반응이 느려졌고, 이는 사용자 경험을 저하시켰다. 이러한 문제점을 개선하기 위해 SPA가 개발되었고 사용되고 있다.
SPA는 하나의 페이지를 가지고 있지만 사실 한 종류의 화면만 사용하지 않는다. 예를 들자면 알림 페이지, 마이 페이지 등의 화면이 필요하고 이 화면에 따라 '주소'도 달라진다. 이렇게 다른 주소에 따라 다른 뷰를 보여주는 과정을 '경로에 따라 변경한다.'라는 의미로 라우팅(routing)이라고 한다.
하지만 React 자체에는 이 기능이 내장돼있지 않다. 우리가 직접 주소마다 다른 뷰를 보여줘야 한다. React SPA에서는 라우팅을 위해 React Router
라는 라이브러리를 가장 많이 사용한다.
React Router의 주요 컴포넌트는 크게 3가지로 분류된다.
<BrowserRouter>
: 라우터 역할, 웹 애플리케이션에서 HTML5의 History API를 사용해 페이지를 새로고침하지 않고도 주소를 변경할 수 있게 해준다. react-router-dom 상위에 위치해야 React Router의 컴포넌트들을 사용할 수 있다.
<Routes>
, <Route>
: 경로 매칭 역할
<Routes>
컴포넌트는 여러 <Route>
컴포넌트를 감싸서 그 중 경로가 일치하는 단 하나의 라우터만 렌더링 시켜준다. <Routes>
를 사용하지 않으면 매칭되는 모든 요소들을 렌더링한다.<Route>
컴포넌트는 path
속성으로 경로를, element
속성으로 컴포넌트를 연결한다.<Route>
는 <Routes>
로 감싸주어야 한다.사용자가 지정된 주소(path) 이외의 주소로 접근할 시 의도한 화면이 보이지 않을 수 있다. 이때,
path
속성을path="*"
으로 지정하면 지정되지 않은 주소로 접근할 시 이 속성이 설정돼 있는 컴포넌트를 보여준다.
<Link>
: 경로 연결 역할, 페이지 전환을 통해 페이지를 새로 불러오지 않고(새로고침 하지 않고) 애플리케이션을 그대로 유지하여 HTML5 Histoty API를 이용해 페이지의 주소만 변경한다.to
속성을 활용해 <Route>
컴포넌트의 path
주소를 연결한다.
<a>
태그가 아닌<Link>
컴포넌트를 사용하는 이유
<a>
:href
속성의 URL로 접근 시, 새로고침 되면서 페이지를 새로 불러온다. 따라서 상태값이 유지되지 못하고 속도도 저하된다.<Link>
: 브라우저의 주소만 바뀌면서 업데이트 되고, 페이지를 새로 불러오지는 않는다. (새로고침하지 않는다.)
이 컴포넌트들을 사용하기 위해서는 React Router 라이브러리에서 아래와 같은 명령어를 사용해 파일로 따로 불러와야 한다.
import { BrowserRouter, Routes, Route, Link } from "react-router-dom";
import
는 필요한 모듈을 불러오는 역할로 비구조화 할당(destructuring assignment)과 비슷하게 이용할 수 있다. (위의 경우, react-router-dom에서 필요한 컴포넌트들만 import해왔다. 컴포넌트 지정없이 react-router-dom의 모든 컴포넌트를 import하면 속도가 저하된다.)
npx create-react-app 프로젝트명 ( or .(현재위치) )
cd 프로젝트명
npm install react-router-dom
라이브러리가 정상적으로 설치됐다면 package.json
파일의 dependencies
항목에 react-router-dom
이라는 라이브러리가 등록된 것을 확인할 수 있다.
(1) App.js
import React from 'react';
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
// (1) App 컴포넌트
function App() {
return (
<BrowserRouter>
<div>
<nav>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/mypage">MyPage</Link>
</li>
<li>
<Link to="/dashboard">Dashboard</Link>
</li>
</ul>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/mypage" element={<MyPage />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</div>
</BrowserRouter>
);
}
function Home() {
return <h1>Home</h1>;
}
function MyPage() {
return <h1>MyPage</h1>;
}
function Dashboard() {
return <h1>Dashboard</h1>;
}
export default App;
// (2) export default를 App 앞 함수에 적을 수도 있다.
export default function App() {
return (...) // 생략
}
(2) App.js가 아닌 index.js에 <BrowserRouter>
를 넣어서 활용할 수도 있다. 이때는 App.js에는 <BrowserRouter>
를 따로 작성하지 않는다.
index.js는 ReactDOM을 렌더하는 단계이므로 index.js에 <BrowserRouter>
를 넣으면 App.js의 App 컴포넌트의 상위, react-router-dom의 최상위에 위치하게 된다.
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</React.StrictMode>
);
<useNavigate>
<useNavigate>
: 특정 행동을 했을 때 해당 주소로 이동시켜주는 역할
<useNavigate>
는 <Link>
와는 달리 함수 호출을 통해 특정 조건에 따라 페이지를 이동할 수 있다.
import { useNavigate } from "react-router-dom";
function MypageForm() {
let navigate = useNavigate();
function handleClick() {
if(로그인 한 상태) {
navigate('/mypage');
} else {
navigate('/login');
}
}
return <button onClick={handleClick}>마이페이지</button>;
}