๐ Chapter 6: ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง
ํด๋ผ์ด์ธํธ ์ฌ์ด๋ ๋ ๋๋ง ํ๊ณโ
๊ฒ์ ์์ง ์ต์ ํ(SEO)โ
ํด๋ผ์ด์ธํธ ์ฌ์ด๋ ๋ ๋๋ง์ ์ฃผ์ ํ๊ณ ์ค ํ๋๋ ์ผ๋ถ ๊ฒ์ ์์ง ํฌ๋กค๋ฌ๊ฐ ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ์ง ์์ผ๋ฉฐ, ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ์คํํ๋ ํฌ๋กค๋ฌ๋ ์์๋๋ก ์คํ๋์ง ์์ ์ ์๊ธฐ์ ์ฝํ ์ธ ๋ฅผ ์ฌ๋ฐ๋ฅด๊ฒ ์์ธํ์ง ๋ชปํ ์ ์๋ค๋ ๊ฒ์ ๋๋ค.
์ฑ๋ฅโ
ํด๋ผ์ด์ธํธ์์ ๋ ๋๋ง๋๋ ์ ํ๋ฆฌ์ผ์ด์
์ ๋๋ฆฐ ๋คํธ์ํฌ๋ ๋ฎ์ ์ฑ๋ฅ ๊ธฐ๊ธฐ์์ ์ฑ๋ฅ ๋ฌธ์ ๋ฅผ ๊ฒช์ ์ ์์ต๋๋ค. ์ฝํ
์ธ ๋ฅผ ๋ ๋๋งํ๊ธฐ ์ ์ ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ๋ค์ด๋ก๋ํ๊ณ , ๊ตฌ๋ฌธ ๋ถ์๊ณผ ์คํ๊น์ง ํด์ผ ํ๊ธฐ ๋๋ฌธ์ ์ฝํ
์ธ ๋ ๋๋ง์ด ์๋นํ ์ง์ฐ๋ ์ ์์ต๋๋ค.
์ ํ๋ฆฌ์ผ์ด์
์ ์ฝ์ด ๋ค์ด๋ ์๊ฐ์ด ๋งค์ฐ ์ค๋ ๊ฑธ๋ฆฌ๋ฉด ์ฌ์ฉ์๊ฐ ํ์ด์ง๋ฅผ ๋ ๋ ์ ์์ผ๋ฉฐ, ์ด๋ ๊ฒ์ ์์ง์ ํ์ด์ง ์์์ ๋ถ์ ์ ์ํฅ์ ๋ฏธ์น ์ ์์ต๋๋ค.
ํด๋ผ์ด์ธํธ ์ ์ฉ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ ๋ค๋ฅธ ๋ฌธ์ ๋ ๋คํธ ์ํฌ ํญํฌ ํ์์ ๋๋ค. ์ด๋ ์น์ฌ์ดํธ๋ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ํ์ํ๊ธฐ ์ํด ๋ธ๋ผ์ฐ์ ๊ฐ ๋ค์ด๋ก๋, ํ์ฑ, ์คํํด์ผ ํ๋ ์๋ฐ์คํฌ๋ฆฝํธ ๋๋ฌธ์ ์ด๊ธฐ ํ์ด์ง ๋ก๋๊ฐ ์ฐจ๋จ๋๋ ํ์์ ๋งํฉ๋๋ค.
์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง์ ์ฌ์ฉํ๋ฉด ์ฌ์ฉ์๊ฐ ์ ์ฉํ ์ฝํ ์ธ ๋ฅผ ์ฆ์ ๋ณผ ์ ์๋๋ก ๊ฐ์ ํ ์ ์์ต๋๋ค.
๋ณด์โ
ํด๋ผ์ด์ธํธ ์ฌ์ด๋ ๋ ๋๋ง์ ํนํ ๋ฏผ๊ฐํ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ ๋ ๋ณด์์ด ๋ฌธ์ ์๋ ์ ์์ต๋๋ค. ์ด๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ชจ๋ ์ฝ๋๊ฐ ํด๋ผ์ด์ธํธ์ ๋ธ๋ผ์ฐ์ ๋ก ๋ค์ด๋ก๋๋์ด ํฌ๋ก์ค ์ฌ์ดํธ ์์ฒญ ์์กฐ(CSRF) ๊ฐ์ ๊ณต๊ฒฉ์ ์ทจ์ฝํ๊ธฐ ๋๋ฌธ์ ๋๋ค.
CSRF๋ฅผ ์์ฃผ ๊น์ด ์์ง ๋ชปํด๋ ์ด๋ฅผ ๋ฐฉ์ดํ ์ ์๋๋ฐ, ์ผ๋ฐ์ ์ธ ๋ฐฉ๋ฒ์ ์น์ฌ์ดํธ๋ ์น ์ฑ์ ์ฌ์ฉ์์๊ฒ ์ ๊ณตํ๋ ์๋ฒ๋ฅผ ์ ์ดํ๋ ๊ฒ์ ๋๋ค. ์ด ์๋ฒ๋ฅผ ์ ์ดํ ์ ์๋ค๋ฉด, ์ ๋ขฐํ ์ ์๋ ์ถ์ฒ์ธ ์๋ฒ์์ ํด๋ผ์ด์ธํธ๋ก ์ ์ ํ CSRF ๋ฐฉ์ง ํ ํฐ์ ์ ์กํ ์ ์์ต๋๋ค.
์๋ฒ ๋ ๋๋ง์ ๋ถ์โ
์๋ฒ ๋ ๋๋ง์ ์ฅ์ โ
- ์ต์ด ์๋ฏธ ์๋ ํ์ธํธ(first meaningful paint)๊ฐ ์์ฑ๋๋ ์๊ฐ์ด ๋ ๋นจ๋ผ์ง๋๋ค.
- ์๋ฒ๊ฐ ์ด๊ธฐ HTML ๋งํฌ์ ์ ๋ ๋๋งํด ํด๋ผ์ด์ธํธ๋ก ์ ์กํ๋ฉด ๋ฐ๋ก ์ฝํ ์ธ ๋ฅผ ๋ณผ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค. ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋ ๋๋ง๋๊ธฐ ์ ์ ํด๋ผ์ด์ธํธ๊ฐ ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ๋ค์ด๋ก๋, ํ์ฑ, ์คํํ ๋๊น์ง ๊ธฐ๋ค๋ ค์ผ ํ๋ ํด๋ผ์ด์ธํธ ์ฌ์ด๋ ๋ ๋๋ง๊ณผ ๋์กฐ์ ์ ๋๋ค.
- ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ๊ทผ์ฑ์ ๊ฐ์ ํฉ๋๋ค
- ์น ์ ํ๋ฆฌ์ผ์ด์ ์ SEO๋ฅผ ๊ฐ์ ํ ์ ์์ต๋๋ค
- ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ณด์๋ ํฅ์์ํฌ ์ ์์ต๋๋ค
๊ทธ๋ฌ๋ ์๋ฒ์์ ๋ ๋๋ง๋ HTML์ ์ ์ ์ด๋ฉฐ ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ์ฝ์ด ๋ค์ด์ง ์์ ์ํ์ด๊ธฐ์ ์ํธ ์์ฉ ์ง์์ด ๋ถ์กฑํฉ๋๋ค. ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ ๊ธฐํ ๋์ ๊ธฐ๋ฅ๋ ํฌํจ๋์ง ์์์ต๋๋ค. ์ฌ์ฉ์ ์ํธ ์์ฉ์ ๋น๋กฏํ ๋์ ๊ธฐ๋ฅ์ ํ์ฑํํ๋ ค๋ฉด, ํ์ํ ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ์ ์ HTML์ ์ด์ดํ๊ฒ ๊ณต๊ธํด ์ค์ผ ํฉ๋๋ค. ํ์ดํธ๋ ์ด์ ์ ํด์ผ ํ๋ค๋ ์๋ฏธ์ ๋๋ค.
ํ์ด๋๋ ์ด์ โ
ํ์ด๋๋ ์ด์
์ ์๋ฒ์์ ์์ฑ๋์ด ํด๋ผ์ด์ธํธ๋ฅผ ์ ์ก๋๋ ์ ์ HTML์ ์ด๋ฒคํธ ๋ฆฌ์ค๋์ ์ฌ๋ฌ ์๋ฐ์คํฌ๋ฆฝํธ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๋ ํ๋ก์ธ์ค๋ฅผ ์๋ฏธํ๋ ์ฉ์ด์
๋๋ค. ํ์ด๋๋ ์ด์
์ ๋ชฉ์ ์ ๋ธ๋ผ์ฐ์ ๊ฐ ์๋ฒ ๋ ๋๋ง ์ ํ๋ฆฌ์ผ์ด์
์ ์ฝ์ด ๋ค์ธ ํ ์ฌ๊ธฐ์ ์ํธ ์์ฉ์ ์ถ๊ฐํด์ ์ฌ์ฉ์์๊ฒ ๋น ๋ฅด๊ณ ์ํํ ๊ฒฝํ์ ์ ๊ณตํ๋ ๊ฒ์
๋๋ค.
๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์
์์ ํ์ด๋๋ ์ด์
์ ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ์์ ๋ ๋๋ง๋ ๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ค์ด๋ก๋ํ ํ์ ๋ฐ์ํฉ๋๋ค. ์ดํ์๋ ๋ค์ ๋จ๊ณ๋ก ์งํ๋ฉ๋๋ค.
ํด๋ผ์ด์ธํธ ๋ฒ๋ค ๋ก๋ฉ
- ๋ธ๋ผ์ฐ์ ๋ ์ ์ HTML์ ๋ ๋๋งํ๋ ๋์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฝ๋๊ฐ ํฌํจ๋ ์๋ฐ์คํฌ๋ฆฝํธ ๋ฒ๋ค์ ๋ค์ด๋ก๋ํ๊ณ ํ์ฑํฉ๋๋ค. ์ด ๋ฒ๋ค์๋ ๋ฆฌ์กํธ ์ปดํฌ๋ํธ์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ธฐ๋ฅ์ ํ์ํ ์ฝ๋๊ฐ ํฌํจ๋์ด ์์ต๋๋ค.
์ด๋ฒคํธ ๋ฆฌ์ค๋ ์ถ๊ฐ
- ์๋ฐ์คํฌ๋ฆฝํธ ๋ฒ๋ค์ด ๋ก๋๋๋ฉด ๋ฆฌ์กํธ๋ ์ด๋ฒคํธ ๋ฆฌ์ค๋ ๋ฐ ๊ธฐํ ๋์ ๊ธฐ๋ฅ์ DOM ์์์ ์ถ๊ฐํด ์ ์ HTML์ ํ์ด๋๋ ์ด์
ํฉ๋๋ค. ์ด ๋์์ ์ผ๋ฐ์ ์ผ๋ก react-dom ํจํค์ง์
hydrateRoot
ํจ์๋ฅผ ์ฌ์ฉํด ์ํ๋๋ฉฐ, ์ด ํจ์๋ ๋ฃจํธ ๋ฆฌ์กํธ ์ปดํฌ๋ํธ์ DOM ์ปจํ ์ด๋๋ฅผ ์ธ์๋ก ๋ฐ์ต๋๋ค. ํ์ด๋๋ ์ด์ ์ด ์๋ฃ๋๋ฉด ์ ์ HTML์ด ์์ ํ ์ธํฐ๋ํฐ๋ธ ๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ณํ๋ฉ๋๋ค.
ํ์ด๋๋ ์ด์ ํ๋ก์ธ์ค๊ฐ ์๋ฃ๋๋ฉด ์ ํ๋ฆฌ์ผ์ด์ ์ ์์ ํ ์ธํฐ๋ํฐ๋ธํ๊ฒ ๋ณํด์ ํ์์ ๋ฐ๋ผ์ ์ฌ์ฉ์ ์ ๋ ฅ์ ์๋ตํ๊ณ , ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ณ , DOM์ ์ ๋ฐ์ดํธํฉ๋๋ค.
ํ์ด๋๋ ์ด์ ์ ๋ํ ๋นํโ
ํ์ด๋๋ ์ด์ ์ ์๋ฒ์์ ๋ ๋๋ง๋ HTML์ ์ธํฐ๋ํฐ๋ธํ๊ฒ ๋ง๋๋ ์ข์ ๋ฐฉ๋ฒ์ด์ง๋ง, ์ผ๋ถ์์๋ ํ์ด๋๋ ์ด์ ์ด ํ์ ์ด์์ผ๋ก ๋๋ฆฌ๋ค๊ณ ๋นํํ๋ฉฐ ์ฌ๊ฐ ๊ฐ๋ฅ์ฑ์ ๋ ๋์ ๋์์ผ๋ก ๊ผฝ๊ธฐ๋ ํฉ๋๋ค.
ํ์ด๋๋ ์ด์ ์ ์ฌ์ฉํ๋ฉด ์๋ฒ์์ ๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋จผ์ ๋ ๋๋งํ ๋ค์ ๋ ๋๋ง๋ ์ถ๋ ฅ์ ํด๋ผ์ด์ธํธ์ ์ ๋ฌํฉ๋๋ค. ํ์ง๋ง ์ด ์์ ๊น์ง๋ ์ธํฐ๋ํฐ๋ธํ ๊ธฐ๋ฅ์ด ์์ต๋๋ค. ์ดํ ๋ธ๋ผ์ฐ์ ๋ ํด๋ผ์ด์ธํธ ๋ฒ๋ค์ ๋ค์ด๋ก๋ํ๊ณ ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ์ถ๊ฐํ ํ ํด๋ผ์ด์ธํธ๋ฅผ ํจ๊ณผ์ ์ผ๋ก '๋ฆฌ๋ ๋๋ง'ํด์ผ ํฉ๋๋ค. ๋ง์ ์์ ์ ํ์๋ก ํ๋ฉฐ ๋๋ก๋ ์ฝํ ์ธ ๊ฐ ์ฌ์ฉ์์๊ฒ ํ์๋๋ ์์ ๊ณผ ์ฌ์ฉ์๊ฐ ์ค์ ๋ก ์ฌ์ดํธ๋ฅผ ์ฌ์ฉํ ์ ์๋ ์์ ์ฌ์ด์ ์ง์ฐ์ด ๋ฐ์ํ ์ ์์ต๋๋ค.
์ฌ๊ฐ ๊ฐ๋ฅ์ฑ์ ํ์ฉํ๋ฉด ์ ์ฒด ์ ํ๋ฆฌ์ผ์ด์ ์ด ์๋ฒ์์ ๋ ๋๋ง๋์ด ๋ธ๋ผ์ฐ์ ๋ก ์คํธ๋ฆฌ๋ฐ๋ฉ๋๋ค. ์ด๊ธฐ ๋งํฌ์ ๊ณผ ํจ๊ป ๋ชจ๋ ์ธํฐ๋ํฐ๋ธ ๋์์ด ์ง๋ ฌํ๋์ด ํด๋ผ์ด์ธํธ๋ก ์ ์ก๋ฉ๋๋ค. ์ด ์์ ์์ ํด๋ผ์ด์ธํธ๋ ์ด๋ฏธ ์ธํฐ๋ํฐ๋ธํด์ง๋ ๋ฐฉ๋ฒ์ ๋ํ ๋ชจ๋ ์ ๋ณด๋ฅผ ๊ฐ์ง๋ฏ๋ก ์๋ฒ๊ฐ ์ค๋จํ ๋ถ๋ถ๋ถํฐ ๋ค์ ์์ํ ์ ์์ต๋๋ค. ํ์ด๋๋ ์ด์ (์ฆ, ์ด๋ฒคํธ ๋ฆฌ์ค๋๋ฅผ ์ฐ๊ฒฐํ๊ณ ํด๋ผ์ด์ธํธ ์ฌ์ด๋์์ ํ์ด์ง๋ฅผ ๋ ๋๋ง)ํ ํ์ ์์ด ์๋ฒ๊ฐ ์ ๊ณตํ ๋ด์ฉ์ ์ญ์ง๋ ฌํํด ๊ทธ์ ๋ฐ๋ผ ๋ฐ์ํ ์ ์์ต๋๋ค. ํ์ด๋๋ ์ด์ ๋จ๊ณ๋ฅผ ๊ฑด๋๋ฐ๋ฉด ์ธํฐ๋ํฐ๋ธ ์๊ฐ(TTI)์ด ์งง์์ง๊ณ ์์ฉ์ ๊ฒฝํ์ด ํฅ์๋ ์ ์์ต๋๋ค.
์๋ฒ ๋ ๋๋ง ์์ฑโ
ํด๋ผ์ด์ธํธ ์ ์ฉ ๋ฆฌ์กํธ ์ฑ์ ์๋ฒ ๋ ๋๋ง์ ์๋์ผ๋ก ์ถ๊ฐํ๊ธฐโ
// server.js
// ํ์ํ ๋ชจ๋ ๊ฐ์ ธ์ค๊ธฐ
const express = require("express");
const path = require("path");
const react = require("react");
// ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง์ ์ํด ReactDOMServer ๊ฐ์ ธ์ค๊ธฐ
const ReactDOMServer = require("react-dom/server");
const App = require("./src/App");
const app = express();
// 'build' ๋๋ ํฐ๋ฆฌ์ ์๋ ์ ์ ํ์ผ ์๋นํ๊ธฐ
app.use(express.static(path.join(__dirname, "build")));
// ๋ชจ๋ GET ์์ฒญ ์ฒ๋ฆฌ
app.get("*", (req, res) => {
// App ์ปดใ
ํฌ๋ํธ๋ฅผ ๋ ๋๋งํด HTML ๋ฌธ์์ด์ ์์ฑํฉ๋๋ค.
const html = ReactDOMServer.renderToString(<App />);
// ๋ ๋๋ง๋ App ์ปดํฌ๋ํธ๊ฐ ํฌํจ๋ HTML ์๋ต์ ์ ์กํฉ๋๋ค.
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>์์ ๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์
</title>
</head>
<body>
<!-- ๋ ๋๋ง๋ App ์ปดํฌ๋ํธ๋ฅผ ์ฌ๊ธฐ์ ์ฝ์
-->
<div id="root">${html}</div>
<!-- ๋ฉ์ธ ์๋ฐ์คํฌ๋ฆฝํธ ๋ฒ๋ค์ ์ฐ๊ฒฐ -->
<script src="/static/js/main.js"></script>
</body>
</html>
`);
});
app.listen(3000, () => {
console.log("Server listening on port 3000");
})
์ด ์์์์๋ Express๋ฅผ ์ฌ์ฉํด ./build
๋๋ ํฐ๋ฆฌ์์ ์ ์ ํ์ผ์ ์๋น์คํ๋ ์๋ฒ๋ฅผ ์์ฑํ ๋ค์, ์๋ฒ์์ ๋ฆฌ์กํธ ์ฑ์ ๋ ๋๋งํฉ๋๋ค. ๋ ReactDOMServer
๋ฅผ ์ฌ์ฉํด ๋ฆฌ์กํธ ์ฑ์ HTML ๋ฌธ์์ด๋ก ๋ ๋๋งํ ๋ค์, ํด๋ผ์ด์ธํธ๋ก ์ ์ก๋๋ ์๋ต์ ์ฝ์
ํฉ๋๋ค.
ํ์ด๋๋ ์ด์ โ
ํ์ด๋๋ ์ด์ ์ ๋ชฉ์ ์ ์๋ฒ์์ ๋ ๋๋ง๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ธ๋ผ์ฐ์ ์์ ์์ ํ ์ธํฐ๋ํฐ๋ธํ๊ฒ ๋ง๋๋ ๊ฒ์ ๋๋ค.
import React from "react";
import { hydrateRoot } from "react-dom/client";
import App from "./App";
hydrateRoot(document, <App />);