์๋: ์ต๊ทผ ํ๋ก ํธ์๋ ํจ๋ฌ๋ค์์ธ Single Page Application์ ๋ํด ์๊ณ ์๋์ง ํ์ธํ๋ ์ง๋ฌธ
ํ: ์ฅ๋จ์ ์ ๋ช ํํ๊ฒ ์ ์ํ๋ฉด ์ข์ต๋๋ค.
๋์ ๋ต์
SPA๋ Single Page Application, ์ฆ 'ํ๋์ ํ์ด์ง๋ก ๊ตฌ์ฑ๋ ์น ์ ํ๋ฆฌ์ผ์ด์ '์ ์๋ฏธํฉ๋๋ค.
์ ํต์ ์ธ ์น์ฌ์ดํธ๋ ์ฌ์ฉ์๊ฐ ํ์ด์ง๋ฅผ ์ด๋ํ ๋๋ง๋ค ์๋ฒ์์ ์๋ก์ด HTML ๋ฌธ์๋ฅผ ๋ฐ์์ ์ ์ฒด๋ฅผ ๋ค์ ๋ ๋๋งํ์ง๋ง,
SPA๋ ์ต์ด ํ ๋ฒ๋ง ์ ์ฒด ํ์ด์ง๋ฅผ ๋ถ๋ฌ์ค๊ณ , ์ดํ์๋ ํ์ํ ๋ฐ์ดํฐ๋ง ๋น๋๊ธฐ์ ์ผ๋ก ๊ฐ์ ธ์์ ํ๋ฉด ์ผ๋ถ๋ง ๊ฐฑ์ ํฉ๋๋ค.์ด๋ฐ ๋ฐฉ์ ๋๋ถ์ ํ์ด์ง ์ ํ ์๋๊ฐ ํจ์ฌ ๋น ๋ฅด๊ณ , ์ฑ์ฒ๋ผ ์์ฐ์ค๋ฌ์ด ์ฌ์ฉ์ ๊ฒฝํ(UX)์ ์ ๊ณตํ ์ ์์ต๋๋ค.
๋ํ ํด๋ผ์ด์ธํธ ์ธก ๋ผ์ฐํ ์ ํตํด URL์ด ๋ฐ๋์ด๋ ์ค์ ๋ก๋ ๊ฐ์ ํ์ด์ง ๋ด์์ ์ปดํฌ๋ํธ๋ง ๊ต์ฒด๋ฉ๋๋ค.๋ค๋ง ์ด๊ธฐ ๋ก๋ฉ ์๋๊ฐ ๋ค์ ๋๋ฆฌ๊ฑฐ๋, ๊ฒ์ ์์ง ์ต์ ํ์ ๋ถ๋ฆฌํ ์ ์๋ค๋ ๋จ์ ์ด ์์ต๋๋ค.
์ด๋ฐ ๋ฌธ์ ๋ ์ฝ๋ ์คํ๋ฆฌํ ์ด๋ ์๋ฒ์ฌ์ด๋ ๋ ๋๋ง(SSR)์ผ๋ก ๋ณด์ํ ์ ์์ต๋๋ค.
์ฃผ์ด์ง ๋ต์ (๋ชจ๋ฒ ๋ต์)
SPA๋ Single Page Application์ ์ฝ์๋ก์ ๋ฆฌ์กํธ๋ ๋ทฐ์ ๊ธฐ๋ฐ์ด ๋๋ ๊ธฐ์ ์ ๋๋ค.
SPA๋ ์ฌ๋ฌ ์ฅ๋จ์ ์ด ์์ต๋๋ค.์ฐ์ ์ฅ์ ๋ถํฐ ๋ง์๋๋ฆฌ๊ฒ ์ต๋๋ค.
๊ธฐ์กด์ MPA๋ฅผ ์ฌ์ฉํ๋ฉด ํ์ด์ง๋ฅผ ์ด๋ํ ๋๋ง๋ค ์๋ก๊ณ ์นจ์ด ๊ณ์ ์ผ์ด๋ฌ๋๋ฐ, SPA๋ฅผ ์ฌ์ฉํ๋ฉด ์ด๋ฐ ์ผ์ด ์์ต๋๋ค.
๋ผ์ฐํฐ๋ก ํ์ด์ง๋ฅผ ์ด๋ํ๊ธฐ๋ ํ์ง๋ง ์ฌ์ค ๋ด๋ถ์ ์ผ๋ก๋ MPA์ ๊ฐ๋ ์ฒ๋ผ ํ์ด์ง๋ฅผ ์ด๋ํ๋ ๊ฒ ์๋๊ธฐ ๋๋ฌธ์ ๋๋ค.
๊ทธ์ URL์ ๋ฐ๊ฟ์ฃผ๊ณ , ๊ทธ URL์ ๋ฐ๋ผ ํ๋ฉด์ ์ปดํฌ๋ํธ๋ฅผ ๋ฐ๊ฟ์น๊ธฐ ํด์ฃผ๋ ๊ฒ์ผ๋ก ์๋ก๊ณ ์นจ ์์ด ์๋ก์ด ํ๋ฉด์ ๋ณผ ์ ์๊ฒ ํด์ฃผ๋ ๊ฒ์ด ํฐ ์ฅ์ ์ ๋๋ค.
์ด์ธ์ ํด๋ผ์ด์ธํธ ์ฌ์ด๋ ๋ ๋๋ง์ ์ฌ์ฉํ์ฌ ์๋ฒ์ ๋ถ๋ด์ ์ค์ด๊ณ ๋น ๋ฅธ ํ๋ฉด ์ ํ ๋ฑ ๋ก๋ฉ ์๋๋ฅผ ๊ฐ์ ํ ์ ์์ต๋๋ค.๋ค๋ง ๋ํ์ ์ธ ๋จ์ ์ผ๋ก๋ SEO์ ์ทจ์ฝํ๋ค๋ ์ ๊ณผ ์ด๊ธฐ ๋ก๋ฉ์ด ๋๋ฆฌ๋ค๋ ์ ์ด ์์ต๋๋ค.
๊ทธ๋์ ์์ฆ์ SPA ๊ธฐ๋ฐ ์ฑ์์ SSR๊ณผ CSR์ ๋ชจ๋ ํผํฉํ์ฌ ์ฅ์ ๋ง ๊ฐ์ ธ์ ์ฌ์ฉํ ์ ์๋ Next.js ํ๋ ์์ํฌ๊ฐ ์ธ๊ธฐ ์๋ ํธ์ ๋๋ค.
| ๋น๊ต ํญ๋ชฉ | MPA(Multi Page Application) | SPA(Single Page Application) |
|---|---|---|
| ํ์ด์ง ์ด๋ ๋ฐฉ์ | ์๋ฒ์์ ์๋ก์ด HTML์ ๋ฐ์์ด | ๊ธฐ์กด HTML์ ์ ์งํ๋ฉฐ JS๋ก ๋ณ๊ฒฝ |
| ์ด๊ธฐ ๋ก๋ฉ ์๋ | ๋น ๋ฆ (ํ์ํ HTML๋ง ๋ก๋) | ์๋์ ์ผ๋ก ๋๋ฆผ (์ ์ฒด ์ ํ๋ฆฌ์ผ์ด์ ์ ํ ๋ฒ์ ๋ก๋) |
| ์ฑ๋ฅ ์ต์ ํ | ์๋ฒ์์ ์ฒ๋ฆฌ, ํด๋ผ์ด์ธํธ ๋ถ๋ด ์ ์ | ํด๋ผ์ด์ธํธ์์ ๋๋ถ๋ถ ์ฒ๋ฆฌ |
| SEO ์ต์ ํ | ๊ฒ์ ์์ง ํฌ๋กค๋ง ์ฉ์ด | ๊ธฐ๋ณธ์ ์ผ๋ก ๋ถ๋ฆฌํ์ง๋ง SSR(์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง)์ผ๋ก ํด๊ฒฐ ๊ฐ๋ฅ |
| ์ฌ์ฉ์ ๊ฒฝํ | ํ์ด์ง ์ ํ ์ ๊น๋นก์ ๋ฐ์ | ๋ถ๋๋ฌ์ด ํ๋ฉด ์ ํ |
React๋ SPA๋ฅผ ๊ฐ๋ฐํ๋ ๋ฐ ์ต์ ํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ฉฐ, ๋ค์๊ณผ ๊ฐ์ ๋ฐฉ์์ผ๋ก ๋์ํ๋ค.
index.html๊ณผ ํจ๊ป JavaScript ๋ฒ๋ค์ด ๋ก๋๋๋ค.index.html์ ๊ธฐ๋ณธ ๊ตฌ์กฐ๋ ์๋์ ๊ฐ๋ค.<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>My React SPA</title>
</head>
<body>
<div id="root"></div> <!-- React ์ ํ๋ฆฌ์ผ์ด์
์ด ์ฌ๊ธฐ์ ๋ ๋๋ง๋จ -->
<script src="/static/js/main.js"></script>
</body>
</html> React๋ #root ๋ด๋ถ์ ์ ์ฒด ์ ํ๋ฆฌ์ผ์ด์
์ ๋ ๋๋งํ๋ฉฐ, ์ดํ์ ๋ชจ๋ ํ๋ฉด ๋ณ๊ฒฝ์ JavaScript๋ฅผ ํตํด ๋์ ์ผ๋ก ์ด๋ฃจ์ด์ง๋ค.ํด๋ผ์ด์ธํธ ์ฌ์ด๋ ๋ผ์ฐํ
React์์๋ react-router-dom์ ์ฌ์ฉํ์ฌ ๋ผ์ฐํ
์ ์ฒ๋ฆฌํ๋ค.
์ด ๋ฐฉ์์ ์๋ฒ์์ ์๋ก์ด HTML์ ์์ฒญํ๋ ๊ฒ์ด ์๋๋ผ, window.history.pushState()๋ฅผ ์ด์ฉํ์ฌ URL์ ๋ณ๊ฒฝํ๊ณ ํด๋น URL์ ๋ง๋ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ๋ ๋ฐฉ์์ด๋ค.
import { BrowserRouter as Router, Routes, Route, Link } from "react-router-dom";
function Home() {
return <h1>ํ ํ์ด์ง</h1>;
}
function About() {
return <h1>์๊ฐ ํ์ด์ง</h1>;
}
function App() {
return (
<Router>
<nav>
<Link to="/">ํ</Link>
<Link to="/about">์๊ฐ</Link>
</nav>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Router>
);
}
export default App;
์ด์ ์ฌ์ฉ์๊ฐ /about์ผ๋ก ์ด๋ํ๋ฉด ์ ์ฒด ํ์ด์ง๋ฅผ ์๋ก๊ณ ์นจํ์ง ์๊ณ About ์ปดํฌ๋ํธ๋ง ๋ณ๊ฒฝ๋๋ค.
์ํ ๊ด๋ฆฌ์ ๋ฐ์ดํฐ ๊ด๋ฆฌ
React์ SPA๋ ํด๋ผ์ด์ธํธ์์ ์ํ๋ฅผ ์ ์งํ๋ฉฐ, ์ํ ๊ด๋ฆฌ๋ฅผ ์ํด ์ฌ๋ฌ ๋ฐฉ๋ฒ์ ์ฌ์ฉํ ์ ์๋ค.
1) useState, useContext๋ฅผ ์ฌ์ฉํ ๊ธฐ๋ณธ์ ์ธ ์ํ ๊ด๋ฆฌ
2) Redux, Zustand ๋ฑ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ์ฉํ ์ ์ญ ์ํ ๊ด๋ฆฌ
3) React Query, SWR(Stale-While-Revalidate)์ ์ฌ์ฉํ ๋น๋๊ธฐ ๋ฐ์ดํฐ ๊ด๋ฆฌ
์์
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>์นด์ดํธ: {count}</p>
<button onClick={() => setCount(count + 1)}>์ฆ๊ฐ</button>
</div>
);
}
export default Counter;
์ด์ฒ๋ผ React๋ ์ํ๋ฅผ ๊ด๋ฆฌํ๋ฉด์ ์ปดํฌ๋ํธ ๊ธฐ๋ฐ์ผ๋ก UI๋ฅผ ๋์ ์ผ๋ก ๊ฐฑ์ ํ๋ค.
๋จ์
์ด๊ธฐ ๋ก๋ฉ ์๋๊ฐ ๋๋ฆผ
SPA๋ ์ฒ์์ ๋ชจ๋ JavaScript ๋ฒ๋ค์ ๋ค์ด๋ก๋ํด์ผ ํ๋ฏ๋ก, ์ฒซ ๋ก๋ฉ์ด MPA๋ณด๋ค ๋๋ฆด ์ ์๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์ฝ๋ ์คํ๋ฆฌํ ๊ณผ ๊ฐ์ ๊ธฐ๋ฒ์ ์ฌ์ฉํ ์ ์๋ค.
import { lazy, Suspense } from "react";
const About = lazy(() => import("./About"));
function App() {
return (
<Suspense fallback={<div>๋ก๋ฉ ์ค...</div>}>
<About />
</Suspense>
);
}
SEO(๊ฒ์ ์์ง ์ต์ ํ) ๋ฌธ์
๋ธ๋ผ์ฐ์ ํ์คํ ๋ฆฌ ๊ด๋ฆฌ ์ด๋ ค์
react-router-dom์ BrowserRouter ๋๋ HashRouter๋ฅผ ํ์ฉํ์ฌ ํด๊ฒฐ ๊ฐ๋ฅํ๋ค.๋ณด์ ๋ฌธ์
React.lazy()๋ฅผ ์ฌ์ฉํ์ฌ ํ์ํ ๋ชจ๋๋ง ๋์ ์ผ๋ก ๋ก๋