์ด์ ๊ธ์ ๋ฆฌ์กํธ 19์์ ์๋ก์์ง ๊ธฐ๋ฅ๋ค์ด๋ ๊ฐ์ ๋ ์ฌํญ์ ๋ํด ์ดํด๋ดค์๋ค.
์ค๋์ Dan Abramov๊ฐ ๋ฐํํ "React for Two Computers"์ ๋ํด ์ ๋ฆฌํด๋ณด๊ณ ์ ํ๋ค.
ํ ํฌ ์์ ๋ถ๋ถ์์ Dan์ ํด๋น ์ฃผ์ ์ ๋ํ ํ ํฌ๋ฅผ ๊ธ๋ก ํฌ์คํ ํ๊ธฐ ์ํด ์ฌ๋ฌ ์ฐจ๋ก ์๋ํด ๋ดค์ง๋ง,
"ํ ํฌ" ์ด๊ธฐ ๋๋ฌธ์ ์ ๋ฌํ ์ ์๋ ๊ฒ๋ค์ด ์๋ ๋ถ๋ถ๋ค์ด "๊ธ"๋ก์จ๋ ์ ๋ฌํ๊ธฐ ์ด๋ ค์ด ์ ์ ๋ํด ์ด์ผ๊ธฐํ๋ค.
๋ฌ๊ธ์์ด ์ ํ ํฌ์ ํฌ์คํธ์ ๋ํด ์ค๋ช ํ๋ ์ถ๊ฒ ์ง๋ง,
์ค๋ ์ฃผ์ ๋ ํ ํฌ(ํ์ฌ, ํด๋ผ์ด์ธํธ)์ ํฌ์คํธ(๊ณผ๊ฑฐ, ์๋ฒ)์ ๋ฐ์ ํ ๋น์ ์ด๋ฏ๋ก ๊ทธ ํน์ฑ์ ๊ณ ๋ คํ๊ณ ํ ํฌ๋ฅผ ๋ค์ผ๋ฉด ์ดํดํ๊ธฐ ๋ ์ฌ์ด ๊ฑฐ ๊ฐ๋ค.
ํ ํฌ๋ ํผํฌ๋จผ์ค์ด์ ์คํ ๋ฆฌ๋ผ๊ณ ํ๋ค.
๊ณต์ฐ์ฒ๋ผ ์ค์๊ฐ์ผ๋ก ๋ง์ ํ๊ณ ํ๋์ ๋ณด์ฌ์ฃผ๊ธฐ๋ ํ๋ค๋ ์ ์์ ํผํฌ๋จผ์ค๊ณ ,
ํ ํฌ๋ฅผ ํ๊ธฐ ์ํด ์ ๋ฌํ ๋ด์ฉ์ด ์๋ค๋ ์ ์์ ์คํ ๋ฆฌ์ด๋ค.
ํ์ฌ์ ํ ํฌ๋ฅผ ํ๊ณ ์๋ Dan๊ณผ ๊ณผ๊ฑฐ์ ๋ฐํ์ค๋น๋ฅผ ํ๊ณ ์๋ Dan, ์ด ๋ ์ฃผ์ฒด ๊ฐ์ ์ฝ๋ผ๋ณด๋ผ๊ณ ์๊ฐํ ์ ์๋ค.
์๊ณต๊ฐ์ ์ผ๋ก ์ด ๋ Dan์ ๊ณต์กดํ ์ ์์ง๋ง ๊ณผ๊ฑฐ์ ํ์ฌ์ Dan ๊ฐ์๋ ์ด๋ ํ ์ปค๋ฎค๋์ผ์ด์
์ด ์๋ค.
"ํ ํฌ"๋ผ๋ ๊ฒ์ ์์ฑํ๊ธฐ ์ํด์๋ ๊ณผ๊ฑฐ์ Dan์ด ๋ฐํ ์ฃผ์ ์ ๋ํ ์๋ฃ(์คํฌ๋ฆฝํธ)๋ฅผ ์ค๋นํด์ผ ํ๊ณ ,
ํ์ฌ์ Dan์ ํด๋น ์๋ฃ๋ฅผ ๊ฐ์ง๊ณ ๋ฐํ๋ฅผ ์ ๋ฌํด์ผ ํ๋ค.
๋ง์ฐฌ๊ฐ์ง๋ก ๋ฎค์ง์ปฌ ๊ณต์ฐ์ ํ๋ค๊ณ ํ๋ฉด, ๊ณต์ฐ์ ํ๊ธฐ ์ํ ๋๋ณธ์ด ์กด์ฌํด์ผ ํ๊ณ , ๊ณต์ฐ์ ํ๋ ๋ฐฐ์ฐ๋ค์ ํด๋น ๋๋ณธ์ ์ฐ๊ธฐํด์ผ ๋ฎค์ง์ปฌ์ด ์์ฑ๋๋ค. ๋๋ณธ๋ง ์ผ๋ค๊ณ ๋ฎค์ง์ปฌ ๊ณต์ฐ์ด๋ผ๊ณ ํ ์ ์๊ณ , ๋ฎค์ง์ปฌ ๋ฐฐ์ฐ๊ฐ ๋๋ณธ ์๋ ์ฐ๊ธฐ๋ฅผ ๋ณด์ฌ์ค๋ค๊ณ ๋ฎค์ง์ปฌ ๊ณต์ฐ์ด๋ผ๊ณ ๋ณด๊ธฐ ์ด๋ ต๋ค.
์ด ์ปค๋ฎค๋์ผ์ด์ ์ "์ปดํจํฐ ์ธ๊ณ"์์๋ ์ ์ฌํ๊ฒ ์กด์ฌํ๋๋ฐ, ์ค๋์ ์ฃผ์ ๊ฐ ์ด ์๋ฒ์ ํด๋ผ์ด์ธํธ์ ๊ด๊ณ ๋ํ ๊ฒ์ด๋ค.
์๋ฒ์ ํด๋ผ์ด์ธํธ๋ฅผ ๋ ผํ๋ ์ฌ๋ฌ ๊ฐ์ง ๋ฐฉ๋ฉด์ด ์๊ฒ ์ง๋ง, Dan์ ๊ทธ์ค "์ ํต์ ์ธ ์ฝํ ์คํธ์์์ ๋ ๋จธ์ (์ปดํจํฐ ํ๋ก๊ทธ๋จ)"์ ๋ํด ๋ค๋ฃฌ๋ค. ์ฆ, "์ ํต์ ์ธ ์น๊ฐ๋ฐ์์์ request/response ๋ชจ๋ธ"์ ์ฝํ ์คํธ์์ ํด๋ผ์ด์ธํธ๊ฐ ์๋ฒ์๊ฒ ์ด๋ค ํ์ด์ง๋ฅผ ์์ฒญํ๊ณ , ์๋ฒ๊ฐ html/์คํฌ๋ฆฝํธ ๋ฑ์ ์๋ต์ ํ๋ค.
์ด ๊ด๊ณ์ ๋ํด์ ๋ ์ดํด๋ณด๊ธฐ ์ํด ๋ค์๊ณผ ๊ฐ์ ์คํฌ๋ฆฝํธ๊ฐ ์๋ค๊ณ ํ์. ์๋์ ์ฝ๋๋ ๋๋ค์ผ๋ก ๊ณ ์์ด ์ด๋ฆ์ ๊ณจ๋ผ์ฃผ๋ ์งง์ ํ๋ก๊ทธ๋จ์ด๋ค.
const catNames = ['Alonzo', 'Bill Bailey', 'Bombalurina', 'Electra', 'Plato'];
function onClick(){
const response = await fetch('/api/cat-names');
const json = await response.json();
const {catNames} = json;
const index = Math.floor(Math.random() * catNames.length);
const catName = catNames[index];
document.body.innerText = catName;
}
ํ์ง๋ง ๊ณ ์์ด ์ด๋ฆ (catNames)์ฒ๋ผ ํ๋์ฝ๋ฉ๋ ์๋ฃ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ๋ณด๋ค ์ข ๋ ์ ์ฐํ๊ฒ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์ ๊ฐ์ ธ์ค๊ฑฐ๋ ํ์ผ ์์คํ ์์ ์ฝ๊ฑฐ๋ ์ฑ์งํผํฐ์๊ฒ ๋ฌผ์ด๋ณด๊ณ ์ถ์ ์๋ ์๋ค.
์ ํต์ ์ธ ๋ฐฉ์์ ๋ฐ๋ผ์ ๋ค์๊ณผ ๊ฐ์ด api์ ์์ฒญ์ ํ ์ ์๋ค.
// client.ts
async function onClick(){
const response = await fetch('/api/cat-names');
const json = await response.json();
const {catNames} = json;
const index = Math.floor(Math.random() * catNames.length);
const catName = catNames[index];
document.body.innerText = catName;
}
ํ์ง๋ง ์ด๋ ๊ฒ ์ฝ๋๋ฅผ ๋ณ๊ฒฝํ๋ฉด api ์๋ต์ ๋ฐ์์ค๊ธฐ๊น์ง์ ์๊ฐ์ฐจ ๋๋ฌธ์ ์ฆ๊ฐ์ ์ผ๋ก ํ๋ฉด์ ์ด๋ฆ์ด ๋ณด์ด์ง ์๋๋ค.
throttle ์ค์ ์ผ๋ก ์ธํฐ๋ท์ด ๋๋ฆฐ ํ๊ฒฝ์ ๋ชจ๋ฐฉํ๋ค๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
์ ์ ์ธํฐํ์ด์ค๊ฐ ๋๋ ๋ ์์ ์๋ ๊ณ ์์ด ์ด๋ฆ์ ๋ํ ๋ฐ์ดํฐ๊ฐ ์๊ธฐ ๋๋ฌธ์ api๋ฅผ ํธ์ถํด์ ํด๋น ๋ฐ์ดํฐ๊ฐ ๋ด๋ ค์ง ๋๊น์ง ๊ธฐ๋ค๋ ค์ผ ํ๋ค.
๋ง์ฝ์ ์ธํฐ๋ท์ด ์์ ์ฐ๊ฒฐ๋์ง ์์ ์ํ๋ผ๋ฉด ๋ฐ์ดํฐ๊ฐ ๋ณด์ด์ง ์์ ์ ์๋ค.
๋๋ถ๋ถ์ ์ํฉ์์๋ ์ด๋ฐ ๋์ ๋ฐฉ์์ด ๋ฌธ์ ๋์ง ์๋๋ค.
๋งํฌ๋ฅผ ํด๋ฆญํ๊ฑฐ๋, ํผ์ ์ ์ถํ๋ ๋์์ ๋น์ฐํ ์๋ฒ๋ฅผ ํตํด ์ด๋ค ๋ฐ์ดํฐ๊ฐ ํ์ํ ๊ฒ ๋น์ฐํ๊ธฐ ๋๋ฌธ์,
๋๋ ์ด๊ฐ ๋ฐ์ํ ์ ์๋ค๋ ๊ฒ ์ง์ ๊ฐ๋ฅํ๋ค.
ํ์ง๋ง ์ด๋ค ๊ฒฝ์ฐ์๋ ๋์๋ค์ด ๋๊ธฐ์ (latency ์ต์ํ)์ผ๋ก ์ด๋ฃจ์ด์ก์ผ๋ฉด ์ข๊ฒ ๋ค๊ณ ์๊ฐํ ์ ์๋ค.
์๋ฅผ ๋ค์ด, ํ์ด์ง์ ๋ฐ์ดํฐ๊ฐ ์๋ ์ํ๊ฐ ์๋ ํ์ด์ง๊ฐ ๋ณด์ด๋ ๋์์ ํ์ด์ง์ ๋ฐ์ดํฐ๊ฐ ํจ๊ป ๋ณด์ด๊ธฐ๋ฅผ ์ํ ์ ์๋ค.
์ด๋ป๊ฒ ํ๋ฉด ํ์ด์ง๋ฅผ ๋ก๋ํ๋ ๋์์ ๋ฐ์ดํฐ๋ฅผ ํจ๊ป ๋ณด์ฌ์ค ์ ์์๊น?
ํด๋ผ์ด์ธํธ์ ์ ์ฅ์์ ์ดํด๋ณด๋ฉด, ์ด์จ๋ api ์์ฒญ์ ํ๋ฉด ํด๋น ๋ฐ์ดํฐ๊ฐ ๋์ฐฉํ ๋๊น์ง ๊ธฐ๋ค๋ ค์ผ ํ๋ค๋ ์ ์์ ํ ์ ์๋ ๊ฒ ์์ด ๋ณด์ธ๋ค.
๊ทธ๋ผ ์๋ฒ ์ฝ๋์์ ์ ๋ณผ ์ ์๋ ๊ฒ ์์๊น?
๋ค์์ api/cat-names์ ์๋ฒ ์ฝ๋๋ค.
import { readFile } from 'fs/promises';
async function server(url) {
if (url === '/api/cat-names') {
const catFile = await readFile('./cats.txt', 'utf8');
const catNames = catFile.split('\n');
const json = { catNames };
return json;
}
if (url === '/') {
return `<!doctype html>
<html>
<head>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<script src="client.ts"></script>
<button onClick="onClick()">Reveal</button>
</body>
</html>
`;
}
}
ํ ๊ฐ์ง ์ง์คํด์ ๋ณผ ๋ถ๋ถ์ <script src="client.ts"></script>์ด๋ค.
์ด ๋ถ๋ถ์ ์์์ ์์ฑํ๋ ํด๋ผ์ด์ธํธ ์ฝ๋๋ฅผ ๋ถ๋ฌ์จ๋ค.
์๋ฒ ์ฝ๋๋ ์ฌ์ค ๋ค์๊ณผ ๊ฐ์ ๊ฒ์ด๋ค.
import { readFile } from 'fs/promises';
async function server(url) {
if (url === '/api/cat-names') {
const catFile = await readFile('./cats.txt', 'utf8');
const catNames = catFile.split('\n');
const json = { catNames };
return json;
}
if (url === '/') {
return `<!doctype html>
<html>
<head>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<script>
async function onClick(){
const response = await fetch('/api/cat-names');
const json = await response.json();
const {catNames} = json;
const index = Math.floor(Math.random() * catNames.length);
const catName = catNames[index];
document.body.innerText = catName;
}
</script>
<button onClick="onClick()">Reveal</button>
</body>
</html>
`;
}
}
์ด ์ ๊น์ง๋ ์๋ฒ ์ฝ๋์ ํด๋ผ์ด์ธํธ ์ฝ๋๋ฅผ ์๋ก ์ปค๋ฎค๋์ผ์ด์ (์์ฒญ/์๋ต ์ฃผ๊ณ ๋ฐ๊ธฐ)ํ๋ ๋ณ๊ฐ์ ๋ ๊ฐ์ ํ๋ก๊ทธ๋จ์ผ๋ก ๋ณด์๋ค๋ฉด,
์ง๊ธ์ ์ฝ๋๋ก ๋ดค์ ๋๋ ํ๋์ ํ๋ก๊ทธ๋จ์ด ๋ ๊ฐ์ ์ปดํจํฐ, ๋ ๊ฐ์ ๋จ๊ณ๋ก ๋์ํ๋ค๋ ๊ด์ ์ผ๋ก ์ดํด๋ณผ ์ ์๋ค.
์ด ๊ด์ ์ผ๋ก ์๊ฐ์ ํด๋ณด๋ฉด ์์ ์ฝ๋๋ฅผ ๊ฐ์ ํ ์ ์๋ ๋ฐฉ์์ ๋ ์ฌ๋ฆด ์ ์๋ค.
๋จผ์ ํด๋ฆญ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ๋๊ธฐ์ ์ผ๋ก ๋ง๋ค๊ธฐ ์ํด aysnc๋ฅผ ์ ๊ฑฐํด ๋ณด์.
import { readFile } from 'fs/promises';
async function server(url) {
if (url === '/api/cat-names') {
const catFile = await readFile('./cats.txt', 'utf8');
const catNames = catFile.split('\n');
const json = { catNames };
return json;
}
if (url === '/') {
return `<!doctype html>
<html>
<head>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<script>
// โจ ๋ณ๊ฒฝ
function onClick(){
const response = await fetch('/api/cat-names');
const json = await response.json();
const {catNames} = json;
const index = Math.floor(Math.random() * catNames.length);
const catName = catNames[index];
document.body.innerText = catName;
}
</script>
<button onClick="onClick()">Reveal</button>
</body>
</html>
`;
}
}
ํ์ง๋ง api ํธ์ถ ์ฝ๋๋ await์ด๋ฏ๋ก async์ ์ง์ ์ด๋ฃจ์ด์ผ ํ๊ธฐ ๋๋ฌธ์ ํด๋น ๋ถ๋ถ์ ๋ํ ์ฒ๋ฆฌ๊ฐ ํ์ํ๋ค.
๋ฆฌ์กํธ์๋ 'lifting state up'๋ผ๋ ๊ฒ์ด ์๋ค. ์ด๋ ๋ถ๋ชจ ์ปดํฌ๋ํธ์์ state๋ฅผ ์ ์ธํ๊ณ ์์(ํ์) ์ปดํฌ๋ํธ์๊ฒ ์ ๋ฌํ๋ ๋ฐฉ์์ธ๋ฐ,
๋ฐ์ดํฐ๋ ๋ง์ฐฌ๊ฐ์ง๋ก ๋์ด์ฌ๋ฆด(lift up) ์ ์์๊น? state๋ฅผ ๋ถ๋ชจ ์ปดํฌ๋ํธ๋ก ๋์ด์ฌ ๋ฆฌ ๋ฏ์ด, ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ชจ ์ปดํจํฐ์ธ ์๋ฒ๋ก ๋์ด์ฌ๋ฆด ์ ์์ง ์์๊น?
๋ค์๊ณผ ๊ฐ์ด fetch ํ๋ ์ฝ๋ ๋ถ๋ถ์ ์๋ฒ๋ก ๋์ด์ฌ๋ ค๋ณด์.
import { readFile } from 'fs/promises';
async function server(url) {
if (url === '/api/cat-names') {
const catFile = await readFile('./cats.txt', 'utf8');
const catNames = catFile.split('\n');
const json = { catNames };
return json;
}
if (url === '/') {
// โจ ๋ณ๊ฒฝ
const response = await fetch('/api/cat-names');
const json = await response.json();
return `<!doctype html>
<html>
<head>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<script>
function onClick(){
const {catNames} = json;
const index = Math.floor(Math.random() * catNames.length);
const catName = catNames[index];
document.body.innerText = catName;
}
</script>
<button onClick="onClick()">Reveal</button>
</body>
</html>
`;
}
}
์ถ๊ฐ๋ก ์ด์ fetch๊ฐ ์๋ฒ์์ ๋์ํ๊ธฐ ๋๋ฌธ์ ํธ์คํธ๋ฅผ ๋ช ์ํด ์ฃผ์.
import { readFile } from 'fs/promises';
async function server(url) {
if (url === '/api/cat-names') {
const catFile = await readFile('./cats.txt', 'utf8');
const catNames = catFile.split('\n');
const json = { catNames };
return json;
}
if (url === '/') {
// โจ ๋ณ๊ฒฝ
const response = await fetch('http://localhost:3000/api/cat-names');
const json = await response.json();
return `<!doctype html>
<html>
<head>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<script>
function onClick(){
const {catNames} = json;
const index = Math.floor(Math.random() * catNames.length);
const catName = catNames[index];
document.body.innerText = catName;
}
</script>
<button onClick="onClick()">Reveal</button>
</body>
</html>
`;
}
}
ํ์ง๋ง ์คํ์ ํ๋ฉด ๋ค์๊ณผ ๊ฐ์ด json์ ๋ํ reference ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
์ผ๋ฐ์ ์ธ ์ค์ฒฉํจ์์๋ค๋ฉด ํด๋ก์ ๋๋ถ์ ์๋ฌ๊ฐ ๋ฐ์ํ์ง ์์์ ๊ฒ์ด๋ค.
ํ์ง๋ง ํ์ฌ ์ฝ๋๋ ๋คํธ์ํฌ ์์์์ ํด๋ก์ ๋ฅผ ์ ์ฉํ๋ ค๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ๋์ํ์ง ์๋๋ค.
json์ ๋ชฉ์ ์ ๋คํธ์ํฌ ์์์ ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๊ธฐ ์ํ ๊ฒ์ด๋ค.
์ง๊ธ๊น์ง ์ดํด๋ณธ ํ๋ก๊ทธ๋จ์ ํ๋ก๊ทธ๋จ์ string์ผ๋ก ์ ์กํ๋ค.
์ด ๊ธฐ๋ฅ์ ํ๊ธฐ ์ํด์ ๋ค์๊ณผ ๊ฐ์ด ํ๋ก๊ทธ๋จ๊ณผ ๊ฐ์ด ๋ฐ์ดํฐ๋ฅผ ์ ์กํ๊ธฐ ์ํด json์ string์ผ๋ก ๋ณ๊ฒฝํด ์ค ์ ์๋ค.
import { readFile } from 'fs/promises';
async function server(url) {
if (url === '/api/cat-names') {
const catFile = await readFile('./cats.txt', 'utf8');
const catNames = catFile.split('\n');
const json = { catNames };
return json;
}
if (url === '/') {
const response = await fetch('http://localhost:3000/api/cat-names');
const json = await response.json();
return `<!doctype html>
<html>
<head>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<script>
function onClick(){
// โจ ๋ณ๊ฒฝ
const {catNames} = ${JSON.stringify(json)};
const index = Math.floor(Math.random() * catNames.length);
const catName = catNames[index];
document.body.innerText = catName;
}
</script>
<button onClick="onClick()">Reveal</button>
</body>
</html>
`;
}
}
์ฝ๋๋ฅผ ์์ ํ๊ณ ๋์ ๋ค์ ๋ฒํผ์ ํด๋ฆญํ์ ๋ ๋คํธ์ํฌ ์์ฒญ์ด ๋ฐ์ํ์ง ์๋๋ค๋ ๊ฑธ ๋ณผ ์ ์๋ค.
์ด๋ ๋ฐ์ดํฐ๊ฐ ํ์ด์ง๋ฅผ ๋ถ๋ฌ์ฌ ๋ ์ด๋ฏธ ๊ฐ์ด ์๊ธฐ ๋๋ฌธ์ด๋ค.
์ฝ๋๋ฅผ ์ดํด๋ณด๋ ๊ณ ์์ด ์ด๋ฆ์ ํ์ผ์์ ์ฝ์ด์จ๋ค๋ ์ญํ ํ๋๋ฟ์ด๋ฏ๋ก api๊ฐ ๊ตณ์ด ํ์ ์๋ ๊ฒ ๊ฐ๋ค.
๋ค์๊ณผ ๊ฐ์ด ํด๋น ๋ถ๋ถ์ ๋ฐ๊ฟ๋ณด์.
import { readFile } from 'fs/promises';
async function server(url) {
if (url === '/') {
const catFile = await readFile('./cats.txt', 'utf8');
const catNames = catFile.split('\n');
const json = await response.json();
return `<!doctype html>
<html>
<head>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<script>
function onClick(){
// โจ ๋ณ๊ฒฝ
const {catNames} = ${JSON.stringify(json)};
const index = Math.floor(Math.random() * catNames.length);
const catName = catNames[index];
document.body.innerText = catName;
}
</script>
<button onClick="onClick()">Reveal</button>
</body>
</html>
`;
}
}
response์ ์ฝ๋๋ฅผ ๋ค์ ์ดํด๋ณด๋ฉด ๋ฐ์ดํฐ ํ๋ฆ์ ๋ํด์ ํ์ ํ ์ ์๋ค.
response๋ก ๋ฐ์ catNames๋ ๋งค์ฐ ๊ธด๋ฐ, ์ค์ ๋ก ์ฐ๋ฆฌ๊ฐ ํ์ํ ๊ณ ์์ด ์ด๋ฆ์ ํ๋๋ฟ์ด๋ค.
์ฝ๋์์ ์ดํด๋ณด๋ฉด ํ์ผ์ ์ฝ์ด์์ ๊ณ ์์ด ์ด๋ฆ๋ค์ ๋ฐฐ์ด์ ๋ด์, ํด๋น ์ด๋ฆ๋ค ์ค ๋๋ค์ผ๋ก ํ๋๋ฅผ ํ ์คํธ๋ก ๋ณด์ฌ์ค๋ค.
์ ์ฒด ๋ฆฌ์คํธ๋ฅผ ๊ฐ์ ธ์ ํด๋ผ์ด์ธํธ์์ ๋๋ค ํ๊ฒ ํ๋๋ฅผ ์ ํํ ํ์ ์์ด, ๋ค์๊ณผ ๊ฐ์ด ์๋ฒ์์ ๋ฐ๋ก ํ๋๋ง ๊ณจ๋ผ์ ๋ณด๋ด์ฃผ๋ฉด ๋๋ค.
import {readFile} from 'fs/promises';
import { readFile } from 'fs/promises';
async function server(url) {
if (url === '/') {
const catFile = await readFile('./cats.txt', 'utf8');
const catNames = catFile.split('\n');
// โจ ๋ณ๊ฒฝ
const index = Math.floor(Math.random() * catNames.length);
const catName = catNames[index];
const json = {catName};
return `<!doctype html>
<html>
<head>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<script>
function onClick(){
// โจ ๋ณ๊ฒฝ
const {catNames} = ${JSON.stringify(json)};
document.body.innerText = catName;
}
</script>
<button onClick="onClick()">Reveal</button>
</body>
</html>
`;
}
}
์ด์ ๋ ์๋ต์ผ๋ก ๋ฐ์ ๋ฐ์ดํฐ๊ฐ ์ฐ๋ฆฌ๊ฐ ํ์๋ก ํ๋ ๋๋ค ํ ๊ณ ์์ด ์ด๋ฆ ํ๋๋ฟ์ด๋ค.
1. ํ๋์ ์ปดํจํฐ์์๋ ๋ค์๊ณผ ๊ฐ์ ๊ฒฐ๊ณผ๋ฌผ์ ๋ง๋ค์ด๋ด๊ณ ,
import {readFile} from 'fs/promises';
const catFile = await readFile('./cats.txt', 'utf8');
const catNames = catFile.split('\n');
const index = Math.floor(Math.random() * catNames.length);
const catName = catNames[index];
const json = {catName};
return `<!doctype html>
<html>
<head>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<script>
function onClick(){
// โจ ๋ณ๊ฒฝ
const {catNames} = ${JSON.stringify(json)};
document.body.innerText = catName;
}
</script>
<button onClick="onClick()">Reveal</button>
</body>
</html>
`;
2. ์ดํ ๋ ๋ค๋ฅธ ์ปดํจํฐ์์๋ ๋ค์๊ณผ ๊ฐ์ ์ฝ๋๋ฅผ ์คํํ๋๋ก ๋ณด๋ด์ง๊ฒ ๋๋ค.
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<script>
function onClick(){
const {catNames} = ${JSON.stringify(json)};
document.body.innerText = catName;
}
</script>
<button onClick="onClick()">Reveal</button>
</body>
</html>
1๊ณผ 2๋ ์๋ก ๋ค๋ฅธ ์ธ๊ณ์์ ์คํ๋๊ณ , ์ด ๊ณผ์ ์ ์ ๊ทผ๋ฒ๊ณผ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์๊ด์์ด ๋์ผํ ํํ์ด๋ค.
ํ์ง๋ง ์์ ์ฝ๋์ฒ๋ผ ์ฝ๋๋ฅผ ์์ฑํด์ผ ํ๋ค๋ฉด syntax highlight๋ ์ง์๋์ง ์๊ณ , ๋ชจ๋์ด๋ ํ์ ์ฒดํฌ์ ํํ๋ ๋๋ฆฌ์ง ๋ชปํ๋ค.
์ง๊ธ์ ๋จ์ํ ์ฝ๋์ง๋ง ํด๋น ์ฝ๋์ ๊ธฐ๋ฅํ๋ feature ํ๋ ์ถ๊ฐํ๋ ค๋ฉด ๊ธฐํ๊ธ์์ ์ผ๋ก ๋ณต์กํด์ง ๊ฑฐ๋ผ๋ ๊บผ๋ฆผ์นํ ๋๋๋ ๋ ๋ค.
์์ ์ฝ๋ ๋ฉ์ด๋ฆฌ๋ฅผ split ํ ์ ์์๊น?
๊ฐ๊ฐ์ ํจ์๋ก ๋ถ๋ฆฌํ๊ณ ๊ฐ ํจ์์ feature์ ๋ง๋ ์ด๋ฆ์ ๋ถ์ฌ์ฃผ๊ณ ์ฌ์ฉํ ์ ์๋ค๋ฉด ํด๋น ์ฝ๋๋ ํจ์ฌ ๊ฐ๊ฒฐํด์ง ๊ฑฐ ๊ฐ๋ค.
๋ฆฌ์กํธ๋ฅผ ์จ๋ณธ ์ ์ด ์๋ค๋ฉด component๋ฅผ ์ฝ๊ฒ ๋ ์ฌ๋ฆด ์ ์์ ๊ฒ์ด๋ค. ํด๋น ์ฝ๋๋ฅผ ์ปดํฌ๋ํธ์ ์ธก๋ฉด์์ ๋ค์ ์ง๋ณด์.
๋จผ์ ๊ณ ์์ด ์ด๋ฆ์ ์์ฑํ ์ปดํฌ๋ํธ๋ ๋ค์์ ์ ๋ณด๊ฐ ํ์ํ ๊ฒ์ด๋ค.
import {readFile} from 'fs/promises';
export default async function CatNameGenerator(){
const catFile = await readFile('./cats.txt', 'utf8');
const catNames = catFile.split('\n');
const index = Math.floor(Math.random() * catNames.length);
const catName = catNames[index];
// ...
}
์ผ๋จ 1์์ ๋ฝ์์๋ ์ฝ๋๋ฅผ ์ ์ธํ๊ณ ํ์ํ ๊ฑฐ ๊ฐ์ ๋ถ๋ถ๋ค์ ์ดํด๋ณด์.
ํ์ด์ง๋ฅผ ๋ก๋ํ ๋ ์ด๋ฏธ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์๊ธฐ ๋๋ฌธ์ ๋ค์๊ณผ ๊ฐ์ด catName์ onClick๋ฐ์ผ๋ก ๋นผ๋ผ ์ ์๋ค.
// ...์ค๋ต...
<script>
// โจ ๋ณ๊ฒฝ
const {catNames} = ${JSON.stringify(json)};
function onClick(){
document.body.innerText = catName;
}
</script>
<button onClick="onClick()">Reveal</button>
// ...์ค๋ต...
์ฌ๊ธฐ์ {catNames}๊ฐ ๋ฆฌ์กํธ์์ ์ต์ํ ํํ์ธ props๋ฅผ ๋๊ฒจ๋ฐ๋ ์ปดํฌ๋ํธ์ ๋น์ทํด ๋ณด์ผ ์ ์๋ค.
import {readFile} from 'fs/promises';
export default async function CatNameGenerator(){
const catFile = await readFile('./cats.txt', 'utf8');
const catNames = catFile.split('\n');
const index = Math.floor(Math.random() * catNames.length);
const catName = catNames[index];
// ...
}
// โจ ์ถ๊ฐ
function RevealButton({catName}){
function onClick(){
document.body.innerText = catName;
}
return (
<button onClick={onClick()}>
Reveal
</button>
);
}
โ ๏ธ html์ด ์๋ jsx์ด๋ฏ๋ก onClick()๋ฅผ ์ค๊ดํธ๋ก ๊ฐ์ธ์ค์ผ ํ๋ค.
์ด์ ์ด ๋ฐ์ดํฐ๋ฅผ ๋ณด์ฌ์ค ์ปดํฌ๋ํธ RevealButton์ ๋ฆฌํดํ๋ฉด ์ปดํฌ๋ํธ๋ก ์ฎ๊ธฐ๋ ์์ ์ ๋๋ ๊ฑฐ ๊ฐ๋ค.
import {readFile} from 'fs/promises';
export default async function CatNameGenerator(){
const catFile = await readFile('./cats.txt', 'utf8');
const catNames = catFile.split('\n');
const index = Math.floor(Math.random() * catNames.length);
const catName = catNames[index];
// โจ ์ถ๊ฐ
return <RevealButton catName={catName}/>;
}
function RevealButton({catName}){
function onClick(){
document.body.innerText = catName;
}
return (
<button onClick={onClick()}>
Reveal
</button>
);
}
'๋ชจ๋ ๋น ์ง์์ด ์ฎ๊ฒจ๋จ๋'์ถ์ง๋ง ํ ๊ฐ์ง ๊ฑธ๋ฆฌ๋ ๋ถ๋ถ์ด ์๋ค. ์ด ๋ถ๋ถ์ ๋ฌด์ํ ์ ์๋?
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
// ...์ค๋ต...
</body>
</html>
ํ ํฌ ์ค๋น๋ฅผ ํ๊ณ ์๋ Dan๊ณผ ํ์ฌ ๋ฐํ๋ฅผ ํ๊ณ ์๋ Dan์ด ๊ฐ์ ์๊ณต๊ฐ์ ์กด์ฌํ ์ ์๋ฏ์ด,
์์์ ์์ฑํ๋ ์ปดํฌ๋ํธ ์ฝ๋๋ ํ๋๋ก ์กด์ฌํ ์ ์๋ค. ์๋ก ๋ค๋ฅธ ํ๊ฒฝ์์ ์คํ๋๊ณ , ์๋ก ๋ค๋ฅธ ์๊ฐ์ ์๋ํ๋ ํ๋ก๊ทธ๋จ์ด๋ค.
์ฆ, ์๋์ ์ฝ๋๊ฐ ์๋ํ๊ณ ์๋ ์ธ๊ณ์
import {readFile} from 'fs/promises';
export default async function CatNameGenerator(){
const catFile = await readFile('./cats.txt', 'utf8');
const catNames = catFile.split('\n');
const index = Math.floor(Math.random() * catNames.length);
const catName = catNames[index];
return <RevealButton catName={catName}/>;
}
์๋์ ์ฝ๋๊ฐ ์๋ํ๊ณ ์๋ ์ธ๊ณ๋ ์๋ก ๋ฌผ๋ฆฌ์ ์ผ๋ก ๋ถ๋ฆฌ๋์ด ์๋ค.
function RevealButton({catName}){
function onClick(){
document.body.innerText = catName;
}
return (
<button onClick={onClick()}>
Reveal
</button>
);
}
์ผ์ชฝ ์ฝ๋๋ ์๋ฒ์์ ๋์๊ฐ๋ ์ฝ๋์ด๊ณ , ์๋ฒ์์ import ๋๋ ๊ฒ๋ค์ ์๋ฒ์ ์กด์ฌํ๋ ๊ฒ๋ค์ด๊ณ ,
์ค๋ฅธ์ชฝ์ ์ฝ๋๋ ๋ธ๋ผ์ฐ์ ์์ ๋์๊ฐ๋ ์ฝ๋์ด๋ฏ๋ก, ๋ธ๋ผ์ฐ์ ์์ import ๋๋ ๊ฒ๋ค์ ๋ธ๋ผ์ฐ์ ์ ์กด์ฌํ๋ค.
์ด ๋ ์ฌ์ด์ ์ ๋ณด๋ ์ด๋ป๊ฒ ์ฃผ๊ณ ๋ฐ๊ฒ ๋๋ ๊ฑธ๊น?
์ด์ ์ ์ฝ๋๋ฅผ ์ดํด๋ณด๋ฉด html์ ํด๋นํ๋ ๋ถ๋ถ์ด string์ผ๋ก ์ ๋ฌ๋ ๋, JSON.stringify๋ก ํจ๊ป ๋ณด๋ด์ง๋ค.
`<!doctype html>
<html>
<head>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<script>
function onClick(){
const {catNames} = ${JSON.stringify(json)};
document.body.innerText = catName;
}
</script>
<button onClick="onClick()">Reveal</button>
</body>
</html>
`
๊ทธ๋ผ ์๋ก ๋ค๋ฅธ ๋ ์ธ๊ณ(ํ๋ก๊ทธ๋จ)๊ฐ ์๊ณ , ์ด ๋ ์ธ๊ณ ๊ฐ์ ํ์ชฝ ๋ฐฉํฅ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ์ ์ ์๋ ๋ฌธ(door)์ด ์๋ค๋ฉด ์ด๋จ๊น?
๊ณผ๊ฑฐ๋ก๋ถํฐ ๋ฏธ๋์ ๋ฌด์ธ๊ฐ๋ฅผ ๋ณด๋ผ ์ ์๋ค๋ฉด? ๊ทธ๋ฆฌ๊ณ ๊ทธ ๋ฌด์ธ๊ฐ๊ฐ serialize ๊ฐ๋ฅํ text๋ json์ด๋ผ๋ฉด?
๊ทธ ๋ฌธ์ ํ๋ ๋ฌ์๋ณด์.
// โจ ์ถ๊ฐ
'use client';
function RevealButton({catName}){
function onClick(){
document.body.innerText = catName;
}
return (
<button onClick={onClick()}>
Reveal
</button>
);
}
Dan์ next๋ก ํด๋น ์ฝ๋๋ฅผ ์์ฑํ์ง๋ง, ๋ฆฌ์กํธ ์๋ฒ ์ปดํฌ๋ํธ๋ฅผ ์ง์ํ๋ค๋ฉด ์ด๋ค ํ๋ ์์ํฌ์์๋ ๋์๊ฐ๋ค.
๊ฐ์ ๋์์ ํ๋์ง ํ์ธ์ ํ์๋ฉด, rsc payload์ ํจ๊ป ์จ ๋ฐ์ดํฐ๋ฅผ ์ด ํด๋ฉด ๋๋ค ํ ๊ณ ์์ด ์ด๋ฆ ํ๋๊ฐ ํ์ด์ง์ ํจ๊ป ๋ด๋ ค์ง๋ค๋ ๊ฑธ ์ ์ ์๋ค.
์ ๋ฆฌํ์๋ฉด, ์ฐ๋ฆฌ๋ ์ข ์ข ์๋ฒ์ ์ปดํฌ๋ํธ๊ฐ ๋ณ๊ฐ์ ํ๋ก๊ทธ๋จ์ผ๋ก ์ธ์ํ๋ค.
์นํ์ด์ง UI๋ฅผ ๋๋ ํ ๋๋ ๋ ๊ฐ์ ์ปดํจํฐ๊ฐ ์กด์ฌํ๋ค๊ณ ๋ณผ ์ ์๋ค.
์ผ์ชฝ ๋ฐ๋๋ผ js์ ๊ฒฝ์ฐ return๋ฌธ ์ด์ ๊น์ง ์๋ฒ์์ ์คํ๋๊ณ , ํด๋ผ์ด์ธํธ์์ ์คํ๋ ํ๋ก๊ทธ๋จ์ return ํ๋ค.
๊ทธ๋ฆฌ๊ณ JSON.stringify๋ก ์๋ฒ์์ ํด๋ผ์ด์ธํธ๋ก ์ ๋ณด๋ฅผ ๋ณด๋ด์ค ๋ฌธ์ด ์กด์ฌํ๋ค๋ ๊ฑธ ์ ์ ์๋ค.
์ค๋ฅธ์ชฝ ๋ฆฌ์กํธ ๋ฒ์ ์ ๊ฒฝ์ฐ ์๋ฒ ์ปดํฌ๋ํธ์ธ CatNameGenerator์์ ๋ฐ์ดํฐ๋ฅผ ์ค๋นํ๊ณ ,
'use client' ํค์๋๋ฅผ ํตํด ์๋ฒ์์ ํด๋ผ์ด์ธํธ๋ก ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌ๋ฐ์ ๋ฌธ์ ๋ซ์ด์ค ์ ์๋ค๋ ๊ฑธ ๋ณผ ์ ์๋ค.
ํด๋น ์ง์์ด๋ก ์ด ๋ชจ๋์ ๋ถ๋ชจ ์ปดํฌ๋ํธ๋ฟ๋ง ์๋๋ผ ์๋ฒ๋ก๋ถํฐ ํ๋กญ์ค๋ฅผ ๋ฐ์ ์ ์๋ค๋ ๊ฒ์ ๋ช ์ํ๋ ๊ฒ์ด๋ค.
๐ ๋ง์น๋ฉด์
"use client"๊ฐ ์ด๋ค ์ญํ ์ ํ๋์ง ๊ฐ๊ฒฐํ๊ณ ์ฝ๊ฒ ์์๋ฅผ ํตํด ๋ณด์ฌ์ฃผ๋ ํ ํฌ์๋ ๊ฑฐ ๊ฐ๋ค.
๊ทธ๋ฆฌ๊ณ ๋ฐ๋๋ผ js๋ก ์๋ฒ์ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๊ฐ ์ด๋ป๊ฒ ์๊ฒผ์์ง๋ฅผ ๋ณด์ฌ์ค์ 'use client'์ ์ญํ ์ด ๋ ์๋ฟ์๋ค.
์๋ฌธ์ผ๋ก ๋ ํ ํฌ์ง๋ง ์ด๋ ค์ด ๋ด์ฉ์ด ์๋๋ฏ๋ก ๊ผญ ์๋ณธ ํ ํฌ๋ฅผ ๋ณด๋ฌ ๊ฐ๋ฉด ์ข์ ๊ฑฐ ๊ฐ๋ค.
ํฌ์คํ ์ ๋ง ์์ํ๋ ์์ ์์ ์ ๋ชฉ์ธ React for Two Computers๋ฅผ ์ด๋ป๊ฒ ๋ฒ์ญํ๋ฉด ์ข์์ง ๊ณ ๋ฏผ์ด ๋์๋ค.
์ง์ญํ๋ฉด '๋ฆฌ์กํธ๋ฅผ ์ํ ๋ ๊ฐ์ ์ปดํจํฐ'์ธ๋ฐ ์ปดํจํฐ๊ฐ ๋ฌด์์ ์ํด ๋ ๋ ์ธ์ง ์ ์ถํ๊ธฐ ์ด๋ ค์, ํ ํฌ์ ๋ด์ฉ์ ์ ๋ด์ ์ ๋ชฉ์ ์๋ ๊ฒ ๊ฐ์๋ค.
๊ทธ๋์ '๋ ๋์ ์ปดํจํฐ๋ก ๋์๊ฐ๋ ๋ฆฌ์กํธ'๋ผ๊ณ ์์ญ์ ํ๋๋ฐ, ๋ฆฌ์กํธ ์ปดํฌ๋ํธ๋ ์๋ฒ์ ์ฝ๋๊ฐ ๋์ํ๊ณ ์๋ ํ๋์ ์ปดํจํฐ (๋ฆฌ์กํธ ์๋ฒ ์ปดํฌ๋ํธ)์, ํด๋ผ์ด์ธํธ์ ์ฝ๋๊ฐ ๋์ํ๊ณ ์๋ ๋ ํ๋์ ์ปดํจํฐ(ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ)๋ก ๋์๊ฐ์ผ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ณด์ฌ์ฃผ๋ ํ๋ก์ธ์ค๊ฐ ์์ฑ๋๊ธฐ ๋๋ฌธ์ด๋ค.
์ฌ๋ฏธ์๋ ํ ํฌ๋ค์ด ์๋ react conf 2024์ ๋ค๋ฅธ ๋ด์ฉ๋ค๋ ์ฐจ์ฐจ ๋ค๋ค๋ณด๊ณ ์ ํ๋ค.
๐ References
https://www.youtube.com/watch?v=ozI4V_29fj4
'๐ฉ๐ปโ๐ป dev' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
polling mechanism์ผ๋ก react ref ํ์ธํ๊ธฐ (0) | 2025.01.22 |
---|---|
puppeteer aws lambda์ ๋ฐฐํฌํ๊ธฐ (0) | 2024.11.02 |
[TOSS] useFunnel ๋ง๋ค์ด๋ณด๊ธฐ (1) | 2024.09.24 |
[โ๏ธ React Conf 2024] ๋ฆฌ์กํธ 19์์ ์๋ก์์ง ๊ฒ๋ค (What's new in React 19 | Lydia Hallie) (1) | 2024.09.24 |
[โ๏ธ react] synthetic event (ft. +17 ๋นํฌ&์ ํํฐ) (0) | 2024.08.16 |