새 페이지를 보여줄 때, 페이지 이동 대신 자바스크립트를 이용하여 기존 DOM을 새로운 DOM으로 대체한다.
MPA와 비교했을 때 SPA의 장단점
SSR vs CSR과는 다른 개념이다. 장단점을 혼동하지 않도록 주의해야 한다.
리액트 프로젝트에서 SPA를 쉽게 도입할 수 있도록 도와주는 라이브러리이다.
npm i react-router-dom
<BrowserRouter>
: react-router를 사용할 앱에서 단 한 번만 사용한다.<Routes>
: <Route>
를 아래와 같이 동작하도록 만든다.<Route>
: props.path
와 브라우저의 url이 일치하면 props.element
를 return한다. 가장 먼저 일치하는 Route
만 적용한다.<Link>
: props.to
에 적힌 url로 http get request를 보내지 않고 브라우저에 표시되는 url만 변경한다. 실제 페이지 이동은 하지 않는다.element={<Main />}
=== 리액트 엘리먼트
👉 컴포넌트를 JSX 형태로 렌더시킨 것이다.
<a href="">
대신 <Link>
를 사용
anchor 태그는 브라우저 url을 '이동'
일반적으로 index.js
에서 사용한다.
import { BrowserRouter, Routes, Route, Link, useParams } from "react-router-dom";
import Form from './Form';
...
const Hello = () => {
const params = useParams();
console.log(params);
return (
<Link to='/'>to home</Link>
)
}
const Main = () => (
<Link to='/hello/5'>to hello with id 5</Link>
)
root.render(
<div>
<BrowserRouter>
<Routes>
<Route path="/" element={<Main />} />
<Route path="/hello/:id" element={<Hello />} />
<Route path="/form" element={<Form />} />
</Routes>
</BrowserRouter>
</div>
);
reportWebVitals();
바뀌지 않는 header와 footer 같은 경우 다음처럼 사용할 수 있다.
useParams()
// index.js
import { BrowserRouter, Routes, Route, Link, useParams, useNavigate } from "react-router-dom";
import Form from './Form';
const Mypage = () => {
const params = useParams();
let navigate = useNavigate();
console.log(params);
return (
<div>
안녕하세요 {params.id}님!
<br/>
<button onClick={() => {
navigate('/');
}}>로그아웃</button>
</div>
)
}
root.render(
// <React.StrictMode>
// <App />
<div>
<BrowserRouter>
<Routes>
<Route path="/" element={<Form />} />
<Route path="/mypage/:id" element={<Mypage />} />
</Routes>
</BrowserRouter>
</div>
// {/* <TestComponent /> */}
// </React.StrictMode>
);
// Form.js
import React, {useState, useRef} from 'react'
import { Link } from "react-router-dom";
export default function Form() {
const [id, setId] = useState("");
const [password, setPassword] = useState("");
const idRef = useRef(null);
const passwordRef = useRef(null);
const idMsg = "유효하지 않은 id입니다.";
const passwordMsg = "유효하지 않은 password입니다.";
const idCheck = (id.length >= 6 && id.length <= 20);
const passwordCheck = (password.length >= 12 && password.length <= 20);
// let navigate = useNavigate();
const handleChangeInput = (e) => {
if(e.target.name === "id") {
setId(e.target.value);
}
if(e.target.name === "password") {
setPassword(e.target.value);
}
}
const handleClick = (e) => {
if ( !idCheck ) {
alert(idMsg);
setId('');
idRef.current.focus();
e.preventDefault();
}
else if ( !passwordCheck ) {
alert(passwordMsg);
setPassword('');
passwordRef.current.focus();
e.preventDefault();
}
};
return (
<div>
<div>
<input type="text" name='id' ref={idRef} value={id} onChange={handleChangeInput} placeholder='6글자 이상 20글자 이하' />
{id ?
idCheck ? null : idMsg
: null}
</div>
<div>
<input type="text" name='password' ref={passwordRef} value={password} onChange={handleChangeInput} placeholder='12글자 이상 20글자 이하' />
{password ?
passwordCheck ? null : passwordMsg
: null}
</div>
<Link to={`/mypage/${id}`} >
<button onClick={handleClick} disabled={
(id || password) ? false : true
}>로그인</button>
</Link>
</div>
);
}