React Server Component๋?
์๋ฒ ์ปดํฌ๋ํธ(Server Components)๋ ์๋ก์ด ์ ํ์ ์ปดํฌ๋ํธ๋ก, ๋ฒ๋ค๋ง ์ ์ ๋ฏธ๋ฆฌ ๋ ๋๋ง๋๋ฉฐ, ํด๋ผ์ด์ธํธ ์ ํ๋ฆฌ์ผ์ด์ ์ด๋ SSR ์๋ฒ์๋ ๋ณ๋์ ํ๊ฒฝ์์ ์คํ๋ฉ๋๋ค.์ด ๋ณ๋์ ํ๊ฒฝ์ด React ์๋ฒ ์ปดํฌ๋ํธ์์ ๋งํ๋ "์๋ฒ"์ ๋๋ค.
์๋ฒ ์ปดํฌ๋ํธ๋ ๋น๋ ์ CI ์๋ฒ์์ ํ ๋ฒ ์คํ๋๊ฑฐ๋, ๊ฐ ์์ฒญ๋ง๋ค ์น ์๋ฒ์์ ์คํ๋ ์ ์์ต๋๋ค.
์ฌ๊ธฐ์ CI ์๋ฒ๋?
CI ์๋ฒ๋ Continuous Integration(์ง์์ ํตํฉ)์ ์ฝ์๋ก, ์ํํธ์จ์ด ๊ฐ๋ฐ์์ ์ฝ๋๋ฅผ ์๋์ผ๋ก ๋น๋ํ๊ณ ํ ์คํธํ๊ธฐ ์ํ ์๋ฒ๋ฅผ ๋งํฉ๋๋ค. CI ์๋ฒ๋ ๊ฐ๋ฐ์๊ฐ ์ฝ๋๋ฅผ ๋ณ๊ฒฝํ๊ณ ์ด๋ฅผ ์ ์ฅ์์ ํธ์ํ ๋, ๋ณ๊ฒฝ๋ ์ฝ๋๋ฅผ ์๋์ผ๋ก
- ๋น๋ (์ฝ๋๋ฅผ ์ปดํ์ผํ๊ฑฐ๋ ๋ฒ๋ค๋ง)
- ํ ์คํธ (์ ๋ ํ ์คํธ, ํตํฉ ํ ์คํธ ๋ฑ ์คํ)
- ๋ฐฐํฌ ์ค๋น
๊ฐ์ ์์ ์ ์ํํ์ฌ ์ฝ๋ ํ์ง์ ์ ์งํ๊ณ ๋ฌธ์ ๋ฅผ ์กฐ๊ธฐ์ ๋ฐ๊ฒฌํ ์ ์๋๋ก ๋์์ค๋๋ค.
React Server Components์ ๊ฒฝ์ฐ, CI ์๋ฒ์์ ๋น๋ ํ์์ ์๋ฒ ์ปดํฌ๋ํธ๋ฅผ ์คํํด HTML์ ๋ฏธ๋ฆฌ ์์ฑํ๊ฑฐ๋, ์ ์ ํ์ผ์ ์์ฑํ๋ ์์ ์ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค. ์ด๋ ์ฝ๋๊ฐ ์ฌ์ฉ์ ์์ฒญ์ ๋ฐ๋ผ ์ค์๊ฐ์ผ๋ก ์คํ๋๋ ๊ฒ์ด ์๋๋ผ ๋น๋ ๋จ๊ณ์์ ๋ฏธ๋ฆฌ ์ฒ๋ฆฌ๋๋ค๋ ์ ์์ ํจ์จ์ ์ ๋๋ค.
∴ CI ์๋ฒ๋ ๊ฐ๋ฐ ์๋ํ ๋๊ตฌ๋ก, React ์๋ฒ ์ปดํฌ๋ํธ๋ฅผ ๋น๋ ์ ๋ฏธ๋ฆฌ ๋ ๋๋งํ๋ ๋ฐ ์ฌ์ฉ๋ ์ ์๋ ํ๊ฒฝ์ ๋๋ค.
๊ณต์ ๋ฌธ์์์๋ ์๋ 4๊ฐ์ง ์ฑํฐ๋ฅผ ํตํด React Server Component๋ฅผ ์ค๋ช ํฉ๋๋ค.
- ์น ์๋ฒ ์๋ ์๋ฒ ์ปดํฌ๋ํธ
- ์น ์๋ฒ์์ ์คํ๋๋ ์๋ฒ ์ปดํฌ๋ํธ
- ์๋ฒ ์ปดํฌ๋ํธ์ ์ํธ์์ฉ ์ถ๊ฐํ๊ธฐ
- ์๋ฒ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ ๋น๋๊ธฐ ์ปดํฌ๋ํธ
๋ด์ฉ์ด ๋๋ฌด ๊ธธ๊ณ ๋ณต์กํ๊ธฐ ๋๋ฌธ์, ์์ธํ ์ค๋ช ํ๊ธฐ ์ ๋จผ์ ํต์ฌ๋ง ์์ฝํด๋ณด๊ฒ ์ต๋๋ค.
- ์๋ฒ์์ ๋ ๋๋ง
- ์๋ฒ ์ปดํฌ๋ํธ๋ ๋ธ๋ผ์ฐ์ ๋ก ์ ์ก๋์ง ์๊ณ , ์๋ฒ์์๋ง ๋ ๋๋ง๋ฉ๋๋ค.
- ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๊ฑฐ๋ ํ์ผ ์์คํ ์ ์ ๊ทผ ๊ฐ๋ฅํ๋ฉฐ, ํด๋ผ์ด์ธํธ๋ก๋ HTML๋ง ์ ์ก๋ฉ๋๋ค.
- ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์ ์กฐํฉ
- ์ํธ์์ฉ์ด ํ์ํ ๊ฒฝ์ฐ, "use client" ์ง์์ด๋ก ์ ์๋ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์ ์กฐํฉํ์ฌ ์ฌ์ฉํฉ๋๋ค.
- ์๋ฒ ์ปดํฌ๋ํธ๋ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๋ก ๋ฐ์ดํฐ๋ฅผ props๋ก ์ ๋ฌํฉ๋๋ค.
- ๋น๋๊ธฐ ์ง์
- async/await ์ ์ฌ์ฉํ์ฌ ๋ฐ์ดํฐ๋ฅผ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
- ์ค์ํ ๋ฐ์ดํฐ๋ ์๋ฒ์์ awaitํ๊ณ , ๋ ์ค์ํ ๋ฐ์ดํฐ๋ ํด๋ผ์ด์ธํธ์์ use API๋ก ๊ธฐ๋ค๋ฆฝ๋๋ค.
- ์ฑ๋ฅ ์ต์ ํ
- ์๋ฒ์์ ๋ฐ์ดํฐ ์ฒ๋ฆฌ์ ๋ ๋๋ง์ ์ํํด ํด๋ผ์ด์ธํธ ๋ฒ๋ค ํฌ๊ธฐ๋ฅผ ์ค์ด๊ณ , ์ด๊ธฐ ๋ก๋ฉ ์๋๋ฅผ ๊ฐ์ ํฉ๋๋ค.
- ์คํธ๋ฆฌ๋ฐ๊ณผ Suspense๋ฅผ ํตํด ์ค์ํ ์ฝํ ์ธ ๋ฅผ ์ฐ์ ์ ์ผ๋ก ๋ ๋๋งํ๊ณ , ๋๋จธ์ง ๋ฐ์ดํฐ๋ฅผ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํฉ๋๋ค.
- ์ ์ฐํ ์ํคํ
์ฒ
- ์๋ฒ ์ค์ฌ ๋ฉํฐ ํ์ด์ง ์ฑ(MPA)๊ณผ ํด๋ผ์ด์ธํธ ์ค์ฌ ์ฑ๊ธ ํ์ด์ง ์ฑ(SPA)์ ์ฅ์ ์ ๊ฒฐํฉํ ์ํคํ ์ฒ๋ก, ๋์ ์ด๊ณ ์ํธ์์ฉ์ ์ธ ์ฑ ๊ตฌํ์ด ๊ฐ๋ฅํฉ๋๋ค.
์น ์๋ฒ ์๋ ์๋ฒ ์ปดํฌ๋ํธ
์๋ฒ ์ปดํฌ๋ํธ๋ ๋น๋ ์ ํ์ผ ์์คํ ์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ฑฐ๋ ์ ์ ์ฝํ ์ธ ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์ผ๋ฏ๋ก ์น ์๋ฒ๊ฐ ํ์ํ์ง ์์ต๋๋ค.
์๋ฅผ ๋ค์ด, ์ฝํ ์ธ ๊ด๋ฆฌ ์์คํ (CMS)์์ ์ ์ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ณ ์ถ์ ๋ ์ฌ์ฉํ ์ ์์ต๋๋ค.
์๋ฒ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ์ง ์์ ๊ฒฝ์ฐ, ์ผ๋ฐ์ ์ผ๋ก ํด๋ผ์ด์ธํธ์์ Effect๋ฅผ ์ฌ์ฉํด ์ ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ต๋๋ค:
// bundle.js
import marked from 'marked'; // 35.9K (11.2K gzipped)
import sanitizeHtml from 'sanitize-html'; // 206K (63.3K gzipped)
function Page({page}) {
const [content, setContent] = useState('');
// ์ฒซ ํ์ด์ง ๋ ๋๋ง ์ดํ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
useEffect(() => {
fetch(`/api/content/${page}`).then((data) => {
setContent(data.content);
});
}, [page]);
return <div>{sanitizeHtml(marked(content))}</div>;
}
// api.js
app.get(`/api/content/:page`, async (req, res) => {
const page = req.params.page;
const content = await file.readFile(`${page}.md`);
res.send({content});
});
์ด ํจํด์์๋ ์ ์ ์ฝํ ์ธ ๋ฅผ ๋ ๋๋งํ๊ธฐ ์ํด ์ฌ์ฉ์๊ฐ ์ถ๊ฐ๋ก 75KB(gzipped)์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ค์ด๋ก๋ํ๊ณ ํ์ฑํด์ผ ํ๊ณ ,
ํ์ด์ง๊ฐ ๋ก๋๋ ํ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์ํ ๋ ๋ฒ์งธ ์์ฒญ์ ๊ธฐ๋ค๋ ค์ผ ํฉ๋๋ค.
์ด ์ฝํ ์ธ ๋ ํ์ด์ง์ ์๋ช ๋์ ๋ณ๊ฒฝ๋์ง ์๋ ์ ์ ์ฝํ ์ธ ์ ๋๋ค.
์๋ฒ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋ฉด ๋น๋ ์ ํ ๋ฒ๋ง ์ด๋ฌํ ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ ์ ์์ต๋๋ค.
import marked from 'marked'; // ๋ฒ๋ค์ ํฌํจ๋์ง ์์
import sanitizeHtml from 'sanitize-html'; // ๋ฒ๋ค์ ํฌํจ๋์ง ์์
async function Page({page}) {
// ์ฑ์ด ๋น๋๋ ๋ ๋ ๋๋ง ์ค ๋ฐ์ดํฐ๋ฅผ ๋ก๋ํฉ๋๋ค.
const content = await file.readFile(`${page}.md`);
return <div>{sanitizeHtml(marked(content))}</div>;
}
๋ ๋๋ง๋ ์ถ๋ ฅ์ ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง(SSR)์ ํตํด HTML๋ก ๋ณํ๋์ด CDN์ ์ ๋ก๋๋ฉ๋๋ค.
์ฑ์ด ๋ก๋๋ ๋, ํด๋ผ์ด์ธํธ๋ ์๋์ Page ์ปดํฌ๋ํธ๋ ๋งํฌ๋ค์ด์ ๋ ๋๋งํ๊ธฐ ์ํ ๋ฌด๊ฑฐ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํฌํจํ์ง ์๊ณ , ๋ ๋๋ง๋ ์ถ๋ ฅ๋ง ๋ณด๊ฒ ๋ฉ๋๋ค.
<div><!-- ๋งํฌ๋ค์ด์ HTML ์ถ๋ ฅ --></div>
๋ฐ๋ผ์ ์๋ฒ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ฉด ์๋์ ๊ฐ์ ์ด์ ์ด ์์ต๋๋ค.
- ์ฒซ ํ์ด์ง ๋ก๋ ์ ์ฝํ ์ธ ๊ฐ ๋ฐ๋ก ํ์๋ฉ๋๋ค.
- ์ ์ ์ฝํ ์ธ ๋ฅผ ๋ ๋๋งํ๋ ๋ฐ ํ์ํ ๋ฌด๊ฑฐ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ด ๋ฒ๋ค์ ํฌํจ๋์ง ์์ต๋๋ค.
์น ์๋ฒ์์ ์คํ๋๋ ์๋ฒ ์ปดํฌ๋ํธ
์๋ฒ ์ปดํฌ๋ํธ๋ ํ์ด์ง ์์ฒญ ์ ์น ์๋ฒ์์ ์คํ๋ ์๋ ์์ต๋๋ค.
์ด๋ฅผ ํตํด API๋ฅผ ๋ฐ๋ก ๊ตฌ์ถํ์ง ์๊ณ ๋ฐ์ดํฐ ๊ณ์ธต์ ์ง์ ์ก์ธ์คํ ์ ์์ต๋๋ค.
์๋ฒ ์ปดํฌ๋ํธ๋ ์ ํ๋ฆฌ์ผ์ด์ ์ด ๋ฒ๋ค๋ง๋๊ธฐ ์ ์ ๋ ๋๋ง๋๋ฉฐ, ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๋ก ๋ฐ์ดํฐ์ JSX๋ฅผ props๋ก ์ ๋ฌํ ์ ์์ต๋๋ค.
์๋ฒ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ์ง ์๋ ๊ฒฝ์ฐ, ์ผ๋ฐ์ ์ผ๋ก ํด๋ผ์ด์ธํธ์์ Effect๋ฅผ ์ฌ์ฉํด ๋์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ต๋๋ค:
// bundle.js
function Note({id}) {
const [note, setNote] = useState('');
// ์ฒซ ๋ ๋๋ง ํ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
useEffect(() => {
fetch(`/api/notes/${id}`).then(data => {
setNote(data.note);
});
}, [id]);
return (
<div>
<Author id={note.authorId} />
<p>{note}</p>
</div>
);
}
function Author({id}) {
const [author, setAuthor] = useState('');
// Note๊ฐ ๋ ๋๋ง๋ ํ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
// ํด๋ผ์ด์ธํธ-์๋ฒ ๊ฐ์ ๋๋ฆฐ ์ํฐํด(waterfall) ํธ์ถ์ ์ ๋ฐํฉ๋๋ค.
useEffect(() => {
fetch(`/api/authors/${id}`).then(data => {
setAuthor(data.author);
});
}, [id]);
return <span>By: {author.name}</span>;
}
// api
import db from './database';
app.get(`/api/notes/:id`, async (req, res) => {
const note = await db.notes.get(id);
res.send({note});
});
app.get(`/api/authors/:id`, async (req, res) => {
const author = await db.authors.get(id);
res.send({author});
});
์๋ฒ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ๋ฉด, ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ณ ์ปดํฌ๋ํธ ๋ด์์ ๋ ๋๋งํ ์ ์์ต๋๋ค.
import db from './database';
async function Note({id}) {
// ๋ ๋๋ง ์ค ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
const note = await db.notes.get(id);
return (
<div>
<Author id={note.authorId} />
<p>{note}</p>
</div>
);
}
async function Author({id}) {
// Note ์ดํ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
// ํ์ง๋ง ๋ฐ์ดํฐ๊ฐ ๊ทผ์ฒ์ ์์ผ๋ฉด ๋น ๋ฅด๊ฒ ์ฒ๋ฆฌ๋ฉ๋๋ค.
const author = await db.authors.get(id);
return <span>By: {author.name}</span>;
}
๋ฒ๋ค๋ฌ๋ ๋ฐ์ดํฐ๋ฅผ ๊ฒฐํฉํ๊ณ , ๋ ๋๋ง๋ ์๋ฒ ์ปดํฌ๋ํธ์ ๋์ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๋ฅผ ํจ๊ป ๋ฒ๋ค๋ก ๋ฌถ์ต๋๋ค.
์ ํ์ ์ผ๋ก ์ด ๋ฒ๋ค์ ์๋ฒ ์ฌ์ด๋ ๋ ๋๋ง(SSR) ๋์ด ํ์ด์ง์ ์ด๊ธฐ HTML์ ์์ฑํ ์ ์์ต๋๋ค.
ํ์ด์ง๊ฐ ๋ก๋๋ ๋ ๋ธ๋ผ์ฐ์ ๋ ์๋์ Note์ Author ์ปดํฌ๋ํธ๋ฅผ ๋ณด์ง ์๊ณ , ๋ ๋๋ง๋ ์ถ๋ ฅ๋ง ํด๋ผ์ด์ธํธ๋ก ์ ๋ฌ๋ฉ๋๋ค.
<div>
<span>By: The React Team</span>
<p>React 19 is...</p>
</div>
์๋ฒ ์ปดํฌ๋ํธ๋ ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๋ค์ ๊ฐ์ ธ์ ๋์ ์ผ๋ก ๋ ๋๋ง๋ ์ ์์ต๋๋ค.
์ด ์๋ก์ด ์ ํ๋ฆฌ์ผ์ด์
์ํคํ
์ฒ๋ ์๋ฒ ์ค์ฌ ๋ค์ค ํ์ด์ง ์ฑ(Multi-Page Apps)์ ๊ฐ๋จํ "์์ฒญ/์๋ต" ๋ชจ๋ธ๊ณผ ํด๋ผ์ด์ธํธ ์ค์ฌ ๋จ์ผ ํ์ด์ง ์ฑ(Single-Page Apps)์ ๋งค๋๋ฌ์ด ์ํธ์์ฉ์ ๊ฒฐํฉํ์ฌ ๋ ๋ ๋๋ง ๋ฐฉ์์ ์ฅ์ ์ ๋ชจ๋ ์ ๊ณตํฉ๋๋ค.
์๋ฒ ์ปดํฌ๋ํธ์ ์ํธ์์ฉ ์ถ๊ฐํ๊ธฐ
์๋ฒ ์ปดํฌ๋ํธ๋ ๋ธ๋ผ์ฐ์ ๋ก ์ ์ก๋์ง ์๊ธฐ ๋๋ฌธ์, useState์ ๊ฐ์ ์ํธ์์ฉ API๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
์๋ฒ ์ปดํฌ๋ํธ์ ์ํธ์์ฉ์ ์ถ๊ฐํ๋ ค๋ฉด "use client" ์ง์์ด๋ฅผ ์ฌ์ฉํ์ฌ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์ ์กฐํฉํด์ผ ํฉ๋๋ค.
์๋ฒ ์ปดํฌ๋ํธ์๋ ๋ณ๋์ ์ง์์ด๊ฐ ์์ต๋๋ค.
"use server" ์ง์์ด๋ ์๋ฒ ์ปดํฌ๋ํธ๋ฅผ ๋ํ๋ด๋ ๊ฒ์ด ์๋๋ผ ์๋ฒ ํจ์(Server Functions) ์ ์ฌ์ฉ๋ฉ๋๋ค.
์๋ฒ ์ปดํฌ๋ํธ์ ๋ํ ๋ณ๋ ์ง์์ด๋ ์์ผ๋ฏ๋ก ํผ๋ํ์ง ์๋๋ก ์ฃผ์ํ์ธ์.
์์ธํ ๋ด์ฉ์ Directives ๋ฌธ์์์ ํ์ธํ ์ ์์ต๋๋ค.
๋ค์ ์์ ์์๋ Notes ์๋ฒ ์ปดํฌ๋ํธ๊ฐ ์ํ๋ฅผ ์ฌ์ฉํด ํ์ฅ ์ฌ๋ถ๋ฅผ toggleํ๋ Expandable ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
์๋ฒ ์ปดํฌ๋ํธ
import Expandable from './Expandable';
async function Notes() {
const notes = await db.notes.getAll();
return (
<div>
{notes.map(note => (
<Expandable key={note.id}>
<p note={note} />
</Expandable>
))}
</div>
);
}
ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ
"use client";
export default function Expandable({children}) {
const [expanded, setExpanded] = useState(false);
return (
<div>
<button onClick={() => setExpanded(!expanded)}>
Toggle
</button>
{expanded && children}
</div>
);
}
์๋ ๋ฐฉ์
- Notes ์ปดํฌ๋ํธ๋ ์๋ฒ ์ปดํฌ๋ํธ๋ก ๋จผ์ ๋ ๋๋ง๋ฉ๋๋ค.
- ๋ฒ๋ค๋ฌ๋ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์ธ Expandable์ ์ํ ๋ฒ๋ค์ ์์ฑํฉ๋๋ค.
๋ธ๋ผ์ฐ์ ์์ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๋ ์๋ฒ ์ปดํฌ๋ํธ๋ก๋ถํฐ ์ ๋ฌ๋ ์ถ๋ ฅ(props)์ ๋ณด๊ฒ ๋ฉ๋๋ค.
<head>
<!-- ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๋ฅผ ์ํ ๋ฒ๋ค -->
<script src="bundle.js"></script>
</head>
<body>
<div>
<Expandable key={1}>
<p>this is the first note</p>
</Expandable>
<Expandable key={2}>
<p>this is the second note</p>
</Expandable>
<!--...-->
</div>
</body>
์ด ๊ตฌ์กฐ๋ฅผ ํตํด ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๋ ๋๋งํ ๋ค์, ํด๋ผ์ด์ธํธ ์ธก์์ ๋์ ์ํธ์์ฉ์ ์ถ๊ฐํ ์ ์์ต๋๋ค.
์๋ฒ ์ปดํฌ๋ํธ์ ๋น๋๊ธฐ ์ปดํฌ๋ํธ
์๋ฒ ์ปดํฌ๋ํธ๋ async/await ์ ์ฌ์ฉํ๋ ์๋ก์ด ๋ฐฉ์์ ์ปดํฌ๋ํธ ์์ฑ๋ฒ์ ์ ๊ณตํฉ๋๋ค.
๋น๋๊ธฐ ์ปดํฌ๋ํธ์์ await๋ฅผ ์ฌ์ฉํ๋ฉด React๋ ์ผ์ ์ค๋จ(suspend)ํ๊ณ , ํ๋ก๋ฏธ์ค๊ฐ ํด๊ฒฐ๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฐ ๋ค ๋ ๋๋ง์ ์ฌ๊ฐํฉ๋๋ค.
์ด ๋ฐฉ์์ ์๋ฒ์ ํด๋ผ์ด์ธํธ ๊ฒฝ๊ณ๋ฅผ ๋๋๋ค๋ฉฐ Suspense๋ฅผ ์ํ ์คํธ๋ฆฌ๋ฐ ์ง์๊ณผ ํจ๊ป ์๋ํฉ๋๋ค.
์ฌ์ง์ด ์๋ฒ์์ ํ๋ก๋ฏธ์ค๋ฅผ ์์ฑํ ๋ค, ํด๋ผ์ด์ธํธ์์ ์ด๋ฅผ awaitํ ์๋ ์์ต๋๋ค.
์๋ฒ ์ปดํฌ๋ํธ
import db from './database';
async function Page({id}) {
// ์๋ฒ ์ปดํฌ๋ํธ๋ฅผ ์ผ์ ์ค๋จํฉ๋๋ค.
const note = await db.notes.get(id);
// ์ฌ๊ธฐ์๋ ๋๊ธฐํ์ง ์์ต๋๋ค. ํ๋ก๋ฏธ์ค๋ ์๋ฒ์์ ์์๋๊ณ ,
// ํด๋ผ์ด์ธํธ์์ ๊ธฐ๋ค๋ฆฝ๋๋ค.
const commentsPromise = db.comments.get(note.id);
return (
<div>
{note}
<Suspense fallback={<p>Loading Comments...</p>}>
<Comments commentsPromise={commentsPromise} />
</Suspense>
</div>
);
}
ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ
"use client";
import {use} from 'react';
function Comments({commentsPromise}) {
// ์๋ฒ์์ ์์๋ ํ๋ก๋ฏธ์ค๋ฅผ ์ด์ด๋ฐ์ต๋๋ค.
// ๋ฐ์ดํฐ๊ฐ ์ค๋น๋ ๋๊น์ง ์ผ์ ์ค๋จ(suspend)๋ฉ๋๋ค.
const comments = use(commentsPromise);
return comments.map(comment => <p>{comment}</p>);
}
์๋ ๋ฐฉ์
- note ์ฝํ ์ธ ๋ ํ์ด์ง ๋ ๋๋ง์ ์ค์ํ ๋ฐ์ดํฐ์ด๋ฏ๋ก, ์๋ฒ์์ ์ด๋ฅผ await ํฉ๋๋ค.
- ๋๊ธ์ ํ์ด์ง ํ๋จ ์ฝํ ์ธ ๋ก ์ฐ์ ์์๊ฐ ๋ฎ๊ธฐ ๋๋ฌธ์, ์๋ฒ์์ ํ๋ก๋ฏธ์ค๋ฅผ ์์ํ๊ณ ํด๋ผ์ด์ธํธ์์ use API๋ฅผ ์ฌ์ฉํด ๊ธฐ๋ค๋ฆฝ๋๋ค.
ํด๋ผ์ด์ธํธ์์ ์ด ์์ ์ ์ผ์ ์ค๋จ(suspend)๋์ง๋ง, ํ์ด์ง์ note ์ฝํ ์ธ ๋ ๋๋ง์ ๋ง์ง๋ ์์ต๋๋ค.
ํด๋ผ์ด์ธํธ์์ ๋น๋๊ธฐ ์ปดํฌ๋ํธ ์ง์
- ํด๋ผ์ด์ธํธ์์๋ ๋น๋๊ธฐ ์ปดํฌ๋ํธ๋ฅผ ์ง์ ์ง์ํ์ง ์๊ธฐ ๋๋ฌธ์, ํด๋ผ์ด์ธํธ์์ ํ๋ก๋ฏธ์ค๋ฅผ ๊ธฐ๋ค๋ฆด ๋๋ use API๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค.
๊ณต์ ๋ฌธ์๋ฅผ ๋ฒ์ญํ๊ณ ์ ๋ฆฌํ๋ฉด์ Server Component๋ฅผ ์ฌ์ฉํ๋ฉด ์ด๋ค ์ด์ ์ด ์๋์ง ํ์ ํ ์ ์์์ต๋๋ค.
์ถ๊ฐ๋ก ์๋ฒ ์ปดํฌ๋ํธ๋ฅผ ์ฌ์ฉํ ๋ ์ด๋ค ์ฅ์ ์ ์ ๊ณตํ๋์ง ๋ค๋ฅธ ๊ด์ ์์ ์ค๋ช ํ๋ ๊ธ์ ์์ฝํด๋ณด๊ฒ ์ต๋๋ค.
1. ๋ฐ์ดํฐ ํ์นญ ๋ฐ ๋ฐ์ดํฐ ์ฒ๋ฆฌ์ ์ฉ์ด์ฑ
React ์ปดํฌ๋ํธ์์ ๋ฐ์ดํฐ ํ์นญ์ ํ๋ ค๋ฉด ๋ณดํต ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ด ์์ต๋๋ค:
- ์ธ๋ถ API์ ์ง์ ์์ฒญํ๊ธฐ
- ์๋ฒ์ ๋ด๋ถ API ์๋ํฌ์ธํธ๋ฅผ ์์ฑํ ํ ํด๋น ์๋ํฌ์ธํธ์ ์์ฒญํ๊ธฐ
ํ์ง๋ง ์ด ๋ ๋ฐฉ๋ฒ์ ๊ฐ๊ฐ ๋จ์ ์ด ์์ต๋๋ค:
- ์ธ๋ถ API์ ์ง์ ์์ฒญ ์:
- CORS ๋ฌธ์ ๋ฐ์ ๊ฐ๋ฅ
- ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๊ฐ์ ๋ฐ์ดํฐ ์์ค์๋ ์ ๊ทผํ ์ ์์
- ๋ฐ์ดํฐ ์์ฒญ ๋ก์ง์ด ํด๋ผ์ด์ธํธ์ ๋ ธ์ถ๋ ์ ์์
- UI ์ปดํฌ๋ํธ์ ์๋ฌ ์ฒ๋ฆฌ ๋ก์ง์ด ๊ฒฐํฉ๋จ
- ๋ด๋ถ API ์๋ํฌ์ธํธ ์ฌ์ฉ ์:
- ๋ณด์ ๋ฌธ์ ๋ ํด๊ฒฐ๋์ง๋ง, ์ถ๊ฐ์ ์ธ ์๋ฒ ์ฝ๋๋ฅผ ์์ฑํด์ผ ํจ
React Server Component์ ์ฅ์
React Server Component(RSC)๋ ์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํด์ค๋๋ค. ์๋ฅผ ๋ค์ด:
async function getUserData() {
// ๋ฐ์ดํฐ ํ์นญ ๋ก์ง ์์ฑ
}
async function MyComponent() {
let data = await getUserData();
if (!data) {
return <div className="error-msg">User not found!</div>;
}
return (
<div>
{data.username}
<DeleteUserButton id={data.id} />
</div>
);
}
์ด ์์ ์ ํน์ง์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
- ํด๋ผ์ด์ธํธ ์ฌ์ด๋ ์ฝ๋๋ ๋ธ๋ผ์ฐ์ API, ์ด๋ฒคํธ ํธ๋ค๋ฌ ๋ฑ์ ์ฌ์ฉํ์ง ์์.
- ์ํ ๊ด๋ฆฌ๋ ์ฌ์ด๋ ์ดํํธ๊ฐ ์์ผ๋ฉฐ, ๋จ์ํ ๋ฐ์ดํฐ๋ฅผ ์กฐํํ๊ณ ๋ ๋๋ง๋ง ํจ.
- ๊ฒฐ๊ณผ๊ฐ ์์ ๋๋ ๋ค๋ฅธ HTML์ ๋ฐํ.
- DeleteUserButton๊ณผ ๊ฐ์ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์๋ ์์ฐ์ค๋ฝ๊ฒ ๊ฒฐํฉ ๊ฐ๋ฅ.
RSC์ ์ฃผ์ ์ด์
- ๋ด๋ถ API ์๋ํฌ์ธํธ๋ฅผ ๋ณ๋๋ก ์์ฑํ์ง ์์๋ ๋จ.
- ๋ฐ์ดํฐ ์์ค์ ์๋ฌ ์ฒ๋ฆฌ๋ฅผ UI ์ปดํฌ๋ํธ์ ๊ฒฐํฉํ์ง ์์.
- CORS ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ง ์์.
- ๋ฐ์ดํฐ๋ฒ ์ด์ค, ์ธ๋ถ API, ๋ก์ปฌ ํ์ผ ๋ฑ ๋ชจ๋ ๋ฐ์ดํฐ ์์ค์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์.
๋ฐ๋ผ์, RSC๋ ๋ฐ์ดํฐ ํ์นญ์ ๋ ๊ฐ๋จํ๊ณ ํจ์จ์ ์ผ๋ก ํ ์ ์๋ ๋ฐฉ๋ฒ์ ์ ๊ณตํฉ๋๋ค.
2. ๋ ๋น ๋ฅธ UI์ ๊ฐ์ ๋ UX
- ์๋ฒ์์ ๋ ๋๋ง๋๊ธฐ ๋๋ฌธ์, ๋ธ๋ผ์ฐ์ ๊ฐ ํด๋น DOM์ ๊ทธ๋ฆด ๋ ๋ณ๋์ ์๋ฐ์คํฌ๋ฆฝํธ ์ฒ๋ฆฌ๊ฐ ํ์ ์์.
- ๋ธ๋ผ์ฐ์ ๋ ์๋ฒ๋ก๋ถํฐ ์ ๋ฌ๋ฐ์ HTML์ DOM์ ์ถ๊ฐํ๊ธฐ๋ง ํ๋ฉด ๋จ.
- ํด๋ผ์ด์ธํธ ์ธก์์ ์๋ฐ์คํฌ๋ฆฝํธ๋ก HTML์ ๋์ ์ผ๋ก ์์ฑํ๊ณ DOM์ ์ ๋ฐ์ดํธํ๋ ์์ ์ด ์๊ธฐ ๋๋ฌธ์, ์ฑ๋ฅ์ด ํฅ์๋จ.
- ํนํ ๋ณต์กํ ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ง์ ์ปดํฌ๋ํธ๋ฅผ ์๋ฒ ์ปดํฌ๋ํธ๋ก ๋ฐ๊พธ๋ฉด ๋ ๋ง์ ์ฒ๋ฆฌ ์์ ์ ์๋ฒ๋ก ๋๊ฒจ, ํด๋ผ์ด์ธํธ์ ๋ถํ๋ฅผ ์ค์ผ ์ ์์.
- ๋ธ๋ผ์ฐ์ ๊ฐ ์ฒ๋ฆฌํด์ผ ํ๋ ์์ ์ด ์ค์ด UI๊ฐ ๋ ๋นจ๋ฆฌ ๋ก๋๋๋ฉฐ, ์ฌ์ฉ์์๊ฒ ๋น ๋ฅธ ์ฒซ ํ๋ฉด ๋ก๋๋ฅผ ์ ๊ณตํ์ฌ UX ๊ฐ์ ์ ๊ธฐ์ฌ.
3. ๋ ๊ฐ๋จํ UI ์ฝ๋
- RSC๋ฅผ ์ฌ์ฉํ๋ฉด ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๊ฐ UI ๋น์ฆ๋์ค ๋ก์ง์ ์ ๋ถ ๊ด๋ฆฌํ ํ์๊ฐ ์์.
- ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์์ ๋ฐ์ดํฐ ํ์นญ ๋ฐ ๋ฐ์ดํฐ ์ฒ๋ฆฌ๋ฅผ ์๋ฒ ์ปดํฌ๋ํธ๋ก ์ด๋ ๊ฐ๋ฅ.
- ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์ ํฌ๊ธฐ๊ฐ ์ค์ด๋ค์ด ๋ฒ๋ค ํฌ๊ธฐ๊ฐ ๊ฐ์, ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ก๋ฉ ์๊ฐ์ด ๋จ์ถ๋จ.
- ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๋ UI ๋ ๋๋ง์๋ง ์ง์คํ๊ณ , ๋น UI ๊ด๋ จ ๋ก์ง์ RSC๋ก ๋ถ๋ฆฌ ๊ฐ๋ฅ.
- ๋์ ๋์์ด ํ์ ์๋ ์ ์ UI ์์๋ค(์: ํธํฐ)๋ ์๋ฒ์์ ๋ ๋๋งํ์ฌ ํด๋ผ์ด์ธํธ ์ฝ๋๋ฒ ์ด์ค๋ฅผ ์ค์ผ ์ ์์.
- references:
Server Components – React
The library for web and native user interfaces
react.dev
It’s 2024, you should be using React Server Components already
3 reasons why you should actually use RSC in all your projects
itnext.io
'Front-end' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| useState์ ๋น๋๊ธฐ์ฑ (0) | 2025.04.13 |
|---|---|
| React key prop๊ณผ ์ํ ์ด๊ธฐํ (0) | 2025.02.23 |
| Timer Interval ์ง์ฐ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํ ๋ฐฉ๋ฒ (2) | 2025.02.02 |
| ์น ํ์ค๊ณผ ์น ์ ๊ทผ์ฑ (0) | 2023.11.29 |
| ๋ชจ๋ ๋ฒ๋ค๋ฌ์ Webpack (1) | 2023.11.23 |