๐ 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 />);
๋ฆฌ์กํธ์ ์๋ฒ ๋ ๋๋ง APIโ
renderToString
โ
renderToString
์ ๋ฆฌ์กํธ์์ ์ ๊ณตํ๋ ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง API๋ก, ์๋ฒ์์ ๋ฆฌ์กํธ ์ปดํฌ๋ํธ๋ฅผ HTML ๋ฌธ์์ด๋ก ๋ ๋๋งํ ๋ ์ฌ์ฉํฉ๋๋ค. renderToString
์ ์ฑ๋ฅ, SEO, ์ ๊ทผ์ฑ์ ๊ฐ์ ํ๊ธฐ ์ํด ์๋ฒ์์ ๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ ๋๋งํ ๋ ์ฃผ๋ก ์ฌ์ฉํฉ๋๋ค.
renderToString
์ ํ๋ฆ์ ๊ฐ๋ก๋ง๋ ๋๊ธฐ์ API์ด๋ฏ๋ก ์คํ์ด ์ค๋จ๋๊ฑฐ๋ ์ผ์์ ์ผ๋ก ์ค์ง๋ ์ ์์ต๋๋ค. ๋์์ด ๋๋ ์ปดํฌ๋ํธ๊ฐ ๋ฃจํธ์์ ๋ช ๋จ๊ณ ๊น์ด์ ์๋ค๋ฉด, ์ฒ๋ฆฌํ๋ ์๊ฐ์ด ์ด๋ ์ ๋ ๊ฑธ๋ฆด ์ ์์ต๋๋ค.
renderToString
์ ์ ๋ฆฌํ ์ฅ์ ์ด ๋ง์ง๋ง ๋ถ๋ฆฌํ ๋จ์ ๋ ๋ช ๊ฐ์ง ์์ต๋๋ค.
- ์ฑ๋ฅ
renderToString
์ ์ฃผ์ ๋จ์ ํ๋๋ ๋๊ท๋ชจ ๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์ ์์๋ ์ฒ๋ฆฌ ์๋๊ฐ ๋๋ฆด ์ ์๋ค๋ ๊ฒ์ ๋๋ค. ๋๊ธฐ์์ด๊ธฐ ๋๋ฌธ์ ์ด๋ฒคํธ ๋ฃจํ๋ฅผ ์ฐจ๋จํ๊ณ ์๋ฒ๊ฐ ์๋ตํ์ง ์๊ฒ ๋ง๋ค ์ ์์ต๋๋ค.
- ์คํธ๋ฆฌ๋ฐ ์ง์ ๋ถ์กฑ
renderToString
์ ์คํธ๋ฆฌ๋ฐ์ ์ง์ํ์ง ์์ผ๋ฏ๋ก ํด๋ผ์ด์ธํธ๋ก ์ ์กํ๋ ค๋ฉด HTML ๋ฌธ์์ด ์ ์ฒด๋ฅผ ์์ฑํด์ผ ํฉ๋๋ค. ์ด๋ก ์ธํด ์ฒซ ๋ฒ์งธ ๋ฐ์ดํธ ์๊ฐ(TTFB)์ด ๋๋ ค์ง๊ณ ํด๋ผ์ด์ธํธ๊ฐ HTML ์์ ์ ์์ํ๊ธฐ๊น์ง ๋ ์ค๋ ์๊ฐ์ด ๊ฑธ๋ฆด ์ ์์ต๋๋ค.
renderToPipeableStream
โ
renderToPipeableStream
์ ๋ฆฌ์กํธ 18์ ๋์
๋ ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง API๋ก, ๋๊ท๋ชจ ๋ฆฌ์กํธ ์ ํ๋ฆฌ์ผ์ด์
์ Node.js ์คํธ๋ฆผ์ ๋ ๋๋งํ๋ ๋ณด๋ค ํจ์จ์ ์ด๊ณ ์ ์ฐํ ๋ฐฉ๋ฒ์
๋๋ค. ์ด API๋ ์๋ต ๊ฐ์ฒด๋ก ํ์ดํํ ์ ์๋ ์คํธ๋ฆผ์ ๋ฐํํ๋ฉฐ, HTML์ด ๋ ๋๋ง๋๋ ๋ฐฉ์์ ๋ ์ธ๋ฐํ๊ฒ ์ ์ดํ ์ ์์ด์ ๋ ์ฝ๊ฒ ๋ค๋ฅธ Node.js ์คํธ๋ฆผ๊ณผ ํตํฉํ ์ ์์ต๋๋ค.
๋ํ ๋ฆฌ์กํธ์ ๋์์ฑ ๊ธฐ๋ฅ, ํนํ ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง ์ค ๋น๋๊ธฐ ๋ฐ์ดํฐ ํ์น๋ฅผ ๋ ์ ์ฒ๋ฆฌํด์ฃผ๋ Suspense๋ฅผ ์๋ฒฝํ๊ฒ ์ง์ํฉ๋๋ค. ์คํธ๋ฆผ์ด๊ธฐ ๋๋ฌธ์ ๋คํธ์ํฌ๋ฅผ ํตํด ์คํธ๋ฆฌ๋ฐํ ์ ์์ผ๋ฉฐ, HTML ์ฒญํฌ๋ฅผ ํด๋ผ์ด์ธํธ์ ๋น๋๊ธฐ์ ์ผ๋ก ์ ์กํ ์ ์์ด์ ๋คํธ์ํฌ ์ง์ฐ ์๋ ์ ์ง์ ์ธ ๋ฐ์ดํฐ ์ ๋ฌ์ด ๊ฐ๋ฅํฉ๋๋ค.
// 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) => {
const { pipe } = ReactDOMServer.renderToPipeableStream(<App />, {
// ๋ฐ์ดํฐ๋ฅผ ํ์นํ๊ธฐ ์ ์ ์ฑ์ด ์ค๋น๋๋ ๊ฒฝ์ฐ
onShellReady: () => {
// ์๋ฒ๊ฐ HTML์ ๋ณด๋ผ ๊ฑฐ๋ผ๊ณ ํด๋ผ์ด์ธํธ์ ํต์ง
res.setHeader("Content-Type", "text/html");
pipe(res); // ๋ฆฌ์กํธ ์คํธ๋ฆผ์ ์ถ๋ ฅ ๊ฒฐ๊ณผ๋ฅผ ์๋ต ์คํธ๋ฆผ์ ํ์ดํ
}
})
});
app.listen(3000, () => {
console.log("Server listening on port 3000");
})
renderToPipeableStream
์ ๋ณํ ๊ฒฐ๊ณผ๋ HTML ๋ฌธ์์ด์ด ์๋๋ผ Node.js ์คํธ๋ฆผ์ด ๋ฉ๋๋ค. ์คํธ๋ฆผ์ ์ฌ์ฉํ๋ฉด ์ ์ฒด ๋ฐ์ดํฐ๋ฅผ ๋ฉ๋ชจ๋ฆฌ์ ํ๊บผ๋ฒ์ ์ฝ์ด ๋ค์ด๋ ๋์ ์ฒญํฌ ๋จ์๋ก ์ ์ง์ ์ผ๋ก ์ฒ๋ฆฌํฉ๋๋ค. ์ด๋ฌํ ์ ๊ทผ ๋ฐฉ์์ ๋ฉ๋ชจ๋ฆฌ์ ๋ชจ๋ ์ฝ์ด ๋ค์ผ ์ ์๊ฑฐ๋ ๋คํธ์ํฌ๋ฅผ ํตํด ํ๋ฒ์ ์ ์กํ์ง ๋ชปํ๋ ๋์ฉ๋ ๋ฌธ์์ด์ด๋ ๋ฐ์ดํฐ ์คํธ๋ฆผ์ ๋ค๋ฃฐ ๋ ํนํ ์ ์ฉํฉ๋๋ค.
๋ฆฌ์กํธ๋ ์๋ฒ์์ ๋ ๋๋งํ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฒซ ๋ฒ์งธ ๋ฐ์ดํธ ์๊ฐ(TTFB) ์งํ๋ฅผ ๊ฐ์ ํ๊ธฐ ์ํด ๋ฆฌ์กํธ ์ปดํฌ๋ํธ๋ฅผ ์ฐ๊ธฐ ๊ฐ๋ฅ ์คํธ๋ฆผ์ผ๋ก ์คํธ๋ฆฌ๋ฐํฉ๋๋ค. ์ด๋ฅผ ํตํด HTML ๋งํฌ์ ์ ์ฒด๊ฐ ์์ฑ๋ ๋๊น์ง ์๋ฒ๊ฐ ๊ธฐ๋ค๋ ธ๋ค๊ฐ ํด๋ผ์ด์ธ ํธ์ ์ ์กํ๋ ๋์ , HTML ์๋ต ์ฒญํฌ๊ฐ ์ค๋น๋๋ ์ฆ์ ์ ์ก์ ์์ํด ์ ๋ฐ์ ์ธ ์ง์ฐ ์๊ฐ์ ์ค์ ๋๋ค.
renderToReadableStream
โ
๋ธ๋ผ์ฐ์ ์คํธ๋ฆผ์ ์น ๋ธ๋ผ์ฐ์ ๋ด์ ํด๋ผ์ด์ธํธ ํ๊ฒฝ์์ ์๋ํ๋๋ก ์ค๊ณ๋์์ผ๋ฉฐ, ์ฃผ๋ก ๋คํธ์ํฌ ์์ฒญ, ๋ฏธ๋์ด ์คํธ๋ฆฌ๋ฐ, ๋ธ๋ผ์ฐ์ ์ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ์์
์์ ์คํธ๋ฆฌ๋ฐ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค. ๋ธ๋ผ์ฐ์ ์คํธ๋ฆผ์ ์น ์ ์ฒด์ API ํ์คํ๋ฅผ ๋ชฉํ๋ก ํ๋ WHATWG์์ ์ ์ํ ์คํธ๋ฆผ ํ์ค์ ๋ฐ๋ฆ
๋๋ค. Node.js ์คํธ๋ฆผ๊ณผ ๋ฌ๋ฆฌ ๋ธ๋ผ์ฐ์ ์คํธ๋ฆผ์ read()
, write()
, pipeThrough()
๋ฑ์ ๋ฉ์๋๋ฅผ ์ฌ์ฉํด ๋ฐ์ดํฐ์ ํ๋ฆ์ ์ ์ดํ๊ณ ์คํธ๋ฆฌ๋ฐ๋ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค. ๋ ๋ณด๋ค ํ์คํ๋ ํ๋ผ๋ฏธ์ค ๊ธฐ๋ฐ API๋ฅผ ์ ๊ณตํฉ๋๋ค.
Node.js ์คํธ๋ฆผ๊ณผ ๋ธ๋ผ์ฐ์ ์คํธ๋ฆผ์ ๋ชจ๋ ์คํธ๋ฆฌ๋ฐ ๋ฐ์ดํฐ ์ฒ๋ฆฌ๋ฅผ ๋ชฉ์ ์ผ๋ก ํ์ง๋ง, ๋์ํ๋ ํ๊ฒฝ์ด ๋ค๋ฅด๋ฉฐ ๊ฐ ํ๊ฒฝ์ด ๋ฐ๋ฅด๋ API์ ํ์ค๋ ์กฐ๊ธ์ฉ ๋ค๋ฆ ๋๋ค. Node.js ์คํธ๋ฆผ์ ์ด๋ฒคํธ ๊ธฐ๋ฐ์ด๋ฉฐ ์๋ฒ ์ธก ์์ ์ ์ ํฉํ ๋ฐ๋ฉด, ๋ธ๋ผ์ฐ์ ์คํธ๋ฆผ์ ์ต์ ์น ํ์ค์ ๋ฐ๋ผ ํ๋ผ๋ฏธ์ค ๊ธฐ๋ฐ์ด๋ฉฐ ํด๋ผ์ด์ธํธ ์ธก ์์ ์ ๋ง๊ฒ ์กฐ์ ๋์์ต๋๋ค.
๋ ํ๊ฒฝ ๋ชจ๋ ์ง์ํ๊ธฐ ์ํด ๋ฆฌ์กํธ์๋ Node.js ์คํธ๋ฆผ์ฉ renderToPipeableStream
๊ณผ ๋ธ๋ผ์ฐ์ ์คํธ๋ฆผ์ฉ renderToReadableStream
ํจ์๊ฐ ์์ต๋๋ค. renderToReadableStream
API๋ renderToPipeableStream
๊ณผ ์ ์ฌํ์ง๋ง Node.js ๋ค์ดํฐ๋ธ ์คํธ๋ฆผ ๋์ ๋ธ๋ผ์ฐ์ ์์ ์ฝ์ ์ ์๋ ์คํธ๋ฆผ์ ๋ฐํํฉ๋๋ค.