1. SPA란?
SPA(Single Page Application) : 한 개의 페이지로 이루어진 애플리케이션
MPA(Multi Page Application) : 여러 개의 페이지로 이루어진 애플리케이션
웹에서 제공하는 정보가 많기 때문에 새 화면을 보여줄 때 마다 서버측에서 성능상의 문제가 발생할 수 있다.
트래픽이 많이 나올 수 있고, 서버에 높은 부하가 걸릴수 있다.
라우팅 : 다른 주소에 다른 화면을 보여주는 것
리액트 라이브러리에는 내장 되있지 않기에 API를 직접 사용해 관리하거나,
라이브러리를 사용해 쉽게 구현할 수 있다.
SPA의 단점
- 앱의 규모가 커지면 JS파일이 너무 커진다.
페이지 로딩시 실제 방문하지 않을 수 있는 페이지의 스크립도 불러오기 때문이다. - JS를 이용해 라우팅을 관리하는 것은 JS를 실행하지 않는 일반 크롤러에서는 페이지 정보를
제대로 수집해 가지 못하는 잠재적인 단점
(SEO를 거쳐야 한다. SEO : Search Engine Optimization 검색엔진 최적화) - JS가 실행될 때까지 페이지가 비어있기에 로딩되어 실행되는 짧은 시간동안 흰페이지가 나타날 수 있다.
(이는 서버 사이드 렌더링을 통해 해결할 수 있다)
2. 프로젝트 준비 및 기본적인 사용법
- 프로젝트 생성 및 라이브러리 설치
// 프로젝트 생성
npm create react-app router-tutorial
cd router-tutorial
// 라이브러리 설치
npm add react-router-dom
- 프로젝트에 라우터 적용 index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
reportWebVitals();
- Route와 Routes
Route 컴포넌트 : 사용자의 현재 경로에 따라 다른 컴포넌트를 보여준다.
react-router 가 v6로 업데이트 되면서 많은 기능이 바뀌었다. 대표적으로 Route의 사용법이다.
무조건 Route를 Routes로 묶어 사용해야 렌더링이 된다.
(...)
{/* 새로운 버전의 react-route에서는 Route는 Routes를 사용해 래핑해야하고 element={<Home />}의 형태로 보여줄 컴포넌트를 설정해주면 된다 */}
<Routes>
{/* v6 부터는 exact 옵션이 삭제됬다. */}
<Route path="/" element={<Home />} />
{/* v6부터 path는 문자열만 올 수 있기에 배열 형태로 여러개를 적용하려면 map함수를 이용해야 한다. */}
{/* <Route path="/about" element={<About/>} /> */}
{["/about", "/info"].map(path => <Route key={path} path={path} element={<About />} />)}
<Route path="/profiles/*" element={<Profiles />} />
<Route path="/history" element={<HistorySample />} />
{/* v6이후 switch가 Routes로 바뀌었다. 존재하지 않는 페이지 주소 입력 시 */}
<Route path="/*" element={<h1>존재하지 않는 페이지 입니다.</h1>}/>
</Routes>
</div>
);
};
export default App;
- Link 컴포넌트를 사용해 다른 주소로 이동하기
Link 컴포넌트 : 클릭하면 다른 주소로 이동시켜주는 컴포넌트
리액트 라우터를 사용할 때는 a태그를 직접 사용해서는 안된다.
페이지를 전환하는 과정에서 페이지를 새로 불러오기에 애플리케이션이 들고있던 상태들을 모두 날리게 되기 때문이다.
Link 사용 예시
<Link to ="주소">내용</Link>
<ul>
<li>
<Link to="/">홈</Link>
</li>
<li>
<Link to="/about">설명</Link>
</li>
<li>
<Link to="/profiles">프로필</Link>
</li>
<li>
<Link to="/history">History 예제</Link>
</li>
</ul>
3. Route 하나에 여러 개의 path 설정하기
v6 부터는 path 값은 무조건 문자열 이여야 한다.
그래서 책에 나온 내용대로 배열을 객체화 하여 전달하는 방법은 동작하지 않는다.
그래서 대체방안으로 map 함수를 이용해 적용시켜주는 방법이 있다.
{/* v6부터 path는 문자열만 올 수 있기에 배열 형태로 여러개를 적용하려면 map함수를 이용해야 한다. */}
{/* <Route path="/about" element={<About/>} /> */}
{["/about", "/info"].map(path => <Route key={path} path={path} element={<About />} />)}
조심해야 할 점은 중괄호로 묶어 JS 값을 사용하여 함수를 이용하는 것이다.
4. URL 파라미터와 쿼리
파라미터 예시 : /profile/velopert
쿼리 예시 : /about/?details=true
파라미터는 일반적으로 특정 아이디 혹은 이름을 사용하여 조회할 때 사용하고
쿼리는 어떤 키워드를 검색하거나 페이지에 필요한 옵션을 전달할 때 사용한다.
- URL 파라미터
v5에서는 URL 파라미터를 사용할 때 라우트로 사용되는 컴포넌트에서 받아오는 match 객체 안의 params값을 참조했다.
(match객체 안에는 현재 컴포넌트가 어떤 경로 규칙에 의해 보이는지에 대한 정보가 들어있다)
하지만, v6에는 match 객체가 삭제되었으므로 useParams를 이용해 params를 사용해야 한다.
import { useParams } from 'react-router';
const data = {
juhyeon: {
name: '오주현',
description: '리액트를 배우고있는 개발자'
},
changmin: {
name: '오창민',
description: '동생'
}
};
const Profile = () => {
const { username } = useParams();
const profile = data[username];
if (!profile) {
return <div>존재하지 않는 사용자입니다.</div>
}
return (
<div>
<h3>
{username}({profile.name})
</h3>
<p>{profile.description}</p>
</div>
);
};
export default Profile;
(...)
<li>
<Link to="/profile/juhyeon">주현 프로필</Link>
</li>
(...)
<Route path="/profiles/:username" element={<Profiles />} />
(...)
- URL 쿼리
쿼리는 location 객체에 들어있는 search 값에서 조회할 수 있다.
location 객체는 라우트로 사용된 컴포넌트에게 props로 전달되며 웹 애플리케이션의 현재 주소에 대한 정보를 지는다.
location객체의 형태
{
"pathname": "/about",
"search": "?detail=true",
"hash": ""
}
URL 쿼리를 읽을 때는 위 객체가 지닌 값 중에서 search 값을 확인해야 한다.
useLocation 을 사용해 읽을 수 있다.
import { useLocation } from 'react-router-dom';
const About = () => {
const { search } = useLocation();
//현재 지금 경로가(search) "?detail=true"인지 확인
const showDetail = search === "?detail=true";
return (
<div>
<h1>소개</h1>
<p>이 프로젝트는 리액트 라우터 기초를 실습해 보는 예제 프로젝트입니다.</p>
{showDetail && <p>detail 값을 true로 설정되있음</p>}
</div>
);
};
export default About;
5. 서브 라우트
서브라우트 : 라우트 내부에 또 라우트를 정의 하는 것
import { Link, Route, Routes } from 'react-router-dom';
import Profile from './Profile';
const Profiles = () => {
const style = {
background: "black",
color: "white",
}
return (
<div>
<h3>사용자 목록</h3>
<ul>
<li>
<Link
to="/profiles/juhyeon"
>주현</Link>
</li>
<li>
<Link
to="/profiles/changmin"
>창민</Link>
</li>
</ul>
<Routes>
<Route
path="/*"
element={<div>사용자를 선택해 주세요.</div>}
/>
<Route path=":username" element={<Profile />} />
{/* /profiles/:username 으로 useParams()을 통해 username을 확인 */}
{/* /profiles/는 색략 가능 */}
</Routes>
</div>
);
};
export default Profiles;
import { Routes, Route, Link } from 'react-router-dom';
import Home from './Home';
import About from './About';
import Profiles from './Profiles';
(...)
<li>
<Link to="/profiles">프로필</Link>
</li>
(...)
<Route path="/profiles/*" element={<Profiles />} />
(...)
6. 리액트 라우터 부가 기능
- history
history객체 : 라우트로 사용된 컴포넌트에 match, location과 함께 전달되는 props중 하나로,
이 객체를 통해 컴포넌트 내에 구현하는 메서드에서 라우터 API를 호출할 수 있다.
(특정 버튼을 눌렀을 때 뒤로 가거나, 로그인 후 화면을 전환하거나, 다른 페이지로 이탈하는 것을 방지할 때 사용)
//history를 사용할 때
// import { createBrowserHistory } from 'history';
// const history = createBrowserHistory();
//v6이후 useHistory가 useNavigate로 변경 되었다.
import { useNavigate } from 'react-router';
//v6이후 Propmt가 사라졌기에 Propmt를 구현한 로직
import usePrompt from './Blocker';
const HistorySample = () => {
const navigate = useNavigate();
// 뒤로가기. 인덱스로 처리, 두번 뒤로 가려면 -2
const goBack = () => {
navigate(-1);
};
// 홈으로 가기
const goHome = () => {
navigate('/');
}
//v6이후 Prompt가 사자져서 사용하는 컴포넌트 (로직 구현)
usePrompt('현재 페이지를 벗어나겠습니까?', true);
return (
<div>
<button onClick={goBack}>뒤로가기</button>
<button onClick={goHome}>홈으로</button>
</div>
)
}
export default HistorySample;
(...)
import HistorySample from './HistorySample';
(...)
<li>
<Link to="/history">History 예제</Link>
</li>
(...)
<Route path="/history" element={<HistorySample />} />
(...)
- withRouter v6에서는 삭제됨
v6에서 withRouter가 삭제 되었다.
withRouter 함수는 match, location, history 객체를 접근할 수 있게해주는 함수였다.
v6에서 삭제 되어 각각 Hooks로 접근해야 한다.
(useParams, useLocation, useNavigate)
import { useParams, useLocation, useNavigate } from 'react-router';
const WithRouterSample = () => {
const params = useParams();
const location = useLocation();
const navigate = useNavigate();
return (
<div>
<h4>location</h4>
<textarea
value={JSON.stringify(location, null, 2)}
rows={7}
readOnly
/>
<h4>match</h4>
<textarea
value={JSON.stringify(params, null, 2)}
rows={7}
readOnly
/>
<button onClick={() => navigate('/')}>홈으로</button>
</div>
);
};
export default WithRouterSample;
import { NavLink, Route, Routes } from 'react-router-dom';
import Profile from './Profile';
import WithRouterSample from './WithRouterSample';
const Profiles = () => {
(...)
return (
<div>
(...)
<WithRouterSample/>
</div>
);
};
export default Profiles;
- Routes
Routes : 여러 Route를 감싸서 그중 일치하는 단 하나의 라우트만을 렌더링 해줌
<Routes>
{/* v6 부터는 exact 옵션이 삭제됬다. */}
<Route path="/" element={<Home />} />
{/* v6부터 path는 문자열만 올 수 있기에 배열 형태로 여러개를 적용하려면 map함수를 이용해야 한다. */}
{/* <Route path="/about" element={<About/>} /> */}
{["/about", "/info"].map(path => <Route key={path} path={path} element={<About />} />)}
<Route path="/profiles/*" element={<Profiles />} />
<Route path="/history" element={<HistorySample />} />
{/* v6이후 switch가 Routes로 바뀌었다. 존재하지 않는 페이지 주소 입력 시 */}
<Route path="/*" element={<h1>존재하지 않는 페이지 입니다.</h1>}/>
</Routes>
- NavLink
현재 경로와 Link에서 사용하는 경로가 일치하는 경우 특정 스타일 혹은 CSS 클래스를 적용할 수 있는 컴포넌트이다.
v6이후 activeStyle과 activeClassName이 없어졌다.
대신 className과 style을 사용한다.
import { NavLink, Route, Routes } from 'react-router-dom';
(...)
const Profiles = () => {
const style = {
background: "black",
color: "white",
}
return (
<div>
<h3>사용자 목록</h3>
<ul>
<li>
<NavLink
to="/profiles/juhyeon"
style={({isActive})=>isActive ? style : undefined}
>주현</NavLink>
</li>
<li>
<NavLink
to="/profiles/changmin"
style={({ isActive }) => isActive ? style : undefined}
//클래스네임 설정
// className={({ isActive }) => 'navlink' + (isActive ? 'activated' : '')}
>창민</NavLink>
</li>
</ul>
<Routes>
<Route
path="/*"
element={<div>사용자를 선택해 주세요.</div>}
/>
<Route path=":username" element={<Profile />} />
{/* /profiles/:username 으로 useParams()을 통해 username을 확인 */}
{/* /profiles/는 색략 가능 */}
</Routes>
<WithRouterSample/>
</div>
);
};
export default Profiles;
'React' 카테고리의 다른 글
React) 15장 Context API (0) | 2022.05.11 |
---|---|
React) 14장 외부 API를 이용한 뉴스 뷰어 (0) | 2022.05.10 |
React) 12장 immer 라이브러리 (0) | 2022.05.06 |
React) 11장 컴포넌트 최적화 (0) | 2022.04.19 |
React) 10장 TodoList 프로젝트 (0) | 2022.04.17 |