전통적인 브라우저 : 필요할 때마다 서버에 요청해서 데이터를 받아옴
SPA(Single page application) : 하나의 큰 앱을 받아 온 후 브라우저(client)에서 보여줄 것들을 취사선택
리액트 라우터 돔(React router dom) = 브라우저에서 어떤 컴포넌트를 보여줄지에 대한 로직을 처리
npm i react-router-dom
npx create-react-app react-router-example
-> cd react-router-example
-> npm i react-router-dom // 리액트 라우터 설치
-> code . -r
src폴더 안에서
pages폴더 생성 -> Home.jsx , Profile.jsx , About.jsx 파일 생성
Home.jsx ->export default function Home() { return <div>Home페이지 입니다.</div>
Profile.jsx ->
export default function Profile() { return <div>Profile페이지 입니다.</div>
About.jsx ->
export default function About() { return <div>About페이지 입니다.</div>
App.js ->
App 함수 내용 전부 지우기
import {BrowserRouter, Route} from 'react-router-dom';
function App() {
return (
<BrowserRotuer> // react-router-dom에서 가져옴 (= import {BrowserRouter} from 'react-router-dom';
<Route path="/" element={<Home />} // Route = 라우터 돔, path= 경로, element = 보여줄 컴포넌트
<Route path="/Profile" element={<Home />}
<Route path="/About" element={<About />}
<Route path="/Profile/About" element={<Profile />}>
// 다 컴포넌트가 중복되어 나옴 ->
/*
- <Route path="/" exact element={Home} // v6에서 exact삭제
- exact 는 더이상 사용하지 않고 여러 라우팅을 매칭하고 싶은 경우 URL 뒤에 * 을 사용합니다.
ex) <Route path="/page1/*" element={<Page1 />} />
- component 방식 변경 (component={COM} 및 render={() => <h1>Hello<h1/>} 삭제)
- path 를 기존의 path="/Web/:id" 에서 path=":id" 로, 상대경로로 지정
- 이 외에도, path="." / path=".." 등으로 상대경로를 표현한다
*/
</BrowserRotuer>
)
}
Dynamic라우팅 : 컴포넌트의 경로가 동적일 때 사용
App.js ->
return (
<BrowserRotuer>
<Route path="/" exact component={Home}
<Route path="/Profile" exact component={Profile}
//----//
// /profile/1 의 경로를 가진 컴포넌트를 가지고 싶다면
<Route path="/Profile/:id" component={Profile} // :[이름]을 통해 컴포넌트에서 데이터를 받아 사용가능
//-----//
<Route path="/About" component={About}
> </BrowserRotuer>
)
Profile.js ->
export default function Profile(props) { // 컴포넌트에 데이터가 들어온다면 props를 통해 들어옴
console.log(props)
return <div>Profile페이지 입니다.</div>
}
props =
= id값은 props.match.params.id 를 통해 접근가능
Profile.js ->
export default function Profile(props) {
console.log(props)
const id = props.match.params.id
console.log(id, typeof id) // id의 타입은 string
return (
<div>
<h2>Profile페이지 입니다.</h2>
{id && <p>는 id는 {id}입니다.</p>}
</div>
)
}
==
localhost:3000/about?name=mark에서
경로에 about?name=mark 와 같은 querystring은 about과 같아서 추가적으로 라우팅할 필요가 없다.(=옵션)
props.location.search으로 name=mark에 접근가능
about.jsx =>
export default function About(props) {
console.log(props)
return <div>About페이지 입니다.</div>
}
about.jsx =>
export default function About(props) {
console.log(props)
const searchParams = props.location.search;
console.log(searchParams)
const obj = new URLSearchParams(searchParams)
console.log(obj.get('name')) // >> 'mark'
// 단점 1. RLSearchParams의 메서드를 외워야함
// 2. URLSearchParams지원 안하는 브라우저가 있음(Internet Explorer)
return <div>About페이지 입니다.</div>
}
// npm i query-string -S
about.jsx =>
import queryString from 'query-string'
export default function About(props) {
console.log(props)
const searchParams = props.location.search;
console.log(searchParams)
const query = queryString.parse(searchParams)
console.log(query) // >> {name:"mark"} 라는 객체로 반환
return (
<div>
<h2>About페이지 입니다.</h2>
{query.name && <p>name 은 {query.name}입니다.</p>}
</div>
)
}
return (
<BrowserRotuer>
<Route path="/" exact component={Home}
<Route path="/Profile" exact component={Profile}
//----//
<Route path="/Profile/:id" component={Profile}
//-----//
<Route path="/About" component={About}
> </BrowserRotuer>
)
Switch 컴포넌트 ==> Routes로 변경(v6~)
App.jsx =>
return (
<BrowserRotuer>
<Switch> // 컴포넌트를 돌며 가장 적합한 컴포넌트를 보여줌.
// 가장 좁거나 하위 컴포넌트부터 돌아야 적합한 결과를 얻을 수 있음
<Route path="/Profile/:id" component={Profile}>
<Route path="/Profile" component={Profile}>
<Route path="/About" component={About}>
<Route path="/" exact component={Home}>
// switch를 쓰면 exact는 가장 마지막 컴포넌트에만 붙이면됨
// 다 돌아도 찾을 수 없으면 Not found 컴포넌트를 보여줌
<Route component={NotFound}> // NotFound 컴포넌트는 경로를 지정안해도됨
</Switch>
</BrowserRotuer>
)
src => pages => NotFound.jsx 생성
NotFound.jsx =>
export default function NotFound() {
return <div>페이지를 찾을 수 없습니다</div>
}
react application 내부에서 페이지를 이동할 때 a태그 사용시 리액트에서 문제 생김 => 새로 서버에서 받아옴
react-router-dom 의 Link 컴포넌트
를 통해 서버를 통하지 않고 링크를 변경할 수 있음
App.jsx =>
return (
<BrowserRotuer>
<Link to="/">Home<Link>
<Switch>
<Route path="/Profile/:id" component={Profile}>
<Route path="/Profile" component={Profile}>
<Route path="/About" component={About}>
<Route path="/" exact component={Home}>
<Route component={NotFound}> // NotFound 컴포넌트는 경로를 지정안해도됨
</Switch>
</BrowserRotuer>
)
src => components 폴더 생성 => Links.jsx 생성
Links.jsx =>
import {Link} from 'react-router-dom'
export default function Links() {
return (
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/profile">Profile</Link>
</li>
<li>
<Link to="/profile/1">Profile/1</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/about?name=mark">About?name=mark</Link>
</li>
</ul>
)
}
App.jsx =>
return (
<BrowserRotuer>
<Links /> // -----------//
<Switch>
<Route path="/Profile/:id" component={Profile}>
<Route path="/Profile" component={Profile}>
<Route path="/About" component={About}>
<Route path="/" exact component={Home}>
<Route component={NotFound}> // NotFound 컴포넌트는 경로를 지정안해도됨
</Switch>
</BrowserRotuer>
)
navigation link :
src => NavLinks.jsx 생성
NavLinks.jsx =>
import {NavLinks} from 'react-router-dom'
const activeStyle = {color:'green'};
export default function Links() {
return (
<ul>
<li>
<NavLinks to="/" exact activeStyle={activeStyle}>Home</NavLinks>
</li>
<li>
<NavLinks to="/profile" exact activeStyle={activeStyle}>Profile</NavLinks>
</li>
<li>
<NavLinks to="/profile/1" activeStyle={activeStyle}>Profile/1</NavLinks>
</li>
<li> // /about 경로는 ?name=mark경로와 겹치기 때문에 exact 만으로 독립성을 유지할 수 없다. isActive 함수 사용(true일 때만 적용)
<NavLinks
to="/about"
activeStyle={activeStyle}
isActive={(match, location) => {
console.log(match, location);
return match !== null && location.search === "";
}}
>About</NavLinks>
</li>
<li>
<NavLinks
to="/about?name=mark"
activeStyle={activeStyle}
isActive={(match, location) => {
console.log(match, location);
return match !== null && location.search === "?name=mark";
}}
>About?name=mark</NavLinks>
</li>
</ul>
)
}
App.jsx =>
return (
<BrowserRotuer>
<Links /> // -----------//
<Switch>
<Route path="/login" component={Login}>
<Route path="/Profile/:id" component={Profile}>
<Route path="/Profile" component={Profile}>
<Route path="/About" component={About}>
<Route path="/" exact component={Home}>
<Route component={NotFound}> // NotFound 컴포넌트는 경로를 지정안해도됨
</Switch>
</BrowserRotuer>
)
page => Login.jsx 생성
export default function Login(props) {
console.log(props)
function login() {
setTimeout( () => {
//페이지를 이동
props.history.push('/');
}, 1000)
}
return (
<div>
<h2> Login 페이지 입니다. </h2>
<button onClick= {login}로그인하기</button>
</div>
)
}
그러나 구조가 복잡해질수록 하위 컴포넌트에서 props를 받기 힘들어질 수 있다.
방법 1 hoc(higher order component) 사용
components => LoginButton.jsx 생성=>
import {withRouter} from 'react-router-dom'
// withRouter = hoc
// 작성한 컴포넌트를 withRouter의 인자로 넣고 실행된 결과물을 컴포넌트로 사용한다
export default withRouter( function LoginButton(props) {
console.log(props);
function login() {
setTimeout( () => {
props.history.push('/');
},1000)
}
return <button onClick={login}> 로그인하기 </button>
})
page => Login.jsx
import LoginButton from '../components/LoginButton'
export default function Login() {
return (
<div>
<h2>Login 페이지 입니다.</h2>
<LoginButton />
// props를 받지 않았으므로 로그인버튼을 눌렀을 때 이동할 경로를 설정하지 못함. Login 함수의 매개변수로 props를 상속받아 쓸 수도 있지만 구조가 심층화될수록 복잡해짐. ==> hoc사용
</div>
)
}
방법 2 hook사용
조건이 충족되면 다른 경로로 redirect 시키는 컴포넌트
const isLogin = true;
<Route
path='/login'
render={ () => (isLogin ? <Redirect to='/' /> : <Login />) }
// render = 함수사용
// isLogin이 true이면 Redirect 경로로 이동, 아니면 Login 경로로 이동