13. 라우터로 SPA 개발
✔ SPA
1. 전통적인 웹 페이지
- 페이지 이동에 따른 html 받아와 로딩할 때마다 서버에 리소스 전달 받아 해석 후 화면에 보여줌
- 사전에 html 파일 생성하거나 데이터에 따른 유동적인 html 생성하는 템플릿 엔진 사용
- 화면 전환마다 html 서버에 요청 시 UI 상태 유지 어려움, 불필요한 로딩있어 비효율적임
2. SPA (Single Page Application)
- 뷰 랜더링은 사용자 브라우저가 담당
- 어플리케이션을 브라우저에 불러오고 실행 후 사용자 인터렉션 발생 시 필요한 부분만 js 사용해 업데이트
3. React Router
- 다른 주소에 다른 화면 보여주기
- 클라이언트 사이드에서 이루어지는 라우팅 간편하게 구현
- 서버 사이드 랜더링 할 때 라우팅 도와주는 컴포넌트 제공
- 규모가 커지면 js도 커지지만 code splitting 사용해 라우트별 파일 나눠 트래픽과 로딩 속도 개선
✔ 준비 & 사용법
1. 라우터 설치
yarn add react-router-dom@^5.3.0
2. 라우터 적용
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import {BrowserRouter} from "react-router-dom";
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
3. 페이지 만들기
//Home
import React from 'react';
const Home=()=>{
return (
<div>
<h1>홈</h1>
<p>홈, 그 페이지는 가장 먼저 보이는 페이지</p>
</div>
)
}
export default Home;
//About
import React from 'react';
const About = ()=>{
return (
<div>
<h1>소개</h1>
<p>이 프로젝트는 리액트 라우터 기초를 실습해보는 예제</p>
</div>
)
}
export default About;
3. Route 컴포넌트로 특정 주소에 컴포넌트 연결
어떤 규칙을 가진 경로에 어떤 컴포넌트 보여줄지 정의
<Route path="주소 규칙" component={보여 줄 컴포넌트}>
import React from "react";
import { Route,Link } from "react-router-dom";
import Home from "./components/Home";
import About from './components/About';
const App = () => {
return (
<div>
<Route path="/" component={Home} exact={true} />
<Route path="/about" component={About}/>
</div>
);
};
export default App;
- 기본 path인
path="/"
에exact={true}
를 설정해야 /로 시작하는 라우팅과 겹치지 않음
exact=true 설정하지 않고 /about으로 페이지 이동을 한 경우 /와 /about로 설정한 것 동시에 등장
4. Link 컴포넌트 사용해 다른 주소로 이동
- Link 컴포넌트를 사용해 페이지 전체 새로 불러오지 않고 HTML5 History API 사용해 페이지 주소만 변경함
<Link to="주소">내용</Link>
import React from "react";
import { Route,Link } from "react-router-dom";
import Home from "./components/Home";
import About from './components/About';
const App = () => {
return (
<div>
<ul>
<li><Link to="/"> 홈</Link></li>
<li><Link to="/about">about</Link></li>
</ul>
<Route path="/" component={Home} exact={true} />
<Route path="/about" component={About}/>
</div>
);
};
export default App;
홈 클릭 | about 클릭 |
---|---|
✔ Route 하나에 여러개 path 설정
/about
과/info
모두 about 페이지로 가도록 한 번에 path 설정
import React from "react";
import { Route,Link } from "react-router-dom";
import Home from "./components/Home";
import About from './components/About';
const App = () => {
return (
<div>
<ul>
<li><Link to="/"> 홈</Link></li>
<li><Link to="/about">about</Link></li>
</ul>
<Route path="/" component={Home} exact={true} />
<Route path={["/about","/info"]} component={About}/>
</div>
);
};
export default App;
✔ URL 파라미터와 쿼리
- 유동적인 값으로 페이지 주소를 정의하는 경우
- 쿼리 : 키워드를 검색하거나 페이지에 필요한 옵션 전달
- 파라미터 : 특정 아이디 혹은 이름 사용해 조회하는 경우
1. URL 파라미터
- 컴포넌트에서 받은 match 객체 안의 params 값 참조해 url 파라미터로 사용
- path 규칙은
/profiles/:username
일 때,match.params.username
을 사용해 파라미터인 username값 조회 가능
import React from 'react';
const data={
velopert:{
name:"김민준",
description :"리액트를 좋아하는 개발자"
},
gildong:{
name:"홍길동",
description:"고전 소설 주인공"
}
};
const Profile=({match})=>{
const {username}=match.params;
//path 규칙에서 파라미터인 username 부분 받아오기
const profile=data[username];
if(!profile){
return <div>존재하지 않는 사용자임</div>
}
return (
<div>
<h3>{username}({profile.name})</h3>
<p>{profile.description}</p>
</div>
);
};
export default Profile;
import React from "react";
import { Route,Link } from "react-router-dom";
import Home from "./components/Home";
import About from './components/About';
import Profile from './components/Profile';
const App = () => {
return (
<div>
<ul>
<li><Link to="/"> 홈</Link></li>
<li><Link to="/about">about</Link></li>
<li><Link to="/profile/velopert">velopert 프로필</Link></li>
<li><Link to="/profile/gildong">gildong 프로필</Link></li>
</ul>
<Route path="/" component={Home} exact={true} />
<Route path={["/about","/info"]} component={About}/>
<Route path="/profile/:username" component={Profile}/>
</div>
);
};
export default App;
2. URL 쿼리
쿼리 문자열을 객체로 변환하는 라이브러리 설치
yarn add qs
숫자 (?value=1) 또는 논리 자료형 (boolean) 사용 시 문자열 형태로 받아짐
숫자는 parseInt 사용해 변환 & 논리 자료형 값 사용해 "true"와 일치하는 지 확인
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.js
import React from "react";
import { Route, Link } from "react-router-dom";
import About from "./components/About";
import Home from "./components/Home";
import Profiles from "./components/Profiles";
const App = () => {
return (
<div>
<ul>
<li>
<Link to="/">HOME</Link>
</li>
<li>
<Link to="/about">ABOUT</Link>
</li>
<li>
<Link to="/profiles">프로필</Link>
</li>
</ul>
<Route path="/" component={Home} exact={true} />
<Route path={["/about", "/info"]} component={About} />
<Route path="/profiles" component={Profiles} />
/*Profiles 컴포넌트를 /profiles 경로에 연결*/
</div>
);
};
export default App;
Profiles.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;
- 첫번째 Route에는 Component가 아닌 render 사용해 보여주고 싶은 JSX 넣음
✔ 리액트 라우터 부가 기능
1. history
- Route로 사용된 컴포넌트에 전달되는 props : match, location, history
- 컴포넌트 내 메서드에서 라우터 API 호출 가능
(특정 버튼 눌러서 뒤로 가기, 로그인 후 화면 전환, 다른 페이지로 이탈 방지)
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 "./components/About";
import Home from "./components/Home";
import Profiles from "./components/Profiles";
import HistorySample from './components/HistorySample';
const App = () => {
return (
<div>
<ul>
<li>
<Link to="/">HOME</Link>
</li>
<li>
<Link to="/about">ABOUT</Link>
</li>
<li>
<Link to="/profiles">프로필</Link>
</li>
<li><Link to="/history">History 예제</Link></li>
</ul>
<Route path="/" component={Home} exact={true} />
<Route path={["/about", "/info"]} component={About} />
<Route path="/profiles" component={Profiles} />
<Route path="/history" component={HistorySample}/>
</div>
);
};
export default App;
2. withRouter
- Higher-order Component
- 라우팅 사용 컴포넌트 아니여도 match, location, history 객체 접근 가능
- withRouter를 사용할 때 함수로 감싸 컴포넌트를 내보냄
- withRouter 사용시 현재 자기를 기준으로 match 전달됨
withRouter(WithRouterSample)
:path="/profiles"
라고만 해서 username 파라미터 읽어온 것 없음
withRouter(Profile)
:path="/profiles/:username"
이라고 하여 username 파라미터 읽어옴
WithRouterSample.js
import React from 'react';
import { withRouter } from 'react-router-dom';
import Profiles from './Profiles';
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,3)}
rows={7}
readOnly={true}
/>
<button onClick={()=>history.push("/")}>홈으로</button>
</div>
);
};
export default withRouter(WithRouterSample);
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;
Profiles.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 }) => {
console.log(match)
const { username } = match.params;
console.log(username);
const profile = data[username];
if (!profile) {
return <div>존재하지 않는 사용자입니다.</div>;
}
return (
<div>
<h3>
{username}({profile.name})
</h3>
<p>{profile.description}</p>
<WithRouterSample/>
</div>
);
};
export default withRouter(Profile);
3. Switch
- 여러 Route 감싸 그 중 일치하는 단 하나의 라우트만 랜더링 시켜줌
- 모든 주소 규칙과 일치 하지 않는 경우 보여 줄 Not Found 페이지 구현 가능
import React from "react";
import { Route, Link, Switch } from "react-router-dom";
import About from "./components/About";
import Home from "./components/Home";
import Profiles from "./components/Profiles";
import HistorySample from './components/HistorySample';
const App = () => {
return (
<div>
<ul>
<li>
<Link to="/">HOME</Link>
</li>
<li>
<Link to="/about">ABOUT</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="/profiles" component={Profiles} />
<Route path="/history" component={HistorySample}/>
{/* path를 따로 정의 하지 않으면 모든 상황에 랜더링됨*/}
<Route render={({location})=>(
<div>
<h2>이 페이지는 존재하지 않습니다.</h2>
<p>{location.pathname}</p>
</div>
)}/>
</Switch>
</div>
);
};
export default App;
4. NavLink
- 현재 경로와 Link에서 사용하는 경로가 일치하는 경우 특정 스타일/CSS 클래스 적용 가능한 컴포넌트
- 링크 활성화 되었을 때 스타일 적용하는 activeStyle
- CSS 클래스 적용할 때 activeClassName 값을 props로 넣기
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">velopert</NavLink>
</li>
<li>
<NavLink
activeStyle={activeStyle}
to="/profiles/gildong">gildong</NavLink>
</li>
</ul>
<Route path="/profiles" exact render={()=><div>사용자를 선택해 주세요</div>}/>
<Route path="/profiles/:username" component={Profile}/>
</div>
);
};
export default Profiles;
Author And Source
이 문제에 관하여(13. 라우터로 SPA 개발), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@hixkix59/13.-라우터로-SPA-개발저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)