SPA(Single Page Application)
한 개의 페이지로 이루어진 애플리케이션이라는 의미
먼저, SPA를 사용하는 이유에 대해 알아보자!
1. 웹에서 제공되는 정보가 많아 새로운 화면을 보여 줘야 할 때마다 서버 측에서 모든 뷰를 준비한다면 성능상의 문제가 발생할 수 있다. (트래픽 ↑, 서버 과부하)
2. html을 서버에 요청할 때, 인터페이스에서 사용하고 있던 상태를 유지하는 것도 번거롭고, 바뀌지 않는 부분까지 새로 불러와 보여 주어야 하기 때문에 불필요한 로딩이 있어 비효율적이다.
SPA는 서버에서 사용자에게 제공하는 페이지는 한 종류이지만,
해당 페이지에서 로딩된 자바스크립트와 함께, 사용자와 브라우저의 주소 상태에 따라
다양한 화면을 보여 줄 수 있다.
→ 이것이 바로 라우팅!
- 프로젝트 생성하기
$ yarn create react-app router-tutorial
- 라우터 설치하기
$ yarn add react-router-dom@5
<적용법>
src/index.js 파일에서 react-router-dom에 내장되어 있는 BrowserRouter라는 컴포넌트를 사용하여 감싼다.
src/index.js
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById("root")
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
라우트로 사용할 페이지 컴포넌트 만들기
Home.js
import React from "react";
const Home = () => {
return (
<div>
<h1>홈</h1>
<p>홈, 그 페이지는 가장 먼저 보여지는 페이지.</p>
</div>
);
};
export default Home;
About.js
import React from "react";
const About = () => {
return (
<div>
<h1>소개</h1>
<p>이 프로젝트는 리액트 라우터 기초를 실습해 보는 예제 프로젝트입니다.</p>
</div>
);
};
export default About;
<Route 컴포넌트 사용방식>
<Route path=”주소 규칙” component={보여 줄 컴포넌트} />
App.js
import React from "react";
import { Route, Routes } from "react-router-dom";
import About from "./About";
import Home from "./Home";
const App = () => {
return (
<div>
<Route path="/" component={Home} />
<Route path="/about" component={About} />
</div>
);
};
export default App;
<결과보기>
<Route path="/" component={Home} exact={true} />
<Link to =”주소”>내용</Link>
import React from "react";
import { Route, Link } from "react-router-dom";
import About from "./About";
import Home from "./Home";
const App = () => {
return (
<div>
<ul>
<li>
<Link to="/">홈</Link>
</li>
<li>
<Link to="/about">소개</Link>
</li>
</ul>
<hr />
<Route path="/" component={Home} />
<Route path="/about" component={About} />
</div>
);
};
export default App;
import React from "react";
import { Route, Link } from "react-router-dom";
import About from "./About";
import Home from "./Home";
const App = () => {
return (
<div>
<ul>
<li>
<Link to="/">홈</Link>
</li>
<li>
<Link to="/about">소개</Link>
</li>
</ul>
<hr />
<Route path="/" component={Home} />
<Route path={["/about", "/info"]} component={About} />
</div>
);
};
export default App;
페이지 주소를 정의할 때 유동적인 값을 전달해야 할 경우 파라미터나 쿼리를 사용한다.
일반적으로 파라미터는 특정 아이디 혹은 이름을 사용하여 조회할 때 사용하고
쿼리는 어떤 키워드를 검색하거나 페이지에 필요한 옵션을 전달할 때 사용한다.
/profile/velopert와 같은 형식으로
뒷부분에 유동적인 username 값을 넣어 줄 때
해당 값을 props로 받아 와 조회하는 방법을 알아보자
Profile.js
import React from "react";
const data = {
velopert: {
name: "김민준",
description: "리액트를 좋아하는 개발자",
},
gildong: {
name: "홍길동",
description: "고전 소설 홍길동전의 주인공",
},
};
const Profile = ({ match }) => {
const { username } = match.params;
const profile = data[username];
if (!profile) {
return <div>존재하지 않는 사용자입니다.</div>;
}
return (
<div>
<h3>
{username}({profile.name})
</h3>
<p>{profile.description}</p>
</div>
);
};
export default Profile;
App 컴포넌트에서 Profile 컴포넌트를 위한 라우터 정의하기
import React from "react";
import { Route, Link } from "react-router-dom";
import About from "./About";
import Home from "./Home";
import Profile from "./Profile";
const App = () => {
return (
<div>
<ul>
<li>
<Link to="/">홈</Link>
</li>
<li>
<Link to="/about">소개</Link>
</li>
<li>
<Link to="/profile/velopert">velopert 프로필</Link>
</li>
<li>
<Link to="/profile/gildong">gildong 프로필</Link>
</li>
</ul>
<hr />
<Route path="/" component={Home} />
<Route path={["/about", "/info"]} component={About} />
<Route path="/profile/:username" component={Profile} />
</div>
);
};
export default App;
쿼리는 location 객체에 들어 있는 search 값에서 조회 가능하며
location 객체는 라우트로 사용된 컴포넌트에게 props로 전달된다.
{
"pathname": "about",
"search": "?detail=true",
"hash": ""
}
▲ http://localhost:3000/about?detail=true 주소로 들어갔을 때의 location 객체의 값
$yarn add qs
About 컴포넌트에서 location.search값에 있는 detail이 true인지 아닌지에 따라 추가 정보를 보여 주도록 만들어보자
About.js
import React from "react";
import qs from "qs";
const About = ({ location }) => {
const query = qs.parse(location.search, {
ignoreQueryPrefix: true, // 이 설정을 통해 문자열 맨 앞의 ?를 생략합니다.
});
const showDetail = query.detail === "true"; // 쿼리의 파싱 결과 값은 문자열입니다.
return (
<div>
<h1>소개</h1>
<p>이 프로젝트는 리액트 라우터 기초를 실습해 보는 예제 프로젝트입니다.</p>
{showDetail && <p>detail 값을 true로 설정하셨군요!</p>}
</div>
);
};
export default About;
라우트 내부에 또 다른 라우트를 정의하는 것
APP 컴포넌트에서 두 종류의 프로필 링크를 보여 줬는데,
이를 잘라내서 프로필 링크를 보여 주는 Profiles라는 라우트 컴포넌트를 따로 만들고,
그 안에서 Profile 컴포넌트를 서브 라우트로 사용하도록 한다.
Profile.js
import React from 'react';
import { Link, Route } from 'react-router-dom';
import Profile from './Profile';
const Profiles = () => {
return (
<div>
<h3>사용자 목록:</h3>
<ul>
<li>
<Link to="/profiles/velopert">velopert</Link>
</li>
<li>
<Link to="/profiles/gildong">gildong</Link>
</li>
</ul>
<Route
path="/profiles"
exact
render={() => <div>사용자를 선택해 주세요.</div>}
/>
<Route path="/profiles/:username" component={Profile} />
</div>
);
};
export default Profiles;
첫번째 라우터의 render props
→ 컴포넌트 자체를 전달하는 것이 아닌 보여주고 싶은 JSX를 넣어 줄 수 있다.
App.js
import React from "react";
import { Route, Link } from "react-router-dom";
import About from "./About";
import Home from "./Home";
import Profile from "./Profile";
import Profiles from "./Profiles";
const App = () => {
return (
<div>
<ul>
<li>
<Link to="/">홈</Link>
</li>
<li>
<Link to="/about">소개</Link>
</li>
<li>
<Link to="/profile/velopert">velopert 프로필</Link>
</li>
<li>
<Link to="/profile/gildong">gildong 프로필</Link>
</li>
<li>
<Link to="/profiles">프로필</Link>
</li>
</ul>
<hr />
<Route path="/" component={Home} exact={true} />
<Route path={["/about", "/info"]} component={About} />
<Route path="/profile/:username" component={Profile} />
<Route path="/profiles" component={Profiles} />
</div>
);
};
export default App;
HistorySample.js
import React, { Component } from "react";
class HistorySample extends Component {
// 뒤로 가기
handleGoBack = () => {
this.props.history.goBack();
};
// 홈으로 이동
handleGoHome = () => {
this.props.history.push("/");
};
componentDidMount() {
// 이것을 설정하고 나면 페이지에 변화가 생기려고 할 때마다 정말 나갈 것인지를 질문함
this.unblock = this.props.history.block("정말 떠나실 건가요?");
}
componentWillUnmount() {
// 컴포넌트가 언마운트되면 질문을 멈춤
if (this.unblock) {
this.unblock();
}
}
render() {
return (
<div>
<button onClick={this.handleGoBack}>뒤로</button>
<button onClick={this.handleGoHome}>홈으로</button>
</div>
);
}
}
export default HistorySample;
App.js
import React from "react";
import { Route, Link } from "react-router-dom";
import About from "./About";
import Home from "./Home";
import Profile from "./Profile";
import Profiles from "./Profiles";
import HistorySample from "./HistorySample";
const App = () => {
return (
<div>
<ul>
<li>
<Link to="/">홈</Link>
</li>
<li>
<Link to="/about">소개</Link>
</li>
<li>
<Link to="/profile/velopert">velopert 프로필</Link>
</li>
<li>
<Link to="/profile/gildong">gildong 프로필</Link>
</li>
<li>
<Link to="/profiles">프로필</Link>
</li>
<li>
<Link to="history">History 예제</Link>
</li>
</ul>
<hr />
<Route path="/" component={Home} exact={true} />
<Route path={["/about", "/info"]} component={About} />
<Route path="/profile/:username" component={Profile} />
<Route path="/profiles" component={Profiles} />
<Route path="/history" component={HistorySample} />
</div>
);
};
export default App;
withRouter함수는 HoC(Higher-order Component)이다.
라우트로 사용된 컴포넌트가 아니어도 match, location, history 객체를 접근할 수 있게 해 준다.
WithRouterSample.js
import React from ‘react‘;
import { withRouter } from ‘react-router-dom‘;
const WithRouterSample = ({ location, match, history }) => {
return (
<div>
<h4>location</h4>
<textarea
value={JSON.stringify(location, null, 2)}
rows={7}
readOnly={true}
/>
<h4>match</h4>
<textarea
value={JSON.stringify(match, null, 2)}
rows={7}
readOnly={true}
/>
<button onClick={() => history.push(‘/‘)}>홈으로</button>
</div>
);
};
export default withRouter(WithRouterSample);
Profile.js
import React from "react";
import { withRouter } from "react-router-dom";
import WithRouterSample from "./WithRouterSample";
const data = {
velopert: {
name: "김민준",
description: "리액트를 좋아하는 개발자",
},
gildong: {
name: "홍길동",
description: "고전 소설 홍길동전의 주인공",
},
};
const Profile = ({ match }) => {
const { username } = match.params;
const profile = data[username];
if (!profile) {
return <div>존재하지 않는 사용자입니다.</div>;
}
return (
<div>
<h3>
{username}({profile.name})
</h3>
<p>{profile.description}</p>
<WithRouterSample />
</div>
);
};
export default Profile;
import React from "react";
import { Route, Link, Switch } from "react-router-dom";
import About from "./About";
import Home from "./Home";
import Profile from "./Profile";
import Profiles from "./Profiles";
import HistorySample from "./HistorySample";
const App = () => {
return (
<div>
<ul>
<li>
<Link to="/">홈</Link>
</li>
<li>
<Link to="/about">소개</Link>
</li>
<li>
<Link to="/profile/velopert">velopert 프로필</Link>
</li>
<li>
<Link to="/profile/gildong">gildong 프로필</Link>
</li>
<li>
<Link to="/profiles">프로필</Link>
</li>
<li>
<Link to="history">History 예제</Link>
</li>
</ul>
<hr />
<Switch>
<Route path="/" component={Home} exact={true} />
<Route path={["/about", "/info"]} component={About} />
<Route path="/profile/:username" component={Profile} />
<Route path="/profiles" component={Profiles} />
<Route path="/history" component={HistorySample} />
<Route
// path를 따로 정의하지 않으면 모든 상황에 렌더링됨
render={({ location }) => (
<div>
<h2>이 페이지는 존재하지 않습니다:</h2>
<p>{location.pathname}</p>
</div>
)}
/>
</Switch>
</div>
);
};
export default App;
import React from "react";
import { NavLink, Route } from "react-router-dom";
import Profile from "./Profile";
const Profiles = () => {
const activeStyle = {
background: "black",
color: "white",
};
return (
<div>
<h3>사용자 목록:</h3>
<ul>
<li>
<NavLink activeStyle={activeStyle} to="/profiles/velopert" active>
velopert
</NavLink>
</li>
<li>
<NavLink activeStyle={activeStyle} to="/profiles/gildong">
gildong
</NavLink>
</li>
</ul>
(…)
</div>
);
};
export default Profiles;
Q1) 다른 주소에 다른 화면을 보여주는 것을 뭐라고 할까요
라우팅
Q2) URL에 유동적인 값을 전달할 때 사용하는 2가지를 적어주세요
파라미터, 쿼리
Q3) 라우트의 부가기능 중 라우트로 사용된 컴포넌트가 아니어도 match, location, history 객체를 접근할 수 있게 해주는 함수는 무엇일까요
withRouter
[리액트를 다루는 기술] 15장 Context API (0) | 2022.01.10 |
---|---|
[리액트를 다루는 기술] 14장 외부 API를 연동하여 뉴스 뷰어 만들기 (0) | 2022.01.03 |
[리액트를 다루는 기술] 12장 immer를 사용하여 더 쉽게 불변성 유지하기 (0) | 2021.12.27 |
[리액트를 다루는 기술] 11장 컴포넌트 성능 최적화 (0) | 2021.12.27 |
React와 Express를 사용해 메모장 만들기(Hooks 사용 ver.) (0) | 2021.12.01 |