
์ง๊ธ ์ค๋ฒจํธ(Svelte)๋ฅผ ๋ฐฐ์์ผ ํ๋ ์ด์
์ง๊ธ ์ค๋ฒจํธ(Svelte)๋ฅผ ๋ฐฐ์์ผ ํ๋ ์ด์ ๊ด๋ จ
๋น์ ์ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์์ธ๊ฐ์, ๋ฆฌ์กํธ์๋ ๊ฐ๋ฐ์์ธ๊ฐ์?
"๋ฆฌ์กํธ ๊ฒฝํ 3๋ ์ด์", "๋ฆฌ์กํธ ๋ค์ดํฐ๋ธ ๊ฐ๋ฐ์ ๊ตฌํจ", "๋ฆฌ์กํธ ํ ๋ง์คํฐ ํด๋์ค"... ์์ฆ ํ๋ก ํธ์๋ ์ฑ์ฉ๊ณต๊ณ ์ ๊ต์ก๊ณผ์ ์ ๋ณด๋ฉด, ํ๋ก ํธ์๋ ๊ฐ๋ฐ๊ณผ ๋ฆฌ์กํธ ๊ฐ๋ฐ์ด ๊ฑฐ์ ๋์์ด์ฒ๋ผ ์ฌ์ฉ๋๊ณ ์์ต๋๋ค. ๊ทธ๋ฐ๋ฐ ํ๋ฒ ์๊ฐํด ๋ณผ๊ฒ์. useMemo๋ฅผ ์ธ์ ์จ์ผ ํ ์ง, React.memo๋ก ์ด๋ป๊ฒ ๋ฆฌ๋ ๋๋ง์ ์ต์ ํํ ์ง, useCallback์ ์์กด์ฑ ๋ฐฐ์ด์ ์ด๋ป๊ฒ ๊ด๋ฆฌํด์ผ ํ ์ง ๋ฑ ์ด๋ฐ ๊ฒ๋ค์ ์ ์๋ค๊ณ ํด์ ์ ๋ง "์ํ๋ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์"์ผ๊น์?
๋์ด์ผ ๋ณด๋ฉด 10๋ ์ ์๋ ์ ์ด์ฟผ๋ฆฌ๊ฐ ์น ๊ฐ๋ฐ์ ํ์ค์ด์์ต๋๋ค. "์ ์ด์ฟผ๋ฆฌ๋ฅผ ๋ชจ๋ฅด๋ฉด ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๊ฐ ์๋๋ค"๋ผ๋ ์ธ์์ด ์์์ฃ . ํ์ง๋ง ๋ฆฌ์กํธ๊ฐ ๋ํ๋๋ฉด์ ํจ๋ฌ๋ค์์ด ๋ณํ๊ณ , ์ ์ด์ฟผ๋ฆฌ๋ง ๊ณ ์งํ๋ ๊ฐ๋ฐ์๋ค์ ์๋ก์ด ๊ธฐ์ ์ ์ ์ํ์ง ๋ชปํ์ต๋๋ค. ์ญ์ฌ๋ ๋ฐ๋ณต๋ฉ๋๋ค. ์ค๋์ ๋ฆฌ์กํธ๋ ๋ด์ผ์ ์ ์ด์ฟผ๋ฆฌ๊ฐ ๋ ์๋ ์์ต๋๋ค.
ํ๋ก ํธ์๋ ์ญ๋์ ํค์ฐ๋ ๋ค์ํ ์ ๊ทผ๋ฒ
๋ง์ ๊ฐ๋ฐ์๋ค์ด Feature-Sliced Design(FSD)์ ๊ฐ์ ์ํคํ ์ฒ ํจํด์ด๋, ์ต์ ์ ํด๋ ๊ตฌ์กฐ๋ฅผ ๊ณ ๋ฏผํฉ๋๋ค. ์ด๋ ์ปค๋ฆฌ์ด์ ๊ธฐ์ ์ญ๋์ ์ฆ์ง์ํค๊ธฐ ์ํ ๋ ธ๋ ฅ์ด์ง๋ง, ์กฐ๊ธ๋ง ๋์ ๋๋ฆฌ๋ฉด ๋ ๊ทผ๋ณธ์ ์ธ ๊ธฐํ๊ฐ ์์ต๋๋ค.
Preact๋ ์ React์ ๋ค๋ฅธ ์ ๊ทผ๋ฒ์ ์ ํํ์๊น์? SolidJS๋ ์ด๋ค ์๋ฆฌ๋ก ๋์ํ๋์? Astro์ ์์ผ๋๋ ์ํคํ ์ฒ๋ React์ ์๋ฒ ์ปดํฌ๋ํธ์ ์ด๋ป๊ฒ ๋ค๋ฅผ๊น์? ๊ทธ๋ฆฌ๊ณ ์์ฆ ๋ง์ ํ๋ ์์ํฌ๊ฐ ์ฑํํ๊ณ ์๋ '์๊ทธ๋ ํจํด'์ ๋ฌด์์ผ๊น์?
์๊ทธ๋ ํจํด์ ์๋ก ๋ค์ด๋ณด๊ฒ ์ต๋๋ค. Vue, Solid, Svelte ๋ฑ ๋ฆฌ์กํธ๋ฅผ ์ ์ธํ ๊ฑฐ์ ๋ชจ๋ ํ๋ ํ๋ ์์ํฌ๋ค์ด ์ด ํจํด์ ์ฑํํ์ต๋๋ค. ์ด ํจํด์ ์ํ ๋ณํ๋ฅผ ์ ํํ ์ถ์ ํ๊ณ ํ์ํ ๋ถ๋ถ๋ง ์ ๋ฐ์ดํธํ์ฌ, ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ ์ ๊ฑฐํฉ๋๋ค. ๋ฆฌ์กํธ์์ useMemo, useCallback, React.memo๋ฅผ ์ฌ์ฉํด, ๊ณ ๊ตฐ๋ถํฌํ๋ ์ต์ ํ ์์ ์ด ๋ค๋ฅธ ํ๋ ์์ํฌ์์๋ ๊ธฐ๋ณธ์ ์ผ๋ก ํด๊ฒฐ๋๋ ๋ฌธ์ ์ธ ๊ฒ์ด์ฃ .
๊ธฐ์ ์ ์ธ๊ณ์์๋ ํ๋ฐ์ฃผ์ ํ๋ ์์ํฌ๋ค์ด ๊ธฐ์ ์ ์ผ๋ก, ๊ฐ๋ฐ์ ๊ฒฝํ(DX)์ ์ผ๋ก ๋ ๋ฐ์ด๋ ํ๋ฅ ์ด ๋์ต๋๋ค. ๊ทธ๋ค์ ์ ํ ๊ธฐ์ ์ ๋ถ๋ง์กฑ์์ ์์ํ๊ธฐ ๋๋ฌธ์ ๋๋ค. 10๋ ์ "์ ์ด์ฟผ๋ฆฌ๋ง ํด๋ ๋ฉ๋๋ค"๋ผ๊ณ ๋งํ๋ ๊ฐ๋ฐ์๋ค์ด ์ค๋๋ ์ด๋์ ์์๊น์? 10๋ ํ "์ ๋ ๋ฆฌ์กํธ๋ง ํฉ๋๋ค"๋ผ๊ณ ๋งํ๋ ๊ฐ๋ฐ์๋ ์ด๋ป๊ฒ ๋ ๊น์?
๊ทธ๋์ ์ด ๊ธฐํ์ ์ค๋ฒจํธ(Svelte)๋ฅผ ๋ฐฐ์ฐ๋ฉด ์ข์ ์ด์
๋ค์ํ ํ๋ ์์ํฌ ๊ฒฝํ์ ๋จ์ํ ๊ธฐ์ ์คํ์ ๋ํ๋ ๊ฒ ์ด์์ ๊ฐ์น๊ฐ ์์ต๋๋ค. ๋ค๋ฅธ ์ ๊ทผ๋ฒ์ ํ๊ตฌํ๋ ๊ณผ์ ์์ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ ๋ณธ์ง์ ๋ฌธ์ ๋ค์ ๋ ๊น์ด ์ดํดํ๊ฒ ๋๊ณ , ์ด๋ ์ฌ๋ฌ๋ถ์ด ์ฃผ๋ก ์ฌ์ฉํ๋ ๋ฆฌ์กํธ์์๋ ๋ ๋์ ํด๊ฒฐ์ฑ ์ ์ฐพ๋ ํต์ฐฐ๋ ฅ์ ์ ๊ณตํฉ๋๋ค.
์ด๋ฐ ๋งฅ๋ฝ์์ ์ค๋ฒจํธ๋ ํน๋ณํ ์์น์ ์์ต๋๋ค. ์ด ๊ธ์์๋ ์ค๋ฒจํธ๊ฐ ์ ์ํ๋ ์ธ ๊ฐ์ง ํต์ฌ ์ฅ์ ์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
- ์๊ทธ๋ ํจํด์ ์ ํํ ์ด์ : ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง ์์ด ์ ํํ DOM ์ ๋ฐ์ดํธ๋ฅผ ๊ฐ๋ฅํ๊ฒ ํ๋ ์ค๋ฒจํธ์ ๋ฐ์์ฑ ์์คํ
- ๋ณด์ผ๋ฌํ๋ ์ดํธ๋ฅผ ์์ ๋ ๋ฌธ๋ฒ: ๊ฐ์ฒด์ ์ง์ ๋ณ๊ฒฝ์ด ๊ฐ๋ฅํ ์์ฐ์ค๋ฌ์ด ์ฝ๋ ์์ฑ ๋ฐฉ์
- ์ฌ์ธ์ ํ๋ ์์ํฌ๋ก์์ ๋งค๋ ฅ: ์ถ๊ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด๋ ์ ๋๋ฉ์ด์ , ์ํ ๊ด๋ฆฌ, ์คํ์ผ๋ง์ ํด๊ฒฐํ๋ ํตํฉ ๊ฒฝํ
์ค๋ฒจํธ๋ ๋จ์ํ ๋ ํ๋์ ํ๋ ์์ํฌ๋ฅผ ๋์ด, ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ ๋ํ ์๋ก์ด ๊ด์ ์ ์ ๊ณตํฉ๋๋ค. ์ง๊ธ๋ถํฐ ์ค๋ฒจํธ์ ์ธ๊ณ๋ก ํจ๊ป ๋ค์ด๊ฐ ๋ณด๊ฒ ์ต๋๋ค.
ํ๋ ์น ํ๋ ์์ํฌ๋ ์๊ทธ๋์ด ๋์ธ
๋ฆฌ์กํธ๋ฅผ ์ฌ์ฉํ๋ค ๋ณด๋ฉด ์ฑ๋ฅ ์ต์ ํ์ ๊ดํ ์๋ง์ ๊ณ ๋ฏผ์ ์ง๋ฉดํฉ๋๋ค. ๋ถํ์ํ ๋ฆฌ๋ ๋๋ง์ ๋ง๊ธฐ ์ํด โReact.memoโ๋ฅผ ์จ์ผ ํ ๊น? โuseMemoโ์ โuseCallbackโ์ ์ธ์ ํ์ํ ๊น? ์ํ ์ ๋ฐ์ดํธ ๋ก์ง์ ์ด๋ป๊ฒ ์ต์ ํํด์ผ ํ ๊น? ์ด๋ฐ ๊ณ ๋ฏผ์ด ์ต์ํ์ ๊ฐ์?
๋ฆฌ๋ ๋๋ง์ ๋ง๋ ๊ธฐ์ ์ ์ตํ๋ ๊ฒ์ด ๊ณผ์ฐ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์๋ก์์ ํต์ฌ ์ญ๋์ผ๊น์? ์ ์ด์ ๋ค๋ฅธ ํ๋ ์์ํฌ์์๋ ์ด๋ฐ ๋ฌธ์ ์์ฒด๊ฐ ์กด์ฌํ์ง ์์ ์๋ ์์ต๋๋ค. ๋ฐ๋ก ์๊ทธ๋ ํจํด ๋๋ฌธ์ ๋๋ค.
์๊ทธ๋ ํจํด์ด๋ ๋ฌด์์ธ๊ฐ?
์๊ทธ๋ ํจํด์ ์ํ ๋ณํ๋ฅผ ๋ฏธ์ธํ ๋จ์๋ก ์ถ์ ํ๊ณ ํ์ํ ๋ถ๋ถ๋ง ์ ๋ฐ์ดํธํ๋ ๋ฐ์ํ ํ๋ก๊ทธ๋๋ฐ ๊ธฐ๋ฒ์ ๋๋ค. ๊ฐ๋จํ ๋งํด ์๊ทธ๋์ ํน์ ๊ฐ์ ๊ฐ์ธ๊ณ ์๋ ์ปจํ ์ด๋๋ก์, ๊ทธ ๊ฐ์ด ๋ณ๊ฒฝ๋๋ฉด ๊ทธ ๊ฐ์ ์ฌ์ฉํ๊ณ ์๋ ์๋น์(์ปดํฌ๋ํธ๋ ๊ณ์ฐ์ ๋ฑ)์๊ฒ๋ง ๋ณ๊ฒฝ์ ์๋ฆฝ๋๋ค.
์๊ทธ๋์ ํต์ฌ์ ์ธ๋ฐํ ๋ฐ์์ฑ(fine-grained reactivity)์ ๋๋ค. ์ด๋ ํ๋ ์์ํฌ๊ฐ ๊ฐ์ด ์ด๋์ ์ด๋ป๊ฒ ์ฌ์ฉ๋๊ณ ์๋์ง ์ธ๋ฐํ๊ฒ ์ถ์ ํ์ฌ, ๊ด๋ จ๋ UI ๋ถ๋ถ๋ง "ํํฌ์ธํธ" ์ ๋ฐ์ดํธํ ์ ์๊ฒ ํฉ๋๋ค. ๋ถ๋ชจ ์ปดํฌ๋ํธ์ ์ํ๊ฐ ๋ณ๊ฒฝ๋๋๋ผ๋ ์์ ์ปดํฌ๋ํธ๋ ํด๋น ์ํ๋ฅผ ์ง์ ์ฌ์ฉํ์ง ์๋ ํ ์ ํ ์ํฅ์ ๋ฐ์ง ์๋ ๊ฒ์ด์ฃ . ์ด๋ ๋ฆฌ์กํธ์ ๋ฆฌ๋ ๋๋ง ์ ๊ทผ ๋ฐฉ์๊ณผ๋ ๊ทผ๋ณธ์ ์ผ๋ก ๋ค๋ฅธ ํจ๋ฌ๋ค์์ ๋๋ค.
ํ๋ ํ๋ ์์ํฌ๋ค์ ์๊ทธ๋ ๋์ ํํฉ
์ต๊ทผ ํ๋ก ํธ์๋ ์ธ๊ณ์์๋ '์๊ทธ๋(Signal)' ํจํด์ด ๋์ธ๋ก ์๋ฆฌ ์ก๊ณ ์์ต๋๋ค. ๋ฆฌ์กํธ๋ฅผ ์ ์ธํ ๊ฑฐ์ ๋ชจ๋ ํ๋ ํ๋ ์์ํฌ๋ค์ด ์ด ํจํด์ ์ฑํํ์ต๋๋ค. ๊ฐ ํ๋ ์์ํฌ์์ ์๊ทธ๋ ํจํด์ด ์ด๋ป๊ฒ ๊ตฌํ๋์ด ์๋์ง ์ดํด๋ณด๊ฒ ์ต๋๋ค.
Composition API์ ref
์ reactive
ํจ์๋ฅผ ํตํด ์๊ทธ๋ ๊ฐ๋
์ ๊ตฌํํ์ต๋๋ค. Vue 3๋ JavaScript์ Proxy
๋ฅผ ํ์ฉํ์ฌ ์ผ๋ฐ ๊ฐ์ฒด๋ฅผ ๊ฐ์ธ reactive proxy๋ก ๋ง๋ค๊ณ , ์ ๊ทผ๋ ์์ฑ๋ง ์ถ์ ํฉ๋๋ค. ์ด๋ฅผ ํตํด ํ
ํ๋ฆฟ์ด๋ ์ปดํฌ์ง์
ํจ์ ๋ด์์ ์ค์ ๋ก ์ฌ์ฉ๋ ๋ฐ์ดํฐ๋ง ๋ฐ์ํ์ผ๋ก ๋ฑ๋ก๋๋ฉฐ, ๊ทธ ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง ํด๋น UI๋ฅผ ์
๋ฐ์ดํธํฉ๋๋ค.
SolidJS๋ ํ๋ ์์ํฌ ์์ฒด๊ฐ ์๊ทธ๋์ ๋ฉ์ธ ์ปจ์
์ผ๋ก ์ ํํ ๋ํ์ ์ธ ์ฌ๋ก์
๋๋ค. SolidJS์์ ์ํ๋ฅผ ๋ง๋ค ๋๋ createSignal
ํจ์๋ฅผ ์ฌ์ฉํ๋ฉฐ, ์ด ํจ์๋ getter์ setter ํ ์์ ๋ฐํํฉ๋๋ค.
const [count, setCount] = createSignal(0);
SolidJS์ ํต์ฌ์ ์ปดํฌ๋ํธ ํจ์๊ฐ ๋จ ํ ๋ฒ๋ง ์คํ๋๋ค๋ ์ ์ ๋๋ค. ์ดํ์๋ ์๊ทธ๋๊ฐ์ด ๋ณ๊ฒฝ๋ ๋ ์ฐ๊ฒฐ๋ ๋ถ๋ถ๋ง ์ฆ์ ๊ฐฑ์ ๋ฉ๋๋ค. ์ปดํฌ๋ํธ ์ ์ฒด๊ฐ ์๋ ์ค์ ๋ก ๋ณ๊ฒฝ๋ ๋ถ๋ถ๋ง ์ ๋ฐ์ดํธ๋๋ฏ๋ก ๋๋ผ์ด ์ฑ๋ฅ์ ๋ณด์ฌ์ค๋๋ค.
๊ฒฝ๋ React ๋์์ผ๋ก ์ ๋ช
ํ Preact๋ @preact/signals
ํจํค์ง๋ฅผ ํตํด ์๊ทธ๋ ๊ฐ๋
์ ๋์
ํ์ต๋๋ค. Preact์์ ์๊ทธ๋์ .value
ํ๋กํผํฐ๋ก ๊ด๋ฆฌ๋ฉ๋๋ค.
import { signal } from "@preact/signals";
const count = signal(0);
// ์ฝ๊ธฐ: count.value
// ์ฐ๊ธฐ: count.value = 1;
Preact ํ์ ์๊ทธ๋ ๋์ ์ผ๋ก "์ํ ์ ๋ฐ์ดํธ ๋น์ฉ์ด ์ปดํฌ๋ํธ ์์ ์๊ด์์ด ํญ์ ๋น ๋ฅด๊ฒ ์ ์ง๋๋ค"๋ผ๊ณ ๊ฐ์กฐํฉ๋๋ค.
Angular๋ ์ต์ ๋ฒ์ (v16 ์ด์)์์ ์๊ทธ๋์ด๋ผ๋ ์๋ก์ด ๋ฐ์ํ ํ๋ฆฌ๋ฏธํฐ๋ธ๋ฅผ ๋์ ํ์ต๋๋ค.
const count = signal(0);
// ์ฝ๊ธฐ: count()
// ์ฐ๊ธฐ: count.set(1) ๋๋ count.update(v => v + 1)
Angular์ ์๊ทธ๋์ ํจ์์ฒ๋ผ ํธ์ถํ์ฌ ๊ฐ์ ์ฝ์ ์ ์๊ณ , ๋ค๋ฅธ ์๊ทธ๋์์ ํ์๋ ๊ณ์ฐ๋ ๊ฐ(computed
)์ ๋ง๋ค๊ฑฐ๋ ๋ถ์์ฉ(effect
)์ ๋ฑ๋กํ ์๋ ์์ต๋๋ค.
Svelte๋ ์ปดํ์ผ๋ฌ๋ก ์๊ทธ๋ ํจํด์ ๋ณด์ผ๋ฌํ๋ ์ดํธ๋ฅผ ์ค์ฌ์ค๋๋ค. ๊ทธ๋์ ์ ์ ๋ ๋ณด๋ค ์ง๊ด์ ์ธ ์ฝ๋๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
<script>
// Svelte 5์ $state rune
let count = $state(0);
function increment() {
// ์ง์ mutation์ด ๊ฐ๋ฅํฉ๋๋ค
count++;
// ๋๋
count = count + 1;
}
function reset() {
// ๊ฐ ํ ๋น๋ ๊ฐ๋จํฉ๋๋ค
count = 0;
}
</script>
<h1>์นด์ดํฐ: {count}</h1>
<button onclick={increment}>์ฆ๊ฐ</button>
<button onclick={reset}>๋ฆฌ์
</button>
์ด ์ฝ๋์์ count
๋ณ์๋ $state
rune์ผ๋ก ์ ์ธ๋์ด ๋ฐ์ํ ์ํ๊ฐ ๋ฉ๋๋ค. count++
๋ count = 0
๊ณผ ๊ฐ์ ์ง์ ์ ์ธ ๋ณ์ด๊ฐ ๊ฐ๋ฅํ๋ฉฐ, ํด๋น ๊ฐ์ด ๋ณ๊ฒฝ๋๋ฉด ์ด๋ฅผ ์ฌ์ฉํ๋ UI ๋ถ๋ถ(์ฌ๊ธฐ์๋ {count}
๊ฐ ํ์๋๋ ๋ถ๋ถ)๋ง ์๋์ผ๋ก ์
๋ฐ์ดํธ๋ฉ๋๋ค.
Qwik์์๋ useSignal
ํ
์ ํตํด ์๊ทธ๋์ ์์ฑํฉ๋๋ค.
const count = useSignal(0);
// ์ฝ๊ธฐ: count.value
// ์ฐ๊ธฐ: count.value = 1
๋ฆฌ์กํธ vs ์๊ทธ๋ ๊ธฐ๋ฐ ํ๋ ์์ํฌ
React์ ๋ฆฌ๋ ๋๋ง ์ค์ฌ ์ํ ๊ด๋ฆฌ
React๋ ์ปดํฌ๋ํธ ๋จ์์ ์ฌ๋ ๋๋ง์ ๊ธฐ๋ณธ ์์น์ผ๋ก ์ผ์ต๋๋ค. useState
๋ this.setState
๋ก ์ํ๋ฅผ ์
๋ฐ์ดํธํ๋ฉด React๋ ํด๋น ์ปดํฌ๋ํธ ํจ์(๋๋ ํด๋์ค ์ปดํฌ๋ํธ์ render ๋ฉ์๋)๋ฅผ ๋ค์ ํธ์ถํ๊ณ , ์์ ์ปดํฌ๋ํธ๋ค์ ๋ชจ๋ ์ฌ๊ท์ ์ผ๋ก ๋ค์ ๋ ๋๋งํฉ๋๋ค.
์ด๋ ๊ฒ ์์ฑ๋ ์๋ก์ด ๊ฐ์ DOM์ React๋ ์ด์ ๊ฐ์ DOM๊ณผ ๋น๊ต(VDOM diff)ํ์ฌ ์ค์ ๋ณ๊ฒฝ๋ ๋ถ๋ถ๋ง ์ค์ DOM์ ๋ฐ์ํฉ๋๋ค. ๋ฐ๋ผ์ React์ ์ฒ ํ์ "๋ชจ๋ ๊ฒ์ ๋ค์ ๊ณ์ฐํ๋, ์ค์ DOM ์กฐ์์ ์ต์ํ"๋ผ๊ณ ์์ฝํ ์ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด, ์๋์ ๊ฐ์ ์ฝ๋๋ฅผ ์๊ฐํด ๋ณผ๊ฒ์.
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>์นด์ดํธ: {count}</p>
<button onClick={() => setCount(count + 1)}>์ฆ๊ฐ</button>
</div>
);
}
setCount
๊ฐ ํธ์ถ๋๋ฉด Counter
์ปดํฌ๋ํธ ํจ์ ์ ์ฒด๊ฐ ๋ค์ ์คํ๋๊ณ , ๊ฐ์ DOM์ ์๋ก ์์ฑํ ํ ์ด์ ๊ณผ ๋น๊ตํฉ๋๋ค.
์๊ทธ๋ ๊ธฐ๋ฐ ์ธ๋ฐํ ๋ฐ์์ฑ
๋ฐ๋ฉด, ์๊ทธ๋ ๊ธฐ๋ฐ ํ๋ ์์ํฌ(์: SolidJS)๋ ๋ค์๊ณผ ๊ฐ์ด ๋์ํฉ๋๋ค.
function Counter() {
const [count, setCount] = createSignal(0);
return (
<div>
<p>์นด์ดํธ: {count()}</p>
<button onClick={() => setCount(count() + 1)}>์ฆ๊ฐ</button>
</div>
);
}
์ค์ํ ์ฐจ์ด์ :
- ์ปดํฌ๋ํธ ํจ์๋ ์ด๊ธฐ์ ํ ๋ฒ๋ง ์คํ๋๊ณ , ์ดํ์๋ ๋ค์ ์คํ๋์ง ์์ต๋๋ค.
count
์๊ทธ๋๊ฐ์ด ๋ณ๊ฒฝ๋๋ฉด,{count()}
๋ถ๋ถ๋ง ์ ๋ฐ์ดํธ๋ฉ๋๋ค. ์ปดํฌ๋ํธ ์ ์ฒด๋ ๋ฒํผ์ ๋ค์ ๊ณ์ฐ๋์ง ์์ต๋๋ค.- ๊ฐ์ DOM ๋น๊ต ๊ณผ์ ์์ด ์ค์ DOM์ ํ์ํ ๋ถ๋ถ๋ง ์ง์ ์ ๋ฐ์ดํธํฉ๋๋ค.
์๊ทธ๋์ด ์ฃผ๋ชฉ๋ฐ๋ ์ด์
์๊ทธ๋ ํจํด์ด ์ฃผ๋ชฉ๋ฐ๋ ์ด์ ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- ์ ํํ UI ์ ๋ฐ์ดํธ: ๋ณ๊ฒฝ๋ ์ํ์ ์ง์ ์์กดํ๋ UI ๋ถ๋ถ๋ง ์ ํํ๊ฒ ์ ๋ฐ์ดํธ๋ฉ๋๋ค. 1๋ง ๊ฐ์ ํญ๋ชฉ์ด ์๋ ๋ฆฌ์คํธ์์ ํ๋์ ํญ๋ชฉ๋ง ์ ๋ฐ์ดํธํ ๋, ๋ฆฌ์กํธ๋ ์ ์ฒด ๋ฆฌ์คํธ๋ฅผ ๋ค์ ๋ ๋๋งํ์ง๋ง, ์๊ทธ๋ ๊ธฐ๋ฐ ํ๋ ์์ํฌ๋ ํด๋น ํญ๋ชฉ๋ง ์ ํํ ์ ๋ฐ์ดํธํฉ๋๋ค.
- ๋ณด์ผ๋ฌํ๋ ์ดํธ ์ฝ๋ ๊ฐ์: ๋ถ๋ณ์ฑ ์ ์ง๋ฅผ ์ํ ๋ณต์กํ ์ฝ๋๊ฐ ํ์ ์์ต๋๋ค. ์ผ๋ฐ์ ์ธ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋์ฒ๋ผ ์ง์ ๊ฐ์ฒด๋ฅผ ์์ ํ ์ ์์ต๋๋ค.
- ์์ธก ๊ฐ๋ฅํ ๋ฐ์์ฑ: ์ํ ๋ณํ์ UI ์ ๋ฐ์ดํธ ์ฌ์ด์ ๊ด๊ณ๊ฐ ๋ช ํํ๊ณ ์ง๊ด์ ์ ๋๋ค.
- ๋ฉ๋ชจ๋ฆฌ ํจ์จ์ฑ: ๊ฐ์ DOM์ ์ฌ์ฉํ์ง ์๊ฑฐ๋, ์ต์ํํ์ฌ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ์ค์ผ ์ ์์ต๋๋ค.
- ์๋ ์ต์ ํ: ๊ฐ๋ฐ์๊ฐ ๋ช ์์ ์ธ ์ต์ ํ(๋ฉ๋ชจ์ด์ ์ด์ ๋ฑ)๋ฅผ ์ ์ฉํ์ง ์์๋ ํ๋ ์์ํฌ๊ฐ ์๋์ผ๋ก ์ต์ ํํฉ๋๋ค. ๋ฆฌ๋ ๋๋ง์ ๋ง๊ธฐ ์ํ ๊ธฐ๋ฒ์ ๊ณ ๋ฏผํ ํ์๊ฐ ์์ต๋๋ค.
๋ฆฌ์กํธ๋ ๋ค๋ฅธ ๊ธธ์ ๊ฐ๋ ์ค
ํฅ๋ฏธ๋ก์ด ์ ์ ๋ฆฌ์กํธ ํ๋ ์ฑ๋ฅ ์ต์ ํ์ ์ค์์ฑ์ ์ธ์ํ๊ณ ์์ง๋ง, ์๊ทธ๋ ํจํด๊ณผ๋ ๋ค๋ฅธ ์ ๊ทผ ๋ฐฉ์์ ์ทจํ๊ณ ์๋ค๋ ๊ฒ์ ๋๋ค. ๋ฆฌ์กํธ ์ฝ์ด ํ์ ์ค๋๋ฅ ํด๋ผํฌ(Andrew Clark)๋ "์๊ทธ๋๊ณผ ๊ฐ์ ๊ธฐ๋ณธ ์์๋ฅผ ๋ฆฌ์กํธ์ ์ถ๊ฐํ ์ ์์ง๋ง, ์ด๊ฒ์ด UI ์ฝ๋๋ฅผ ์์ฑํ๋ ์ข์ ๋ฐฉ๋ฒ์ด๋ผ๊ณ ์๊ฐํ์ง๋ ์๋๋ค. ์ฑ๋ฅ ๋ฉด์์๋ ๋ฐ์ด๋์ง๋ง, ๋๋ ๋งค๋ฒ ์ ์ฒด๊ฐ ๋ค์ ์์ฑ๋๋ค๊ณ ๊ฐ์ ํ๋ ๋ฆฌ์กํธ์ ๋ชจ๋ธ์ ์ ํธํ๋ค. ์ฐ๋ฆฌ์ ๊ณํ์ ์ปดํ์ผ๋ฌ๋ฅผ ์ฌ์ฉํ์ฌ ๋น์ทํ ์ฑ๋ฅ์ ๋ฌ์ฑํ๋ ๊ฒ์ด๋ค."๋ผ๊ณ ์ธ๊ธํ์ต๋๋ค.

๋ฆฌ์กํธ๋ 'React Forget'์ด๋ผ๋ ์ปดํ์ผ๋ฌ๋ฅผ ํตํด ์๋์ผ๋ก useCallback
, useMemo
๋ฑ์ ์ ์ฉํ๊ณ , ์๋ฒ ์ปดํฌ๋ํธ๋ฅผ ํตํด ๋ฆฌ๋ ๋๋ง์ด ํ์ํ ์ปดํฌ๋ํธ๋ฅผ ์ต์ํํ๋ ๋ฐฉ์์ผ๋ก ์ต์ ํ๋ฅผ ์ถ๊ตฌํ๊ณ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ด๋ฐ ์ ๊ทผ ๋ฐฉ์์ ์ค๋ซ๋์ ๊ฐ๋ฐ ์ค์ด์ง๋ง, ์์ง ์ค๋ฌด์์ ์์ ํ ํ์ฉํ๊ธฐ ์ด๋ ค์ด ์ํ์
๋๋ค.
๋ฆฌ์กํธ์ ๋ฐฉ์์ ํจ์ํ ์ปดํฌ๋ํธ์ ๋ฆฌ๋ ๋๋ง์ ์ต์ ํํ๋ ๋ฐ ์ด์ ์ ๋ง์ถ๊ณ ์์ง๋ง, ๊ฒฐ๊ตญ ์ปดํฌ๋ํธ ์ฐจ์ด๋ฅผ ๋น๊ตํ๋ ๊ณผ์ ์ ์ฌ์ ํ ํ์ํฉ๋๋ค. ์ด๋ ์ ์ด์ ๊ฐ์ DOM๊ณผ ๋น๊ต(diffing) ์๊ณ ๋ฆฌ์ฆ์ ์์กดํ๋ ๋ฆฌ์กํธ์ ๊ทผ๋ณธ์ ์ธ ์ ๊ทผ ๋ฐฉ์์์ ๋น๋กฏ๋ฉ๋๋ค.
ํํธ, TC39(์๋ฐ์คํฌ๋ฆฝํธ ํ์คํ ์์ํ)์์๋ ์๊ทธ๋ ํจํด์ ์ธ์ด ํ์ค์ผ๋ก ๋ง๋ค๊ธฐ ์ํ ์ ์์ด ์งํ ์ค์ ๋๋ค. ์ด ์ ์์ "ํ๋ก๋ฏธ์ค/A+๊ฐ ES2015์ ํ๋ก๋ฏธ์ค ํ์คํ์ ์ ํํ๋ ๊ฒ์ฒ๋ผ, ์๋ฐ์คํฌ๋ฆฝํธ์์ ์๊ทธ๋์ ์ด๊ธฐ ๊ณตํต ๋ฐฉํฅ์ ์ค๋ช "ํ๊ณ ์์ต๋๋ค. Angular, Preact, Qwik, Solid, Svelte, Vue ๋ฑ ์ฌ๋ฌ ํ๋ ์์ํฌ์ ๊ฐ๋ฐ์๋ค์ด ์ด ํ์คํ ์์ ์ ์ฐธ์ฌํ๊ณ ์์ผ๋ฉฐ, ์ด๋ ์๋ฐ์คํฌ๋ฆฝํธ ์ํ๊ณ ์ ๋ฐ์ ํตํฉ์ ๋ชฉํ๋ก ํฉ๋๋ค.
๊ฒฐ๊ตญ ๋ฆฌ์กํธ๊ฐ ์๊ทธ๋ ํจํด์ ์ฑํํ์ง ์๋๋ผ๋ ํ๋ก ํธ์๋ ์ํ๊ณ ์ ๋ฐ์ ์๊ทธ๋ ๋ฐฉํฅ์ผ๋ก ์์ง์ด๊ณ ์์ต๋๋ค. ์ค๋ฒจํธ์ ๊ฐ์ ํ๋ ์์ํฌ๋ ์ด๋ฏธ ์ปดํ์ผ ํ์์ ์๊ทธ๋๊ณผ ์ ์ฌํ ๋ฐ์์ฑ์ ์ ๊ณตํ๊ณ ์์ผ๋ฉฐ, ์ด๋ ๋ฆฌ์กํธ์ ๊ฐ์ DOM ๋น๊ต ๋ฐฉ์๋ณด๋ค ๋ ํจ์จ์ ์ธ ์ ๊ทผ ๋ฐฉ์์ผ๋ก ์ฃผ๋ชฉ๋ฐ๊ณ ์์ต๋๋ค.
์๊ทธ๋ ํจํด์ ๋จ์ํ ์ํ ๊ด๋ฆฌ์ ์๋ก์ด ์ถ์ธ๊ฐ ์๋๋ผ, ์น ์ ํ๋ฆฌ์ผ์ด์ ์์ UI์ ์ํ๋ฅผ ์ฐ๊ฒฐํ๋ ๊ทผ๋ณธ์ ์ผ๋ก ๋ ํจ์จ์ ์ธ ๋ฐฉ๋ฒ์ ์ ์ํฉ๋๋ค. ์ด๋ ๋ณต์กํ ์ ํ๋ฆฌ์ผ์ด์ ์์ ํนํ ์ค์ํ ์๋ฏธ๋ฅผ ๊ฐ์ต๋๋ค.
๋ ์ด์์ ๋ณด์ผ๋ฌ ํ๋ ์ดํธ๋ ์๋ค
๋ฆฌ์กํธ๋ฅผ ์ฌ์ฉํด ๋ณธ ๊ฐ๋ฐ์๋ผ๋ฉด ์ํ ์ ๋ฐ์ดํธ๋ฅผ ์ํ ๋ณด์ผ๋ฌํ๋ ์ดํธ ์ฝ๋์ ์ง์ณ๋ณธ ๊ฒฝํ์ด ์์ ๊ฒ์ ๋๋ค. ํนํ ๋ณต์กํ ๊ฐ์ฒด๋ ๋ฐฐ์ด์ ๋ค๋ฃฐ ๋ ๋ถ๋ณ์ฑ์ ์ ์งํ๊ธฐ ์ํ ์ฝ๋๋ ์๋นํ ๋ฒ๊ฑฐ๋กญ์ต๋๋ค. ์ค๋ฒจํธ๋ ์ด๋ฐ ๋ฌธ์ ๋ฅผ ์ปดํ์ผ ์์ ์ ํ๋ก์ ํจํด์ผ๋ก ์ฐ์ํ๊ฒ ํด๊ฒฐํฉ๋๋ค. ์ฝ๋ ์์๋ฅผ ํตํด ์ค๋ฒจํธ์ ๋ฌธ๋ฒ์ด ์ผ๋ง๋ ์ง๊ด์ ์ด๊ณ ๊ฐ๊ฒฐํ์ง ์ดํด๋ณด๊ฒ ์ต๋๋ค.
๋ฐฐ์ด ์กฐ์: ๋ฆฌ์กํธ vs ์ค๋ฒจํธ
๋ฆฌ์กํธ์์์ ๋ฐฐ์ด ์ ๋ฐ์ดํธ
๋ฆฌ์กํธ์์ ๋ฐฐ์ด ํญ๋ชฉ์ ์ถ๊ฐํ๊ฑฐ๋ ์ ๊ฑฐํ ๋๋ ํญ์ ๋ถ๋ณ์ฑ์ ์ ์งํด์ผ ํฉ๋๋ค.
function TodoList() {
const [todos, setTodos] = React.useState([
{ id: 1, text: "Learn React", completed: false },
{ id: 2, text: "Build an app", completed: false }
])
const addTodo = (text) => {
// ๊ธฐ์กด ๋ฐฐ์ด์ ๋ณต์ฌํ๊ณ ์ ํญ๋ชฉ ์ถ๊ฐ
setTodos([...todos, {
id: Date.now(),
text,
completed: false
}])
}
const removeTodo = (id) => {
// ํํฐ๋ก ์ ๋ฐฐ์ด ์์ฑ
setTodos(todos.filter(todo => todo.id !== id))
}
const toggleTodo = (id) => {
// map์ผ๋ก ์ ๋ฐฐ์ด ์์ฑํ๋ฉด์ ํน์ ํญ๋ชฉ๋ง ์์
setTodos(todos.map(todo =>
todo.id === id
? { ...todo, completed: !todo.completed }
: todo
))
}
return (/* ๋ ๋๋ง ๋ก์ง */)
}
๋ฐฐ์ด ์กฐ์์ ์ํด ํ์ฐ ์ฐ์ฐ์(...
), map()
, filter()
๋ฑ์ ์ฌ์ฉํด ํญ์ ์ ๋ฐฐ์ด์ ์์ฑํด์ผ ํฉ๋๋ค. ์ฝ๋๊ฐ ์ฅํฉํด์ง๊ณ ์๋๊ฐ ์ง๊ด์ ์ผ๋ก ๋ณด์ด์ง ์์ต๋๋ค.
์ค๋ฒจํธ 5์์์ ๋ฐฐ์ด ์ ๋ฐ์ดํธ
๊ฐ์ ๊ธฐ๋ฅ์ ์ค๋ฒจํธ 5๋ก ๊ตฌํํ๋ฉด ํจ์ฌ ๊ฐ๊ฒฐํด์ง๋๋ค.
<script>
// ์ค๋ฒจํธ 5์ $state rune์ผ๋ก ๋ฐ์ํ ์ํ ์ ์ธ
let todos = $state([
{ id: 1, text: "Learn Svelte", completed: false },
{ id: 2, text: "Build an app", completed: false }
])
function addTodo(text) {
// ์ง์ ๋ฐฐ์ด์ push - ๋ณ์ด(mutation)๊ฐ ๊ฐ๋ฅ!
todos.push({
id: Date.now(),
text,
completed: false
})
}
function removeTodo(id) {
// ์๋ณธ ๋ฐฐ์ด์์ ์ง์ ์ ๊ฑฐ
const index = todos.findIndex(todo => todo.id === id)
if (index !== -1) {
todos.splice(index, 1)
}
}
function toggleTodo(id) {
// ํด๋น ํญ๋ชฉ์ ์ง์ ์์
const todo = todos.find(todo => todo.id === id)
if (todo) {
todo.completed = !todo.completed
}
}
function popLastTodo() {
// pop()๋ ์ง์ ์ฌ์ฉ ๊ฐ๋ฅ
todos.pop()
}
</script>
<!-- ์ค๋ฒจํธ 5 ๋ฌธ๋ฒ์ผ๋ก ๋งํฌ์
-->
<ul>
{each todos as todo}
<li class={todo.completed ? "completed" : ""}>
<span>{todo.text}</span>
<button onclick={() => toggleTodo(todo.id)}>Toggle</button>
<button onclick={() => removeTodo(todo.id)}>Delete</button>
</li>
{/each}
</ul>
<button onclick={popLastTodo}>Remove Last</button>
์ค๋ฒจํธ์์๋ $state()
๋ก ์ํ๋ฅผ ์ ์ธํ๋ฉด ๊ทธ ์ํ๋ฅผ ์ง์ ๋ณ๊ฒฝํ ์ ์์ต๋๋ค. .push()
, .pop()
, .splice()
๊ฐ์ ๋ฐฐ์ด ๋ฉ์๋๋ฅผ ์ง์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ปดํ์ผ ์์ ์ ์ค๋ฒจํธ๋ ์ด๋ฌํ ๋ฉ์๋ ํธ์ถ์ ๊ฐ๋ก์ฑ์ ์ํ ๋ณ๊ฒฝ์ ๊ฐ์งํ๊ณ UI๋ฅผ ์๋์ผ๋ก ์
๋ฐ์ดํธํฉ๋๋ค.
๋ฐฐ์ด ์์ ์ง์ ์์ ์ ๊ฐํธํจ
๋ฆฌ์กํธ์ ์ค๋ฒจํธ์ ๊ฐ์ฅ ํฐ ์ฐจ์ด์ ์ค ํ๋๋ ๋ฐฐ์ด ์์๋ฅผ ์ง์ ์์ ํ ์ ์๋ ๋ฅ๋ ฅ์ ๋๋ค. ๋ฆฌ์กํธ์์๋ ํนํ ๋ฐฐ์ด์ ์ค๊ฐ ์์๋ฅผ ์์ ํ ๋ ๊ณ ํต์ค๋ฌ์ด ๊ฒฝํ์ ํ๊ฒ ๋ฉ๋๋ค.
๋ฆฌ์กํธ์์ ๋ฐฐ์ด ์์ ์์ ํ๊ธฐ
function ItemList() {
const [items, setItems] = React.useState([
{ id: 1, name: "Item 1", count: 0, details: { category: "A" } },
{ id: 2, name: "Item 2", count: 0, details: { category: "B" } },
{ id: 3, name: "Item 3", count: 0, details: { category: "A" } }
])
// ํน์ ์ธ๋ฑ์ค์ ํญ๋ชฉ ์์
const updateItemAtIndex = (index, newCount) => {
setItems(items.map((item, i) =>
i === index
? { ...item, count: newCount }
: item
))
}
// ํน์ ์ธ๋ฑ์ค์ ์ค์ฒฉ ์์ฑ ์์
const updateItemCategory = (index, newCategory) => {
setItems(items.map((item, i) =>
i === index
? { ...item, details: { ...item.details, category: newCategory } }
: item
))
}
return (
<div>
{items.map((item, index) => (
<div key={item.id}>
<span>{item.name}: {item.count}</span>
<button onClick={() => updateItemAtIndex(index, item.count + 1)}>
Increment
</button>
<span>Category: {item.details.category}</span>
<button onClick={() => updateItemCategory(index, item.details.category === "A" ? "B" : "A")}>
Toggle Category
</button>
</div>
))}
</div>
)
}
์ค๋ฒจํธ์์ ๋ฐฐ์ด ์์ ์์ ํ๊ธฐ
<script>
let items = $state([
{ id: 1, name: "Item 1", count: 0, details: { category: "A" } },
{ id: 2, name: "Item 2", count: 0, details: { category: "B" } },
{ id: 3, name: "Item 3", count: 0, details: { category: "A" } }
])
function updateItemAtIndex(index, newCount) {
// ๋ฐฐ์ด ์์ ์ง์ ์์ !
items[index].count = newCount
}
function updateItemCategory(index, newCategory) {
// ์ค์ฒฉ๋ ์์ฑ๋ ์ง์ ์์ !
items[index].details.category = newCategory
}
</script>
<div>
{each items as item, index}
<div>
<span>{item.name}: {item.count}</span>
<button onclick={() => updateItemAtIndex(index, item.count + 1)}>
Increment
</button>
<span>Category: {item.details.category}</span>
<button onclick={() => updateItemCategory(index, item.details.category === "A" ? "B" : "A")}>
Toggle Category
</button>
</div>
{/each}
</div>
์ค๋ฒจํธ์์๋ items[index].count = newCount
์ ๊ฐ์ด ๋ฐฐ์ด์ ํน์ ์์๋ฅผ ์ง์ ์์ ํ ์ ์์ต๋๋ค. ์ค์ฒฉ๋ ๊ฐ์ฒด ์์ฑ๋ items[index].details.category = newCategory
์ฒ๋ผ ๋ฐ๋ก ์์ ํ ์ ์์ต๋๋ค. ์ด๊ฒ์ด ๋ฐ๋ก ์ค๋ฒจํธ์ ๊ฐ์ฅ ํฐ ๋งค๋ ฅ ์ค ํ๋์
๋๋ค.
๋ฐ์ธ๋ฉ ๋ฌธ๋ฒ๊ณผ ํผ ์ฒ๋ฆฌ์ ๊ฐ์ํ
๋ฆฌ์กํธ์ ์ค๋ฒจํธ์ ๋ ๋ค๋ฅธ ํฐ ์ฐจ์ด์ ์ ๋ฐ์ธ๋ฉ ๋ฌธ๋ฒ์ ๋๋ค. ์ค๋ฒจํธ๋ ์๋ฐฉํฅ ๋ฐ์ธ๋ฉ์ ์์ฐ์ค๋ฝ๊ฒ ์ง์ํ์ฌ ํผ ์ฒ๋ฆฌ๋ฅผ ํฌ๊ฒ ๊ฐ์ํํฉ๋๋ค.
๋ฆฌ์กํธ์์์ ํผ ์ฒ๋ฆฌ
๋ฆฌ์กํธ์์๋ ์
๋ ฅ ํ๋๋ง๋ค value
์ onChange
ํธ๋ค๋ฌ๋ฅผ ์ฐ๊ฒฐํด์ผ ํฉ๋๋ค. ํผ์ด ๋ณต์กํด์ง๋ฉด ์ฝ๋๋ ๋ณต์กํด์ง๊ณ , react-hook-form ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋์
ํ๊ฒ ๋ฉ๋๋ค. ์ด๋ ํผ ์
๋ ฅ ๋๋ง๋ค ์ปดํฌ๋ํธ๊ฐ ๋ค์ ๋ ๋๋ง๋๋ ๋ฆฌ์กํธ์ ํน์ฑ ๋๋ฌธ์
๋๋ค.
function SignupForm() {
const [formData, setFormData] = React.useState({
username: "",
email: "",
password: "",
confirmPassword: "",
agreeTerms: false
})
const [errors, setErrors] = React.useState({})
const handleChange = (e) => {
const { name, value, type, checked } = e.target
setFormData({
...formData,
[name]: type === "checkbox" ? checked : value
})
}
const validateForm = () => {
// ์๋ต: ์ ํจ์ฑ ๊ฒ์ฌ ๋ก์ง
}
const handleSubmit = (e) => {
e.preventDefault()
if (validateForm()) {
// ํผ ์ ์ถ ๋ก์ง
}
}
return (
<form onSubmit={handleSubmit}>
<div>
<label>Username:</label>
<input
type="text"
name="username"
value={formData.username}
onChange={handleChange}
/>
{errors.username && <p className="error">{errors.username}</p>}
</div>
{/* ๋ ๋ง์ ํ๋๋ค... */}
<div>
<label>
<input
type="checkbox"
name="agreeTerms"
checked={formData.agreeTerms}
onChange={handleChange}
/>
I agree to the terms
</label>
{errors.agreeTerms && <p className="error">{errors.agreeTerms}</p>}
</div>
<button type="submit">Sign Up</button>
</form>
)
}
์ค๋ฒจํธ 5์์์ ํผ ์ฒ๋ฆฌ
<script>
let formData = $state({
username: "",
email: "",
password: "",
confirmPassword: "",
agreeTerms: false
})
let errors = $state({})
function validateForm() {
// ์๋ต: ์ ํจ์ฑ ๊ฒ์ฌ ๋ก์ง
return true
}
function handleSubmit() {
if (validateForm()) {
// ํผ ์ ์ถ ๋ก์ง
}
}
</script>
<form onsubmit={handleSubmit}>
<div>
<label>Username:</label>
<!-- ์๋ฐฉํฅ ๋ฐ์ธ๋ฉ - ํธ๋ค๋ฌ ํ์ ์์ -->
<input type="text" bind:value={formData.username} />
{if errors.username}
<p class="error">{errors.username}</p>
{/if}
</div>
<!-- ๋ ๋ง์ ํ๋๋ค... -->
<div>
<label>
<!-- ์ฒดํฌ๋ฐ์ค๋ ๊ฐ๋จํ๊ฒ ๋ฐ์ธ๋ฉ -->
<input type="checkbox" bind:checked={formData.agreeTerms} />
I agree to the terms
</label>
{if errors.agreeTerms}
<p class="error">{errors.agreeTerms}</p>
{/if}
</div>
<button type="submit">Sign Up</button>
</form>
์ค๋ฒจํธ์ bind:value
์ bind:checked
์ง์์ด๋ฅผ ์ฌ์ฉํ๋ฉด ์๋ฐฉํฅ ๋ฐ์ธ๋ฉ์ด ์๋์ผ๋ก ์ค์ ๋ฉ๋๋ค. ๋ณ๋์ ์ด๋ฒคํธ ํธ๋ค๋ฌ ์์ด๋ ์
๋ ฅ๊ฐ๊ณผ ์ํ๊ฐ ๋๊ธฐํ๋ฉ๋๋ค.
๋ฆฌ์กํธ์ ์ค๋ฒจํธ์ ๊ทผ๋ณธ์ ์ฐจ์ด
๋ฆฌ์กํธ์ ์ค๋ฒจํธ์ ์ด๋ฐ ์ฐจ์ด๋ ๋จ์ํ ๋ฌธ๋ฒ ์ฐจ์ด๋ฅผ ๋์ด ๋ ํ๋ ์์ํฌ์ ๊ทผ๋ณธ์ ์ธ ์ฒ ํ ์ฐจ์ด์์ ๋น๋กฏ๋ฉ๋๋ค.
๋ฆฌ์กํธ: ๋ถ๋ณ์ฑ๊ณผ ์์ ํ ๋ฆฌ๋ ๋๋ง
๋ฆฌ์กํธ๋ ์ํ๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์ปดํฌ๋ํธ ํจ์๋ฅผ ๋ค์ ์คํํ๊ณ , ์ด์ ๊ฒฐ๊ณผ์ ์ ๊ฒฐ๊ณผ๋ฅผ ๋น๊ตํฉ๋๋ค. ์ด ๊ณผ์ ์์ ๋ถ๋ณ์ฑ์ ๋งค์ฐ ์ค์ํฉ๋๋ค.
- ์ํ ๋ณ๊ฒฝ์ ๊ฐ์งํ๊ธฐ ์ํด ์ฐธ์กฐ ๋น๊ต๋ฅผ ์ฌ์ฉํฉ๋๋ค.
- ์ปดํฌ๋ํธ๊ฐ ๋ถํ์ํ๊ฒ ๋ฆฌ๋ ๋๋ง๋๋ ๊ฒ์ ๋ฐฉ์งํ๊ธฐ ์ํด ๋ฉ๋ชจ์ด์ ์ด์ (React.memo, useMemo, useCallback)์ด ํ์ํฉ๋๋ค.
- ์๋ณธ ๊ฐ์ฒด๋ฅผ ์ง์ ์์ ํ๋ฉด ๋ฆฌ์กํธ๊ฐ ๋ณ๊ฒฝ์ ๊ฐ์งํ์ง ๋ชปํฉ๋๋ค.
์ด๋ก ์ธํด ๋ฆฌ์กํธ์์๋ ๋ฐฐ์ด์ด๋ ๋ณต์กํ ๊ฐ์ฒด๋ฅผ ์ฒ๋ฆฌํ ๋ ์๋ณธ ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ๋ณต์ฌํ๊ณ ์ ๋ฒ์ ์ ๋ง๋ค์ด์ผ ํฉ๋๋ค. ๊ฐ์ฒด๊ฐ ๊น๊ฒ ์ค์ฒฉ๋ ์๋ก ๋ ๋ง์ ๋ณด์ผ๋ฌํ๋ ์ดํธ ์ฝ๋๊ฐ ํ์ํฉ๋๋ค.
์ค๋ฒจํธ: ์ง์ ๋ณ๊ฒฝ๊ณผ ์ธ๋ฐํ ์ ๋ฐ์ดํธ
์ค๋ฒจํธ๋ ์ปดํ์ผ ์์ ์ ๋ฐ์์ฑ์ ์ฃผ์ ํฉ๋๋ค.
- ๋ณ์์ ๊ฐ์ ํ ๋นํ๊ฑฐ๋, ๊ฐ์ฒด์ ๋ฐฐ์ด์ ๋ณํํ๋ ๊ฒ์ ๊ฐ์งํฉ๋๋ค.
- ์ปดํ์ผ๋ฌ๊ฐ ๋ณ๊ฒฝ ์ฌํญ์ ์ถ์ ํ๊ณ ํ์ํ DOM ์ ๋ฐ์ดํธ๋ง ์คํํฉ๋๋ค.
- ์ ์ฒด ์ปดํฌ๋ํธ๋ฅผ ๋ค์ ์คํํ์ง ์๊ณ ๋ณ๊ฒฝ๋ ๋ถ๋ถ๋ง ์ ๋ฐ์ดํธํฉ๋๋ค.
์ด๋ฐ ์ ๊ทผ ๋ฐฉ์ ๋๋ถ์ ์ค๋ฒจํธ์์๋ ๋ฐฐ์ด์ ํญ๋ชฉ์ ์ถ๊ฐํ๊ธฐ ์ํด items.push(newItem)
์ ๊ฐ์ ์ง๊ด์ ์ธ ์ฝ๋๋ฅผ ์์ฑํ ์ ์๊ณ , ๊ฐ์ฒด์ ๊น์ ์์ฑ์ user.preferences.notifications.email = true
์ ๊ฐ์ด ์ง์ ์์ ํ ์ ์์ต๋๋ค.
์ฌ์ธ์ ํ๋ ์์ํฌ์ ๋งค๋ ฅ
ํ๋ก ํธ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ฐ๋ฐํ ๋ ์ฐ๋ฆฌ๋ ๋ณดํต ์ฌ๋ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์กฐํฉํด์ ์ฌ์ฉํฉ๋๋ค. ๋ฆฌ์กํธ๋ก ๊ฐ๋ฐ์ ์์ํ๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ ๊ฒฐ์ ์ ํด์ผ ํฉ๋๋ค:
- ์ํ ๊ด๋ฆฌ: Redux? MobX? Zustand? Recoil? Jotai? ์๋๋ฉด Context API๋ง ์ฌ์ฉํ ๊น?
- ์คํ์ผ๋ง: CSS Modules? Styled-components? Emotion? CSS-in-JS vs CSS-in-CSS?
- ์ ๋๋ฉ์ด์ : Framer Motion? React Spring? GSAP?
์ด๋ฐ ์ ํ์ง๊ฐ ๋ง๋ค๋ ๊ฒ์ ์ ์ฐ์ฑ์ ์ ๊ณตํ์ง๋ง, ๋์์ "๊ฒฐ์ ํผ๋ก(decision fatigue)"๋ฅผ ์ ๋ฐํฉ๋๋ค. ๋ํ ๊ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ง๋ค ๋ค๋ฅธ API์ ํจํด์ ๋ฐฐ์์ผ ํ๋ฉฐ, ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ฐ ํตํฉ ๊ณผ์ ์์ ์์์น ๋ชปํ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
๋ฐ๋ฉด, ์ค๋ฒจํธ๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ง์ ๊ธฐ๋ฅ์ ๋ด์ฅํ๊ณ ์๋ "์ฌ์ธ์" ํ๋ ์์ํฌ์ ๋๋ค. ์คํ์ผ๋ง, ์ ๋๋ฉ์ด์ , ์ํ ๊ด๋ฆฌ ๋ฑ์ ์ํ ๋ณ๋์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด๋ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค. ์ด๋ฐ ํตํฉ์ ์ ๊ทผ ๋ฐฉ์์ ๊ฐ๋ฐ ๊ฒฝํ์ ํฌ๊ฒ ํฅ์์ํค๊ณ ๋ฒ๋ค ํฌ๊ธฐ๋ ์ค์ฌ์ค๋๋ค.
์ปดํฌ๋ํธ ์ค์ฝํ CSS: ๋ชจ๋ํ๊ฐ ๊ธฐ๋ณธ
๋ฆฌ์กํธ์์ CSS๋ฅผ ๋ชจ๋ํํ๋ ค๋ฉด CSS Modules, Styled Components ๋ฑ์ ์ถ๊ฐ ๋๊ตฌ๊ฐ ํ์ํฉ๋๋ค. ๊ทธ๋ฌ๋ ์ค๋ฒจํธ์์๋ ์คํ์ผ ๋ชจ๋ํ๊ฐ ๊ธฐ๋ณธ ์ ๊ณต๋ฉ๋๋ค.
<script>
let isActive = $state(false)
</script>
<button
class={isActive ? 'active' : ''}
onclick={() => isActive = !isActive}
>
Toggle Me
</button>
<style>
/* ์ด ์คํ์ผ์ ์๋์ผ๋ก ์ด ์ปดํฌ๋ํธ์๋ง ๋ฒ์๊ฐ ํ์ ๋ฉ๋๋ค */
button {
background: eee;
border: 1px solid 999;
border-radius: 4px;
padding: 8px 16px;
}
.active {
background: 67b3ff;
color: white;
}
</style>
<style>
ํ๊ทธ ์์ ์์ฑํ CSS๋ ์๋์ผ๋ก ํด๋น ์ปดํฌ๋ํธ์๋ง ์ ์ฉ๋ฉ๋๋ค. ํด๋์ค ์ด๋ฆ์ด๋ ์ ํ์๊ฐ ๋ค๋ฅธ ์ปดํฌ๋ํธ์ ์ถฉ๋ํ ๊ฑฑ์ ์์ด ๊ฐ๋จํ ์ด๋ฆ์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ค๋ฒจํธ๋ ๋น๋ ๊ณผ์ ์์ ์ด ์คํ์ผ์ ํด์ฑํ์ฌ ๊ณ ์ ํ ํด๋์ค ์ด๋ฆ์ผ๋ก ๋ณํํฉ๋๋ค.
๊ธ๋ก๋ฒ ์คํ์ผ์ ์ ์ฉํ๊ณ ์ถ๋ค๋ฉด :global()
์์ ์๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค.
<style>
:global(body) {
margin: 0;
font-family: sans-serif;
}
/* ํ์ ์ ํ์์ ๊ธ๋ก๋ฒ ์ ์ฉ */
div :global(a) {
color: purple;
}
</style>
์ค๋ฒจํธ๋ CSS ๋ณ์์ ๋ฏธ๋์ด ์ฟผ๋ฆฌ ๊ฐ์ ๋ชจ๋ ์ต์ CSS ๊ธฐ๋ฅ์ ์ง์ํ๋ฉฐ, ๋ณ๋์ CSS-in-JS ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด๋ ๋์ ์คํ์ผ๋ง์ ์ฝ๊ฒ ๊ตฌํํ ์ ์์ต๋๋ค.
์ ์ญ ์ํ ๊ด๋ฆฌ: .svelte.ts
์ Runes์ ๋ง๋ฒ
๋ฆฌ์กํธ์์๋ ์ปดํฌ๋ํธ ์ธ๋ถ์์ ์ํ๋ฅผ ๊ด๋ฆฌํ๋ ค๋ฉด Redux, Zustand ๊ฐ์ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ํ์ํฉ๋๋ค. useState
์ ๊ฐ์ ํ
์ ๋ฆฌ์กํธ ์ปดํฌ๋ํธ ๋ด๋ถ์์๋ง ์ฌ์ฉํ ์ ์๋ ์ ์ฝ์ด ์์ต๋๋ค.
์ค๋ฒจํธ 5์์๋ Runes ์์คํ
์ ํตํด ์ด ๋ฌธ์ ๋ฅผ ์ฐ์ํ๊ฒ ํด๊ฒฐํฉ๋๋ค. .svelte.ts
ํ์ผ์ ์ฌ์ฉํ๋ฉด ์ค๋ฒจํธ์ ๋ฐ์์ฑ ์์คํ
์ ์ปดํฌ๋ํธ ์ธ๋ถ์์๋ ํ์ฉํ ์ ์์ต๋๋ค.
export function createCounter() {
let count = $state(0);
return {
get count() { return count },
increment: () => count += 1
};
}
<!-- App.svelte -->
<script lang="ts">
import { createCounter } from './counter.svelte.js';
const counter = createCounter();
</script>
<button onclick={counter.increment}>
clicks: {counter.count}
</button>
.svelte.js
์ .svelte.ts
๋ชจ๋์์๋ ์ปดํฌ๋ํธ ์ธ๋ถ์์๋ Runes๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๋ฆฌํด๋ ๊ฐ์ฒด์์ get
ํ๋กํผํฐ๋ฅผ ์ฌ์ฉํ๋ฉด counter.count
๊ฐ ํญ์ ํ์ฌ ๊ฐ์ ์ฐธ์กฐํฉ๋๋ค.์ด๋ฐ ๋ฐฉ์์ผ๋ก ์ค๋ฒจํธ 5๋ ๋ณ๋์ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด๋, ์ ์ญ ์ํ๋ฅผ ๊ด๋ฆฌํ ์ ์๊ฒ ํด์ค๋๋ค. ๋ฆฌ์กํธ์ Context API๋ Redux์ ๊ฐ์ ๋ณต์กํ ์ค์ ์์ด ๊ฐ๋จํ๊ฒ ์ํ๋ฅผ ๊ณต์ ํ๊ณ ์ ๋ฐ์ดํธํ ์ ์์ต๋๋ค.
๋ด์ฅ ์ ๋๋ฉ์ด์ ๊ณผ ํธ๋์ง์ : ์ถ๊ฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด๋ ๊ฐ๋ฅ
๋ฆฌ์กํธ์์ ์์๊ฐ DOM์ ์ถ๊ฐ๋๊ฑฐ๋ ์ ๊ฑฐ๋ ๋ ์ ๋๋ฉ์ด์ ์ ์ ์ฉํ๋ ค๋ฉด, Framer Motion ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ํ์ํฉ๋๋ค. ์ค๋ฒจํธ์์๋ ์ด๋ฐ ๊ธฐ๋ฅ์ด ๊ธฐ๋ณธ์ผ๋ก ๋ด์ฅ๋์ด ์์ต๋๋ค.
<script>
import { fade, fly, slide } from 'svelte/transition'
import { elasticOut } from 'svelte/easing'
let visible = $state(true)
</script>
<button onclick={() => visible = !visible}>
Toggle
</button>
{if visible}
<div transition:fade={{ duration: 300 }}>
Fades in and out
</div>
{/if}
{if visible}
<div in:fly={{ y: 200, duration: 500 }} out:slide>
Flies in, slides out
</div>
{/if}
transition:
, in:
, out:
์ง์์ด๋ฅผ ์ฌ์ฉํ์ฌ ์์๊ฐ DOM์ ์ถ๊ฐ๋๊ฑฐ๋ ์ ๊ฑฐ๋ ๋ ์ ๋๋ฉ์ด์
์ ์ ์ฉํ ์ ์์ต๋๋ค. ์ค๋ฒจํธ๋ ๋ค์ํ ๋ด์ฅ ํธ๋์ง์
(fade, fly, slide, scale ๋ฑ)๊ณผ ์ด์ง ํจ์๋ฅผ ์ ๊ณตํฉ๋๋ค.
์ค์ํ ์ ์ ์ค๋ฒจํธ์ ํธ๋์ง์ ์ด CSS ์ ๋๋ฉ์ด์ ์ผ๋ก ์ปดํ์ผ๋๋ค๋ ๊ฒ๋๋ค. ์๋ฐ์คํฌ๋ฆฝํธ๋ก DOM์ ์ง์ ์กฐ์ํ๋ ๋ฐฉ์๋ณด๋ค ์ฑ๋ฅ์ด ๋ฐ์ด๋ฉ๋๋ค. ์ด๋ ํนํ ๋ชจ๋ฐ์ผ ๊ธฐ๊ธฐ์์ ์ค์ํ ์ฅ์ ์ ๋๋ค.
์ ๋ ์ด๋ฐ ์ค๋ฒจํธ์ ๋ด์ฅ API๋ง ๊ฐ์ง๊ณ ํ์ด์ง ํธ๋์ง์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ง๋ค์์ต๋๋ค. ๋ฆฌ์กํธ์๋ค๋ฉด ์ฌ๋ฌ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์กฐํฉํด์ผ ํ์ ๊ธฐ๋ฅ์ ์ค๋ฒจํธ์์๋ ๋ด์ฅ ๊ธฐ๋ฅ๋ง์ผ๋ก ๊ตฌํํ ์ ์์์ต๋๋ค.

ํตํฉ๋ ๊ฒฝํ์ ๊ฐ์น
์ค๋ฒจํธ์ ์ฌ์ธ์ ์ ๊ทผ ๋ฐฉ์์ ์ฌ๋ฌ ์ด์ ์ ์ ๊ณตํฉ๋๋ค.
- ํ์ต ๋น์ฉ ๊ฐ์: ์ฌ๋ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ API ๋์ ์ผ๊ด๋ ํจํด์ ๋ฐฐ์ธ ์ ์์ต๋๋ค.
- ์์ ๋ฒ๋ค ํฌ๊ธฐ: ์ธ๋ถ ์์กด์ฑ์ด ์ ์ด ์ต์ข ๋ฒ๋ค ํฌ๊ธฐ๊ฐ ์์์ง๋๋ค.
- ๊ฐ๋ฐ ๊ฒฝํ ํฅ์: ๋๊ตฌ ์ ํ์ ๊ณ ๋ฏผํ๋ ๋์ ๋น์ฆ๋์ค ๋ก์ง์ ์ง์คํ ์ ์์ต๋๋ค.
- ์ต์ ํ๋ ์ฑ๋ฅ: ๊ฐ ๊ธฐ๋ฅ์ด ์๋ก ์ ์กฐํ๋์ด ์ต์ ์ ์ฑ๋ฅ์ ๋ฐํํฉ๋๋ค.
- ์ฝ๋๋ฒ ์ด์ค ์ผ๊ด์ฑ: ๋ชจ๋ ํ์์ด ๋์ผํ ํจํด์ ๋ฐ๋ฅด๋ฏ๋ก ์ฝ๋ ์ผ๊ด์ฑ์ด ํฅ์๋ฉ๋๋ค.
ํนํ ํ๋ ์์ํฌ๊ฐ ๋ง์ ๋ถ๋ถ์ ๋ฐฉํฅ์ ์ ํด์ฃผ๊ธฐ ๋๋ฌธ์, ๋ชจ๋ ํ๋ก์ ํธ๊ฐ ๋น์ทํ ์ฝ๋ ํจํด์ ๊ฐ๊ฒ ๋์ด ์ ์ง๋ณด์์ฑ์ด ํฌ๊ฒ ํฅ์๋ฉ๋๋ค. ์๋ก์ด ๊ฐ๋ฐ์๊ฐ ํ์ ํฉ๋ฅํ๋๋ผ๋ ๊ธฐ์กด ์ฝ๋๋ฒ ์ด์ค๋ฅผ ๋น ๋ฅด๊ฒ ์ดํดํ ์ ์์ต๋๋ค.
๋ ๋ฆฝ์ ์ธ ํจํค์ง๋ค์ ์กฐํฉํด ๊ตฌ์ฑํ๋ ๋ฆฌ์กํธ ์ํ๊ณ์ "LEGO ๋ธ๋ก" ์ ๊ทผ ๋ฐฉ์๋ ์ฅ์ ์ด ์์ง๋ง, ์ค๋ฒจํธ์ "์ฌ์ธ์" ์ ๊ทผ ๋ฐฉ์์ ํนํ ๋น ๋ฅธ ๊ฐ๋ฐ๊ณผ ์ผ๊ด๋ ์ฌ์ฉ์ ๊ฒฝํ์ ์ํ๋ ํ์๊ฒ ๋งค๋ ฅ์ ์ธ ์ ํ์ ๋๋ค. ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์์กดํ์ง ์๊ณ ๋ ๋ชจ๋ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ํ์ํ ๋๋ถ๋ถ์ ๊ธฐ๋ฅ์ ๋ด์ฅํ๊ณ ์์ด, ๊ฐ๋ฐ์๋ "๋ฌด์์ ์ฌ์ฉํ ์ง"๊ฐ ์๋ "์ด๋ป๊ฒ ๊ตฌํํ ์ง"์ ์ง์คํ ์ ์์ต๋๋ค.
โ์ค๋ฒจํธ๋ฅผ ์ฌ๋ํด ์ฃผ์ธ์โ
์ด๋ฒ ๊ธ์์ ์ดํด๋ณธ ๊ฒ์ฒ๋ผ, ์ค๋ฒจํธ๋ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ ๋ค์ํ ๋ฌธ์ ๋ฅผ ํ์ ์ ์ธ ๋ฐฉ์์ผ๋ก ํด๊ฒฐํฉ๋๋ค. ์๊ทธ๋ ํจํด์ ํตํ ์ธ๋ฐํ ๋ฐ์์ฑ, ์ง์ ์ ์ธ ์ํ ๋ณ์ด๋ฅผ ํ์ฉํ๋ ์ง๊ด์ ์ธ ๋ฌธ๋ฒ, ๊ทธ๋ฆฌ๊ณ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ด๋ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ์ฌ์ธ์ ์ ๊ทผ ๋ฐฉ์์ ๊ฐ๋ฐ์ ๊ฒฝํ์ ํฌ๊ฒ ํฅ์์ํต๋๋ค.
์ค๋ฒจํธ๋ฅผ ์์ํ๋ ๊ฒ์ ์๊ฐ๋ณด๋ค ์ฌ์ด๋ฐ์. ๋ถ๋ด ์์ด ์๋ํด ๋ณผ ์ ์๋ ๋ช ๊ฐ์ง ๋ฐฉ๋ฒ์ ์๊ฐํด ๋ณผ๊ฒ์.
์์ ํ๋ก์ ํธ๋ถํฐ ์์ํ๊ธฐ
- ์ด๋๋ฏผ ํ์ด์ง: ๋ฉ์ธ ์๋น์ค๋ ๊ธฐ์กด ํ๋ ์์ํฌ๋ก ์ ์งํ๋ฉด์, ์ด๋๋ฏผ ๋์๋ณด๋๋ฅผ ์ค๋ฒจํธ๋ก ๊ตฌํํด ๋ณด์ธ์. ๋ด๋ถ ๋๊ตฌ๋ ์คํ์ ์ธ ๊ธฐ์ ์ ์ ์ฉํ๊ธฐ์ ๋ถ๋ด์ด ์ ์ต๋๋ค.
- ๊ฐ์ธ ๋ธ๋ก๊ทธ๋ ํฌํธํด๋ฆฌ์ค: ์์ ์ ๋ธ๋ก๊ทธ๋ ํฌํธํด๋ฆฌ์ค ์ฌ์ดํธ๋ฅผ ์ค๋ฒจํธ๋ก ๋ง๋ค์ด๋ณด์ธ์. ๋งํฌ๋ค์ด ๊ธฐ๋ฐ ๋ธ๋ก๊ทธ๋ SvelteKit๊ณผ ํนํ ๊ถํฉ์ด ์ข์ต๋๋ค.
- ๋ง์ดํฌ๋ก ์๋น์ค๋ ์์ ฏ: ๊ธฐ์กด ์ฑ์ ์ถ๊ฐํ๋ ์์ ๊ธฐ๋ฅ(๋ด์ค๋ ํฐ ๊ตฌ๋ ํผ, ์ฑํ ์์ ฏ ๋ฑ)์ ์ค๋ฒจํธ๋ก ๊ตฌํํด ๋ณผ ์ ์์ต๋๋ค.
๋ฐฑ์๋ ๊ฐ๋ฐ์๋ฅผ ์ํ ํ๋ก ํธ์๋ ์ ๋ฌธ
๋ฐฑ์๋๋ฅผ ์ฃผ๋ก ๋ค๋ฃจ๋ ๊ฐ๋ฐ์๊ฐ ํ๋ก ํธ์๋๋ฅผ ๋ฐฐ์ฐ๊ณ ์ถ๋ค๋ฉด, ์ค๋ฒจํธ๋ ํ๋ฅญํ ์ ํ์ ๋๋ค. ๋ณต์กํ ๊ฐ๋ ์ด๋ ๋ณด์ผ๋ฌํ๋ ์ดํธ ์ฝ๋ ์์ด๋ ์ง๊ด์ ์ผ๋ก UI๋ฅผ ๊ตฌํํ ์ ์์ด ์ง์ ์ฅ๋ฒฝ์ด ๋ฎ์ต๋๋ค. ์๋ฐ์คํฌ๋ฆฝํธ ๊ธฐ๋ณธ ๋ฌธ๋ฒ์ ๊ฐ๊น์ด ์ค๋ฒจํธ์ ์ ๊ทผ ๋ฐฉ์์ ๋ฐฑ์๋ ๊ฐ๋ฐ์๋ค์ด ๋น ๋ฅด๊ฒ ์ ์ํ ์ ์๊ฒ ํด์ค๋๋ค. HTML, CSS, JavaScript๋ฅผ ์์ฐ์ค๋ฝ๊ฒ ํตํฉํ๋ ๋ฐฉ์๋ ์ด๋ณด์์๊ฒ ์น์ํ๊ฒ ๋ค๊ฐ์ค๊ณ ์.
ํ์ต ์๋ฃ
์ค๋ฒจํธ๋ฅผ ๋ฐฐ์ฐ๋ ๋ฐ ๋์์ด ๋ ์๋ฃ๋ฅผ ์๊ฐํฉ๋๋ค.

๋ง์น๋ฉฐ
๊ธฐ์ ์ ์ธ๊ณ๋ ๋์์์ด ์งํํ๊ณ ์์ด์, ๊ฐ๋ฐ์๋ผ๋ฉด ๋ค์ํ ์ ๊ทผ ๋ฐฉ์์ ๊ฒฝํํ๊ณ ์ ์ฐํ๊ฒ ์ดํดํ๋ ์์ธ๊ฐ ์ค์ํฉ๋๋ค. ํนํ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ ๋ณํ๊ฐ ๋น ๋ฅด๊ธฐ ๋๋ฌธ์ ์๋ก์ด ํจ๋ฌ๋ค์์ ์ด๋ฆฐ ๋ง์์ผ๋ก ๋ฐ์๋ค์ด๋ ๊ฒ์ด ํ์ํ์ฃ . ๊ทธ๋ฐ ์๋ฏธ์์ ์ค๋ฒจํธ๋ฅผ ๋ฐฐ์ฐ๋ ๊ณผ์ ์ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์ ๋ณธ์ง์ ์ธ ๋ฌธ์ ๋ฅผ ์๋ค๋ฅด๊ฒ ๋ฐ๋ผ๋ณผ ์ ์๋ ์ข์ ๊ธฐํ์ ๋๋ค. ์์ ํ๋ก์ ํธ๋ถํฐ ์ฒ์ฒํ ์์ํ๋ฉด์ ์ง์ ๊ฒฝํํด ๋ณด๋ฉด, ์ค๋ฒจํธ๋ง์ ๊น์ ๋งค๋ ฅ์ ๋์ฑ ์์ํ๊ฒ ๋๋ ์ ์์ ๊ฒ๋๋ค.