์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐Ÿš€ DOM ์กฐ์ž‘๊ณผ ๊ฐ€์ƒ DOM์˜ ์„ฑ๋Šฅ ์ฐจ์ด ์™„๋ฒฝ ๋ถ„์„: ์›น ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ์ตœ์ ํ™” ์ „๋žต ๊ฐ€์ด๋“œ

๐Ÿš€ DOM ์กฐ์ž‘๊ณผ ๊ฐ€์ƒ DOM์˜ ์„ฑ๋Šฅ ์ฐจ์ด ์™„๋ฒฝ ๋ถ„์„: ์›น ๊ฐœ๋ฐœ์ž๋ฅผ ์œ„ํ•œ ์ตœ์ ํ™” ์ „๋žต ๊ฐ€์ด๋“œ

์‹ค์ „์—์„œ ๋ฐ”๋กœ ์จ๋จน๋Š” DOM ์„ฑ๋Šฅ ์ตœ์ ํ™” ๋…ธํ•˜์šฐ ๐Ÿ’ก

์•ˆ๋…•! ์˜ค๋Š˜์€ ์›น ๊ฐœ๋ฐœํ•˜๋ฉด์„œ ๋ˆ„๊ตฌ๋‚˜ ํ•œ ๋ฒˆ์ฏค์€ ๊ณ ๋ฏผํ•ด๋ดค์„ ์ฃผ์ œ๋ฅผ ๊ฐ€์ง€๊ณ  ์™”์–ด. ๋ฐ”๋กœ DOM ์กฐ์ž‘๊ณผ ๊ฐ€์ƒ DOM์˜ ์„ฑ๋Šฅ ์ฐจ์ด์— ๋Œ€ํ•œ ์ด์•ผ๊ธฐ์•ผ. ๐Ÿ˜Š

์†”์งํžˆ ๋งํ•˜๋ฉด, ์ฒ˜์Œ JavaScript ๋ฐฐ์šธ ๋•Œ๋Š” "๊ทธ๋ƒฅ document.getElementById() ์“ฐ๋ฉด ๋˜๋Š” ๊ฑฐ ์•„๋ƒ?"๋ผ๊ณ  ์ƒ๊ฐํ–ˆ์—ˆ์–ด. ๊ทผ๋ฐ ํ”„๋กœ์ ํŠธ ๊ทœ๋ชจ๊ฐ€ ์ปค์ง€๊ณ , ์‚ฌ์šฉ์ž๊ฐ€ ๋Š˜์–ด๋‚˜๋ฉด์„œ ์„ฑ๋Šฅ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ์‹œ์ž‘ํ•˜๋”๋ผ๊ณ . ํŽ˜์ด์ง€๊ฐ€ ๋ฒ„๋ฒ…๊ฑฐ๋ฆฌ๊ณ , ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด ๋–จ์–ด์ง€๋Š” ๊ฑธ ๋ณด๋ฉด์„œ "์•„, ์ด๊ฒŒ ๋ฌธ์ œ๊ตฌ๋‚˜!"๋ผ๊ณ  ๊นจ๋‹ฌ์•˜์ง€.

React, Vue ๊ฐ™์€ ํ”„๋ ˆ์ž„์›Œํฌ๋“ค์ด ์™œ ๊ฐ€์ƒ DOM์„ ์‚ฌ์šฉํ•˜๋Š”์ง€, ์‹ค์ œ๋กœ ์„ฑ๋Šฅ ์ฐจ์ด๊ฐ€ ์–ผ๋งˆ๋‚˜ ๋‚˜๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ์šฐ๋ฆฌ๊ฐ€ ์‹ค๋ฌด์—์„œ ์–ด๋–ป๊ฒŒ ์ตœ์ ํ™”ํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ์นœ๊ตฌํ•œํ…Œ ์„ค๋ช…ํ•˜๋“ฏ์ด ํŽธํ•˜๊ฒŒ ํ’€์–ด๋ณผ๊ฒŒ! ๐ŸŽฏ
DOM vs Virtual DOM Real DOM Direct Manipulation Slow Reflow Heavy Performance Virtual DOM Diff Algorithm Batch Updates Optimized Performance Real DOM: ์ง์ ‘ ์กฐ์ž‘, ๋А๋ฆฐ ๋ Œ๋”๋ง Virtual DOM: ์ตœ์ ํ™”๋œ ์—…๋ฐ์ดํŠธ

๐ŸŽญ DOM์ด ๋ญ”๋ฐ? ๊ธฐ๋ณธ๋ถ€ํ„ฐ ์ œ๋Œ€๋กœ ์•Œ์•„๋ณด์ž

๋จผ์ € DOM(Document Object Model)์ด ์ •ํ™•ํžˆ ๋ญ”์ง€๋ถ€ํ„ฐ ์งš๊ณ  ๋„˜์–ด๊ฐ€์ž. ๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด "HTML์ด๋ž‘ ๊ฐ™์€ ๊ฑฐ ์•„๋‹ˆ์•ผ?"๋ผ๊ณ  ์ƒ๊ฐํ•˜๋Š”๋ฐ, ์‚ฌ์‹ค์€ ์ข€ ๋‹ฌ๋ผ! ๐Ÿค”

DOM์˜ ์ •์ฒด DOM์€ HTML ๋ฌธ์„œ๋ฅผ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์ ์œผ๋กœ ์ ‘๊ทผํ•˜๊ณ  ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๋Š” ์ธํ„ฐํŽ˜์ด์Šค์•ผ. ์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด, ๋ธŒ๋ผ์šฐ์ €๊ฐ€ HTML์„ ์ฝ๊ณ  ๋‚˜์„œ ๋งŒ๋“ค์–ด๋‚ด๋Š” "ํŠธ๋ฆฌ ๊ตฌ์กฐ์˜ ๊ฐ์ฒด ๋ชจ๋ธ"์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋ผ. ๋งˆ์น˜ ๊ฐ€๊ณ„๋„์ฒ˜๋Ÿผ ๋ถ€๋ชจ-์ž์‹ ๊ด€๊ณ„๋กœ ์—ฐ๊ฒฐ๋˜์–ด ์žˆ์ง€!
์˜ˆ๋ฅผ ๋“ค์–ด๋ณผ๊ฒŒ. ์ด๋Ÿฐ ๊ฐ„๋‹จํ•œ HTML์ด ์žˆ๋‹ค๊ณ  ํ•ด๋ณด์ž:
<div id="container">
  <h1>์•ˆ๋…•ํ•˜์„ธ์š”</h1>
  <p>DOM ๊ณต๋ถ€ ์ค‘์ž…๋‹ˆ๋‹ค</p>
</div>
๋ธŒ๋ผ์šฐ์ €๋Š” ์ด๊ฑธ ์ฝ๊ณ  DOM ํŠธ๋ฆฌ๋ฅผ ๋งŒ๋“ค์–ด. ๊ทธ๋Ÿฌ๋ฉด ์šฐ๋ฆฌ๋Š” JavaScript๋กœ ์ด๋ ‡๊ฒŒ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์ง€:
const container = document.getElementById('container');
const heading = container.querySelector('h1');
heading.textContent = '๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹ค'; // ํ…์ŠคํŠธ ๋ณ€๊ฒฝ
heading.style.color = 'blue'; // ์Šคํƒ€์ผ ๋ณ€๊ฒฝ

๐Ÿ” DOM ์กฐ์ž‘์ด ์™œ ๋А๋ฆด๊นŒ?

์—ฌ๊ธฐ์„œ ํ•ต์‹ฌ ์งˆ๋ฌธ! "๊ทธ๋ƒฅ ํ…์ŠคํŠธ ๋ฐ”๊พธ๋Š” ๊ฑด๋ฐ ๋ญ๊ฐ€ ๋А๋ ค?" ๐Ÿคทโ€โ™‚๏ธ

์‚ฌ์‹ค DOM ์ž์ฒด๋Š” ๊ทธ๋ ‡๊ฒŒ ๋А๋ฆฌ์ง€ ์•Š์•„. ๋ฌธ์ œ๋Š” DOM์„ ๋ณ€๊ฒฝํ–ˆ์„ ๋•Œ ์ผ์–ด๋‚˜๋Š” ํ›„์† ์ž‘์—…๋“ค์ด์•ผ. ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ•ด์•ผ ํ•˜๋Š” ์ผ๋“ค์„ ์ˆœ์„œ๋Œ€๋กœ ๋ณด๋ฉด:
๋ธŒ๋ผ์šฐ์ € ๋ Œ๋”๋ง ํŒŒ์ดํ”„๋ผ์ธ ๐Ÿ”„ 1. Recalculate Style (์Šคํƒ€์ผ ์žฌ๊ณ„์‚ฐ)
๋ณ€๊ฒฝ๋œ ์š”์†Œ์™€ ๊ด€๋ จ๋œ CSS ๊ทœ์น™๋“ค์„ ๋‹ค์‹œ ๊ณ„์‚ฐํ•ด.

2. Layout (Reflow)
์š”์†Œ์˜ ํฌ๊ธฐ๋‚˜ ์œ„์น˜๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด, ์˜ํ–ฅ๋ฐ›๋Š” ๋ชจ๋“  ์š”์†Œ๋“ค์˜ ์œ„์น˜๋ฅผ ๋‹ค์‹œ ๊ณ„์‚ฐํ•ด์•ผ ํ•ด. ์ด๊ฒŒ ์ œ์ผ ๋ฌด๊ฑฐ์šด ์ž‘์—…์ด์•ผ! ๐Ÿ˜ฐ

3. Paint (Repaint)
ํ™”๋ฉด์— ์‹ค์ œ๋กœ ํ”ฝ์…€์„ ๊ทธ๋ฆฌ๋Š” ์ž‘์—…. ์ƒ‰์ƒ, ๊ทธ๋ฆผ์ž, ํ…์ŠคํŠธ ๋“ฑ์„ ๋ Œ๋”๋งํ•ด.

4. Composite
์—ฌ๋Ÿฌ ๋ ˆ์ด์–ด๋ฅผ ํ•ฉ์„ฑํ•ด์„œ ์ตœ์ข… ํ™”๋ฉด์„ ๋งŒ๋“ค์–ด.
ํŠนํžˆ Layout ๋‹จ๊ณ„๊ฐ€ ๋ฌธ์ œ์•ผ. ํ•˜๋‚˜์˜ ์š”์†Œ๋ฅผ ๋ณ€๊ฒฝํ–ˆ๋Š”๋ฐ, ๊ทธ ๋ถ€๋ชจ๋‚˜ ํ˜•์ œ, ์ž์‹ ์š”์†Œ๋“ค๊นŒ์ง€ ๋ชจ๋‘ ์˜ํ–ฅ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๊ฑฐ๋“ . ๋งˆ์น˜ ๋„๋ฏธ๋…ธ์ฒ˜๋Ÿผ ๋ง์ด์•ผ! ๐ŸŽฒ

โš ๏ธ ์„ฑ๋Šฅ ํ‚ฌ๋Ÿฌ: Reflow๋ฅผ ์œ ๋ฐœํ•˜๋Š” ์ž‘์—…๋“ค

์‹ค์ œ๋กœ ์–ด๋–ค ์ž‘์—…๋“ค์ด Reflow๋ฅผ ์ผ์œผํ‚ค๋Š”์ง€ ์•Œ์•„๋ณด์ž:
์ž‘์—… ์œ ํ˜• ์˜ˆ์‹œ ์ฝ”๋“œ ์˜ํ–ฅ๋„
ํฌ๊ธฐ ๋ณ€๊ฒฝ element.style.width = '500px' โš ๏ธโš ๏ธโš ๏ธ ๋†’์Œ
์œ„์น˜ ๋ณ€๊ฒฝ element.style.top = '100px' โš ๏ธโš ๏ธโš ๏ธ ๋†’์Œ
ํด๋ž˜์Šค ์ถ”๊ฐ€/์ œ๊ฑฐ element.classList.add('active') โš ๏ธโš ๏ธ ์ค‘๊ฐ„
๋‚ด์šฉ ๋ณ€๊ฒฝ element.innerHTML = '...' โš ๏ธโš ๏ธ ์ค‘๊ฐ„
๋ ˆ์ด์•„์›ƒ ์†์„ฑ ์ฝ๊ธฐ element.offsetHeight โš ๏ธโš ๏ธโš ๏ธ ๋†’์Œ (๊ฐ•์ œ ๋™๊ธฐํ™”)
ํŠนํžˆ ์กฐ์‹ฌํ•ด์•ผ ํ•  ๊ฒŒ ๋ ˆ์ด์•„์›ƒ ์†์„ฑ์„ ์ฝ๋Š” ์ž‘์—…์ด์•ผ. ์˜ˆ๋ฅผ ๋“ค์–ด:
// ๐Ÿšซ ๋‚˜์œ ์˜ˆ: ๋ฐ˜๋ณต๋ฌธ ์•ˆ์—์„œ ๋ ˆ์ด์•„์›ƒ ์†์„ฑ ์ฝ๊ธฐ
for (let i = 0; i < elements.length; i++) {
  elements[i].style.width = elements[i].offsetWidth + 10 + 'px';
  // offsetWidth๋ฅผ ์ฝ์„ ๋•Œ๋งˆ๋‹ค ๊ฐ•์ œ๋กœ ๋ ˆ์ด์•„์›ƒ ๊ณ„์‚ฐ ๋ฐœ์ƒ!
}

// โœ… ์ข‹์€ ์˜ˆ: ๋จผ์ € ์ฝ๊ณ , ๋‚˜์ค‘์— ์“ฐ๊ธฐ
const widths = [];
for (let i = 0; i < elements.length; i++) {
  widths[i] = elements[i].offsetWidth; // ์ฝ๊ธฐ ๋‹จ๊ณ„
}
for (let i = 0; i < elements.length; i++) {
  elements[i].style.width = widths[i] + 10 + 'px'; // ์“ฐ๊ธฐ ๋‹จ๊ณ„
}
์ด๋Ÿฐ ์‹์œผ๋กœ ์ฝ๊ธฐ์™€ ์“ฐ๊ธฐ๋ฅผ ๋ถ„๋ฆฌํ•˜๋ฉด Reflow ํšŸ์ˆ˜๋ฅผ ํš๊ธฐ์ ์œผ๋กœ ์ค„์ผ ์ˆ˜ ์žˆ์–ด! ๐ŸŽฏ

โœจ ๊ฐ€์ƒ DOM์˜ ๋“ฑ์žฅ: ๊ฒŒ์ž„ ์ฒด์ธ์ €

์ž, ์ด์ œ ๊ฐ€์ƒ DOM(Virtual DOM)์ด ์™œ ๋‚˜์™”๋Š”์ง€ ์ดํ•ด๊ฐ€ ๋˜์ง€? ์ „ํ†ต์ ์ธ DOM ์กฐ์ž‘์˜ ์„ฑ๋Šฅ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ์•ผ! ๐Ÿš€

๐Ÿ’ก ๊ฐ€์ƒ DOM์˜ ํ•ต์‹ฌ ์•„์ด๋””์–ด "์‹ค์ œ DOM์„ ์ง์ ‘ ๊ฑด๋“œ๋ฆฌ์ง€ ๋ง๊ณ , ๋ฉ”๋ชจ๋ฆฌ ์ƒ์— ๊ฐ€๋ฒผ์šด ๋ณต์‚ฌ๋ณธ์„ ๋งŒ๋“ค์–ด์„œ ๊ฑฐ๊ธฐ์„œ ๋จผ์ € ๋ณ€๊ฒฝํ•˜์ž. ๊ทธ๋ฆฌ๊ณ  ๋ณ€๊ฒฝ์‚ฌํ•ญ์„ ๋ชจ์•„์„œ ํ•œ ๋ฒˆ์— ์‹ค์ œ DOM์— ๋ฐ˜์˜ํ•˜์ž!"
React๋ฅผ ๋งŒ๋“  Facebook ํŒ€์ด ์ด ๊ฐœ๋…์„ ๋Œ€์ค‘ํ™”์‹œ์ผฐ์–ด. ๋ฌผ๋ก  Vue, Svelte ๊ฐ™์€ ๋‹ค๋ฅธ ํ”„๋ ˆ์ž„์›Œํฌ๋“ค๋„ ๋น„์Šทํ•œ(๋˜๋Š” ๋” ๋ฐœ์ „๋œ) ๊ฐœ๋…์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์ง€! ๐Ÿ˜Š

๐ŸŽจ ๊ฐ€์ƒ DOM์€ ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ• ๊นŒ?

๋‹จ๊ณ„๋ณ„๋กœ ์ž์„ธํžˆ ์•Œ์•„๋ณด์ž:
๊ฐ€์ƒ DOM ์ž‘๋™ ์›๋ฆฌ (4๋‹จ๊ณ„) Step 1: ๊ฐ€์ƒ DOM ํŠธ๋ฆฌ ์ƒ์„ฑ ๐ŸŒณ
์‹ค์ œ DOM์˜ ๊ฐ€๋ฒผ์šด JavaScript ๊ฐ์ฒด ๋ฒ„์ „์„ ๋งŒ๋“ค์–ด. ์ด๊ฑด ๊ทธ๋ƒฅ ์ผ๋ฐ˜ ๊ฐ์ฒด๋ผ์„œ ์กฐ์ž‘์ด ์—„์ฒญ ๋นจ๋ผ!

Step 2: ์ƒํƒœ ๋ณ€๊ฒฝ ๊ฐ์ง€ ๐Ÿ‘€
๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด, ์ƒˆ๋กœ์šด ๊ฐ€์ƒ DOM ํŠธ๋ฆฌ๋ฅผ ๋งŒ๋“ค์–ด.

Step 3: Diffing ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์‹คํ–‰ ๐Ÿ”
์ด์ „ ๊ฐ€์ƒ DOM๊ณผ ์ƒˆ๋กœ์šด ๊ฐ€์ƒ DOM์„ ๋น„๊ตํ•ด์„œ ์ •ํ™•ํžˆ ์–ด๋–ค ๋ถ€๋ถ„์ด ๋ฐ”๋€Œ์—ˆ๋Š”์ง€ ์ฐพ์•„๋‚ด.

Step 4: ์ตœ์†Œํ•œ์˜ ๋ณ€๊ฒฝ์‚ฌํ•ญ๋งŒ ์‹ค์ œ DOM์— ๋ฐ˜์˜ โšก
๋ณ€๊ฒฝ๋œ ๋ถ€๋ถ„๋งŒ ๊ณจ๋ผ์„œ ์‹ค์ œ DOM์„ ์—…๋ฐ์ดํŠธํ•ด. ์ด๊ฑธ "Reconciliation(์žฌ์กฐ์ •)"์ด๋ผ๊ณ  ๋ถˆ๋Ÿฌ.
์ฝ”๋“œ๋กœ ๋ณด๋ฉด ๋” ์ดํ•ด๊ฐ€ ์‰ฌ์šธ ๊ฑฐ์•ผ. React ์˜ˆ์‹œ๋ฅผ ๋“ค์–ด๋ณผ๊ฒŒ:
// React ์ปดํฌ๋„ŒํŠธ
function TodoList({ todos }) {
  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  );
}

// todos ๋ฐฐ์—ด์ด ๋ณ€๊ฒฝ๋˜๋ฉด?
// 1. ์ƒˆ๋กœ์šด ๊ฐ€์ƒ DOM ํŠธ๋ฆฌ ์ƒ์„ฑ
// 2. ์ด์ „ ํŠธ๋ฆฌ์™€ ๋น„๊ต (Diffing)
// 3. ๋ณ€๊ฒฝ๋œ <li> ์š”์†Œ๋งŒ ์‹ค์ œ DOM์—์„œ ์—…๋ฐ์ดํŠธ!
๋งŒ์•ฝ 10๊ฐœ์˜ todo ์ค‘์—์„œ ํ•˜๋‚˜๋งŒ ๋ฐ”๋€Œ์—ˆ๋‹ค๋ฉด? ๊ฐ€์ƒ DOM์€ ๊ทธ ํ•˜๋‚˜๋งŒ ์‹ค์ œ DOM์—์„œ ์—…๋ฐ์ดํŠธํ•ด. ๋‚˜๋จธ์ง€ 9๊ฐœ๋Š” ๊ฑด๋“œ๋ฆฌ์ง€๋„ ์•Š์•„! ์ด๊ฒŒ ๋ฐ”๋กœ ํšจ์œจ์„ฑ์˜ ๋น„๋ฐ€์ด์•ผ. ๐ŸŽฏ

๐Ÿ”ฌ Diffing ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๊นŠ์ด ํŒŒํ—ค์น˜๊ธฐ

React์˜ Diffing ์•Œ๊ณ ๋ฆฌ์ฆ˜์€ ์ •๋ง ๋˜‘๋˜‘ํ•ด. ๋ช‡ ๊ฐ€์ง€ ํœด๋ฆฌ์Šคํ‹ฑ(๊ฒฝํ—˜์  ๊ทœ์น™)์„ ์‚ฌ์šฉํ•ด์„œ O(nยณ) ๋ณต์žก๋„๋ฅผ O(n)์œผ๋กœ ์ค„์˜€๊ฑฐ๋“ ! ๐Ÿ˜ฒ

ํ•ต์‹ฌ ํœด๋ฆฌ์Šคํ‹ฑ 3๊ฐ€์ง€:

1. ๋‹ค๋ฅธ ํƒ€์ž…์˜ ์š”์†Œ๋Š” ์™„์ „ํžˆ ๋‹ค๋ฅธ ํŠธ๋ฆฌ๋ฅผ ๋งŒ๋“ ๋‹ค <div>๊ฐ€ <span>์œผ๋กœ ๋ฐ”๋€Œ๋ฉด? ์ž์‹ ์š”์†Œ๋“ค๊นŒ์ง€ ์ „๋ถ€ ์ƒˆ๋กœ ๋งŒ๋“ค์–ด. ๋น„๊ตํ•  ํ•„์š” ์—†์ด!

2. key prop์„ ํ†ตํ•ด ์ž์‹ ์š”์†Œ์˜ ๋ณ€๊ฒฝ์„ ์ถ”์ ํ•œ๋‹ค

๋ฆฌ์ŠคํŠธ ๋ Œ๋”๋งํ•  ๋•Œ key๋ฅผ ์“ฐ๋ผ๊ณ  ํ•˜๋Š” ์ด์œ ๊ฐ€ ๋ฐ”๋กœ ์ด๊ฑฐ์•ผ. key๊ฐ€ ์žˆ์œผ๋ฉด ์–ด๋–ค ์š”์†Œ๊ฐ€ ์ถ”๊ฐ€/์‚ญ์ œ/์ด๋™ํ–ˆ๋Š”์ง€ ์ •ํ™•ํžˆ ์•Œ ์ˆ˜ ์žˆ์–ด!

3. ๊ฐ™์€ ๋ ˆ๋ฒจ์˜ ์ž์‹๋“ค๋ผ๋ฆฌ๋งŒ ๋น„๊ตํ•œ๋‹ค

ํŠธ๋ฆฌ์˜ ๋‹ค๋ฅธ ๋ ˆ๋ฒจ์— ์žˆ๋Š” ์š”์†Œ๋“ค์€ ๋น„๊ตํ•˜์ง€ ์•Š์•„. ์ด๊ฒŒ ์„ฑ๋Šฅ์„ ํฌ๊ฒŒ ํ–ฅ์ƒ์‹œ์ผœ!
์‹ค์ œ ์˜ˆ์‹œ๋ฅผ ๋ณด์ž:
// ๐Ÿšซ ๋‚˜์œ ์˜ˆ: key ์—†์ด ๋ฆฌ์ŠคํŠธ ๋ Œ๋”๋ง
{items.map(item => (
  <div>{item.name}</div>
))}
// ํ•ญ๋ชฉ์ด ์ถ”๊ฐ€/์‚ญ์ œ๋˜๋ฉด ๋ชจ๋“  ์š”์†Œ๋ฅผ ๋‹ค์‹œ ๋ Œ๋”๋ง!

// โœ… ์ข‹์€ ์˜ˆ: key ์‚ฌ์šฉ
{items.map(item => (
  <div key={item.id}>{item.name}</div>
))}
// ๋ณ€๊ฒฝ๋œ ํ•ญ๋ชฉ๋งŒ ์ •ํ™•ํžˆ ์—…๋ฐ์ดํŠธ!

// ๐Ÿšซ ์ตœ์•…์˜ ์˜ˆ: index๋ฅผ key๋กœ ์‚ฌ์šฉ
{items.map((item, index) => (
  <div key={index}>{item.name}</div>
))}
// ์ˆœ์„œ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ๋ชจ๋“  ์š”์†Œ๊ฐ€ ๋‹ค์‹œ ๋ Œ๋”๋ง๋  ์ˆ˜ ์žˆ์–ด!
Virtual DOM Workflow Step 1 Create Virtual DOM Tree Step 2 State Change Detection Step 3 Diffing Algorithm Step 4 Patch Real DOM ๐Ÿ“ JavaScript Object ๐Ÿ”” Data Updated ๐Ÿ” Compare Changes โšก Minimal Update Result: ์ตœ์†Œํ•œ์˜ DOM ์กฐ์ž‘์œผ๋กœ ์ตœ๋Œ€์˜ ์„ฑ๋Šฅ ํšจ์œจ ๋‹ฌ์„ฑ! ๐ŸŽฏ

๐Ÿ“Š ์‹ค์ œ ์„ฑ๋Šฅ ๋น„๊ต: ์ˆซ์ž๋กœ ๋ณด๋Š” ์ฐจ์ด

์ด๋ก ์€ ์ถฉ๋ถ„ํžˆ ๋ดค์œผ๋‹ˆ, ์ด์ œ ์‹ค์ œ ์„ฑ๋Šฅ ์ฐจ์ด๋ฅผ ๋ฐ์ดํ„ฐ๋กœ ํ™•์ธํ•ด๋ณด์ž! ๐Ÿ”ฌ

๐Ÿงช ํ…Œ์ŠคํŠธ ์‹œ๋‚˜๋ฆฌ์˜ค

1000๊ฐœ์˜ ๋ฆฌ์ŠคํŠธ ์•„์ดํ…œ์„ ๋ Œ๋”๋งํ•˜๊ณ , ๊ทธ ์ค‘ 100๊ฐœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ์ƒํ™ฉ์„ ๊ฐ€์ •ํ•ด๋ณผ๊ฒŒ. ์‹ค๋ฌด์—์„œ ํ”ํžˆ ๋ณผ ์ˆ˜ ์žˆ๋Š” ์‹œ๋‚˜๋ฆฌ์˜ค์ง€!
๋ฐฉ์‹ ์ดˆ๊ธฐ ๋ Œ๋”๋ง ์—…๋ฐ์ดํŠธ (100๊ฐœ) ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ
์ˆœ์ˆ˜ DOM ์กฐ์ž‘ ~45ms ~120ms ๋‚ฎ์Œ
jQuery ~50ms ~130ms ์ค‘๊ฐ„
React (Virtual DOM) ~60ms ~25ms ์ค‘๊ฐ„-๋†’์Œ
Vue 3 (Virtual DOM) ~55ms ~22ms ์ค‘๊ฐ„
Svelte (์ปดํŒŒ์ผ) ~40ms ~20ms ๋‚ฎ์Œ
โš ๏ธ ์ฃผ์˜์‚ฌํ•ญ ์ด ์ˆ˜์น˜๋“ค์€ ๋Œ€๋žต์ ์ธ ์ฐธ๊ณ ์šฉ์ด์•ผ! ์‹ค์ œ ์„ฑ๋Šฅ์€ ๋ธŒ๋ผ์šฐ์ €, ํ•˜๋“œ์›จ์–ด, ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ, ์ตœ์ ํ™” ์ˆ˜์ค€์— ๋”ฐ๋ผ ํฌ๊ฒŒ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์–ด. ์ค‘์š”ํ•œ ๊ฑด ์ƒ๋Œ€์ ์ธ ๊ฒฝํ–ฅ์„ฑ์„ ์ดํ•ดํ•˜๋Š” ๊ฑฐ์•ผ.

๐Ÿ’ก ๊ฒฐ๊ณผ ๋ถ„์„

์žฌ๋ฏธ์žˆ๋Š” ํŒจํ„ด์ด ๋ณด์ด์ง€? ๐Ÿ˜Š

์ดˆ๊ธฐ ๋ Œ๋”๋ง ์ˆœ์ˆ˜ DOM ์กฐ์ž‘์ด๋‚˜ Svelte๊ฐ€ ๊ฐ€์žฅ ๋นจ๋ผ. ์™œ๋ƒํ•˜๋ฉด ๊ฐ€์ƒ DOM์„ ๋งŒ๋“œ๋Š” ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ์—†๊ฑฐ๋“ ! ๋‹จ์ˆœํžˆ HTML์„ ์ƒ์„ฑํ•˜๊ณ  DOM์— ์ถ”๊ฐ€ํ•˜๊ธฐ๋งŒ ํ•˜๋ฉด ๋˜๋‹ˆ๊นŒ.

์—…๋ฐ์ดํŠธ ์„ฑ๋Šฅ
์—ฌ๊ธฐ์„œ ๊ฐ€์ƒ DOM์˜ ์ง„๊ฐ€๊ฐ€ ๋“œ๋Ÿฌ๋‚˜! React์™€ Vue๋Š” ๋ณ€๊ฒฝ๋œ 100๊ฐœ๋งŒ ์ •ํ™•ํžˆ ์ฐพ์•„์„œ ์—…๋ฐ์ดํŠธํ•ด. ๋ฐ˜๋ฉด ์ˆœ์ˆ˜ DOM ์กฐ์ž‘์€ ๋น„ํšจ์œจ์ ์ธ ๋ฐฉ์‹์œผ๋กœ ์—…๋ฐ์ดํŠธํ•˜๋ฉด ํ›จ์”ฌ ๋А๋ ค์งˆ ์ˆ˜ ์žˆ์–ด.

๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ
๊ฐ€์ƒ DOM์€ ๋ฉ”๋ชจ๋ฆฌ์— ํŠธ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ์œ ์ง€ํ•ด์•ผ ํ•˜๋‹ˆ๊นŒ ์ถ”๊ฐ€ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด. ํ•˜์ง€๋งŒ ์š”์ฆ˜ ๋””๋ฐ”์ด์Šค์—์„œ๋Š” ํฐ ๋ฌธ์ œ๊ฐ€ ์•ˆ ๋ผ!

๐ŸŽฏ ์‹ค์ „ ๋ฒค์น˜๋งˆํฌ ์ฝ”๋“œ

์ง์ ‘ ํ…Œ์ŠคํŠธํ•ด๋ณผ ์ˆ˜ ์žˆ๋Š” ์ฝ”๋“œ๋ฅผ ๊ณต์œ ํ• ๊ฒŒ:
// ์ˆœ์ˆ˜ DOM ์กฐ์ž‘ ๋ฐฉ์‹
function pureDOM_update() {
  const start = performance.now();
  
  const list = document.getElementById('list');
  for (let i = 0; i < 100; i++) {
    const item = list.children[i];
    item.textContent = `Updated ${i}`;
    item.style.backgroundColor = '#f0f0f0';
  }
  
  const end = performance.now();
  console.log(`Pure DOM: ${end - start}ms`);
}

// React ๋ฐฉ์‹ (๊ฐ€์ƒ DOM)
function ReactComponent() {
  const [items, setItems] = useState(initialItems);
  
  const updateItems = () => {
    const start = performance.now();
    
    setItems(prevItems => 
      prevItems.map((item, i) => 
        i < 100 ? { ...item, text: `Updated ${i}`, bg: '#f0f0f0' } : item
      )
    );
    
    // React๋Š” ๋น„๋™๊ธฐ๋กœ ์—…๋ฐ์ดํŠธํ•˜๋ฏ€๋กœ useEffect์—์„œ ์ธก์ •
    requestAnimationFrame(() => {
      const end = performance.now();
      console.log(`React: ${end - start}ms`);
    });
  };
  
  return (
    <ul>
      {items.map(item => (
        <li key={item.id} style={{backgroundColor: item.bg}}>
          {item.text}
        </li>
      ))}
    </ul>
  );
}
์‹ค์ œ๋กœ ๋Œ๋ ค๋ณด๋ฉด ์ฐจ์ด๋ฅผ ์ฒด๊ฐํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ! ํŠนํžˆ ์—…๋ฐ์ดํŠธ ํšŸ์ˆ˜๊ฐ€ ๋งŽ์•„์งˆ์ˆ˜๋ก ๊ฐ€์ƒ DOM์˜ ์žฅ์ ์ด ๋”์šฑ ๋‘๋“œ๋Ÿฌ์ ธ. ๐Ÿš€

๐Ÿ› ๏ธ ์‹ค์ „ ์ตœ์ ํ™” ์ „๋žต: DOM ์กฐ์ž‘ ํŽธ

์ž, ์ด์ œ ์‹ค๋ฌด์—์„œ ๋ฐ”๋กœ ์จ๋จน์„ ์ˆ˜ ์žˆ๋Š” ์ตœ์ ํ™” ๊ธฐ๋ฒ•๋“ค์„ ์•Œ์•„๋ณด์ž! ์ˆœ์ˆ˜ JavaScript๋กœ DOM์„ ๋‹ค๋ฃฐ ๋•Œ ์•Œ์•„์•ผ ํ•  ํ•ต์‹ฌ ์ „๋žต๋“ค์ด์•ผ. ๐Ÿ’ช

1๏ธโƒฃ DocumentFragment ํ™œ์šฉํ•˜๊ธฐ

์—ฌ๋Ÿฌ ๊ฐœ์˜ ์š”์†Œ๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ ๊ฐ€์žฅ ํšจ๊ณผ์ ์ธ ๋ฐฉ๋ฒ•์ด์•ผ!
// ๐Ÿšซ ๋‚˜์œ ์˜ˆ: ๋ฐ˜๋ณต๋ฌธ์—์„œ ์ง์ ‘ DOM์— ์ถ”๊ฐ€
const list = document.getElementById('list');
for (let i = 0; i < 1000; i++) {
  const li = document.createElement('li');
  li.textContent = `Item ${i}`;
  list.appendChild(li); // 1000๋ฒˆ์˜ Reflow ๋ฐœ์ƒ!
}

// โœ… ์ข‹์€ ์˜ˆ: DocumentFragment ์‚ฌ์šฉ
const list = document.getElementById('list');
const fragment = document.createDocumentFragment();

for (let i = 0; i < 1000; i++) {
  const li = document.createElement('li');
  li.textContent = `Item ${i}`;
  fragment.appendChild(li); // ๋ฉ”๋ชจ๋ฆฌ์—์„œ๋งŒ ์ž‘์—…
}

list.appendChild(fragment); // ๋‹จ 1๋ฒˆ์˜ Reflow!
์„ฑ๋Šฅ ํ–ฅ์ƒ: ์•ฝ 10~20๋ฐฐ! ์ง„์งœ ํฐ ์ฐจ์ด์•ผ. ๐Ÿ˜ฒ

2๏ธโƒฃ ๋ฐฐ์น˜ ์Šคํƒ€์ผ ๋ณ€๊ฒฝ

์Šคํƒ€์ผ์„ ์—ฌ๋Ÿฌ ๋ฒˆ ๋ณ€๊ฒฝํ•  ๋•Œ๋Š” ํ•œ ๋ฒˆ์— ๋ชจ์•„์„œ ์ ์šฉํ•˜์ž:
// ๐Ÿšซ ๋‚˜์œ ์˜ˆ: ๊ฐœ๋ณ„ ์Šคํƒ€์ผ ๋ณ€๊ฒฝ
element.style.width = '100px';   // Reflow
element.style.height = '100px';  // Reflow
element.style.border = '1px solid black'; // Reflow

// โœ… ์ข‹์€ ์˜ˆ 1: cssText ์‚ฌ์šฉ
element.style.cssText = 'width: 100px; height: 100px; border: 1px solid black;';

// โœ… ์ข‹์€ ์˜ˆ 2: ํด๋ž˜์Šค ์‚ฌ์šฉ (๊ฐ€์žฅ ๊ถŒ์žฅ!)
element.className = 'styled-box';

// CSS
// .styled-box {
//   width: 100px;
//   height: 100px;
//   border: 1px solid black;
// }
ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ๊ฐ€์žฅ ์ข‹์•„. CSS๋Š” ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ตœ์ ํ™”ํ•˜๊ธฐ ์‰ฝ๊ฑฐ๋“ ! ๐ŸŽจ

3๏ธโƒฃ ์š”์†Œ๋ฅผ ์ˆจ๊ธฐ๊ณ  ์ž‘์—…ํ•˜๊ธฐ

๋ณต์žกํ•œ DOM ์กฐ์ž‘์„ ํ•  ๋•Œ๋Š” ์š”์†Œ๋ฅผ ์ผ์‹œ์ ์œผ๋กœ ์ˆจ๊ธฐ๋Š” ๊ฒŒ ํšจ๊ณผ์ ์ด์•ผ:
const element = document.getElementById('complex-element');

// ์š”์†Œ๋ฅผ ์ˆจ๊น€ (display: none์€ ๋ ˆ์ด์•„์›ƒ์—์„œ ์ œ๊ฑฐ)
element.style.display = 'none';

// ๋ณต์žกํ•œ DOM ์กฐ์ž‘ ์ˆ˜ํ–‰
for (let i = 0; i < 100; i++) {
  // ์—ฌ๋Ÿฌ ์ž์‹ ์š”์†Œ ์ถ”๊ฐ€/์ˆ˜์ •
  // ์ด ๋™์•ˆ์—๋Š” Reflow๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Œ!
}

// ๋‹ค์‹œ ํ‘œ์‹œ
element.style.display = 'block'; // ์ด๋•Œ ํ•œ ๋ฒˆ๋งŒ Reflow

4๏ธโƒฃ ์ฝ๊ธฐ์™€ ์“ฐ๊ธฐ ๋ถ„๋ฆฌํ•˜๊ธฐ

์•ž์—์„œ๋„ ์–ธ๊ธ‰ํ–ˆ์ง€๋งŒ, ์ •๋ง ์ค‘์š”ํ•ด์„œ ๋‹ค์‹œ ํ•œ๋ฒˆ! ๐Ÿ˜Š
// ๐Ÿšซ ๋‚˜์œ ์˜ˆ: ์ฝ๊ธฐ์™€ ์“ฐ๊ธฐ๊ฐ€ ์„ž์—ฌ์žˆ์Œ
const elements = document.querySelectorAll('.item');
elements.forEach(el => {
  const width = el.offsetWidth; // ์ฝ๊ธฐ โ†’ ๊ฐ•์ œ Reflow
  el.style.width = width * 1.5 + 'px'; // ์“ฐ๊ธฐ
  const height = el.offsetHeight; // ์ฝ๊ธฐ โ†’ ๊ฐ•์ œ Reflow
  el.style.height = height * 1.5 + 'px'; // ์“ฐ๊ธฐ
});

// โœ… ์ข‹์€ ์˜ˆ: ์ฝ๊ธฐ์™€ ์“ฐ๊ธฐ ๋ถ„๋ฆฌ
const elements = document.querySelectorAll('.item');
const dimensions = [];

// 1๋‹จ๊ณ„: ๋ชจ๋“  ์ฝ๊ธฐ ์ž‘์—…
elements.forEach(el => {
  dimensions.push({
    width: el.offsetWidth,
    height: el.offsetHeight
  });
});

// 2๋‹จ๊ณ„: ๋ชจ๋“  ์“ฐ๊ธฐ ์ž‘์—…
elements.forEach((el, i) => {
  el.style.width = dimensions[i].width * 1.5 + 'px';
  el.style.height = dimensions[i].height * 1.5 + 'px';
});

5๏ธโƒฃ ์ด๋ฒคํŠธ ์œ„์ž„ (Event Delegation)

์ˆ˜๋ฐฑ ๊ฐœ์˜ ์š”์†Œ์— ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋ฅผ ๋‹ฌ ๋•Œ ํ•„์ˆ˜ ๊ธฐ๋ฒ•์ด์•ผ!
// ๐Ÿšซ ๋‚˜์œ ์˜ˆ: ๊ฐ ์š”์†Œ์— ๊ฐœ๋ณ„ ๋ฆฌ์Šค๋„ˆ
const items = document.querySelectorAll('.item');
items.forEach(item => {
  item.addEventListener('click', handleClick);
});
// ๋ฉ”๋ชจ๋ฆฌ ๋‚ญ๋น„ + ๋™์  ์š”์†Œ ์ฒ˜๋ฆฌ ์–ด๋ ค์›€

// โœ… ์ข‹์€ ์˜ˆ: ๋ถ€๋ชจ์— ํ•˜๋‚˜์˜ ๋ฆฌ์Šค๋„ˆ
const list = document.getElementById('list');
list.addEventListener('click', (e) => {
  if (e.target.classList.contains('item')) {
    handleClick(e);
  }
});
// ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์  + ๋™์  ์š”์†Œ ์ž๋™ ์ฒ˜๋ฆฌ!
์ด๋ฒคํŠธ ์œ„์ž„์€ ์„ฑ๋Šฅ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ฝ”๋“œ ์œ ์ง€๋ณด์ˆ˜์—๋„ ์ข‹์•„. ๋‚˜์ค‘์— ์š”์†Œ๋ฅผ ์ถ”๊ฐ€ํ•ด๋„ ์ž๋™์œผ๋กœ ์ด๋ฒคํŠธ๊ฐ€ ์ž‘๋™ํ•˜๊ฑฐ๋“ ! ๐ŸŽฏ
๐Ÿ’ก ํ”„๋กœ ํŒ: requestAnimationFrame ํ™œ์šฉ

์• ๋‹ˆ๋ฉ”์ด์…˜์ด๋‚˜ ์Šคํฌ๋กค ์ด๋ฒคํŠธ ์ฒ˜๋ฆฌํ•  ๋•Œ๋Š” requestAnimationFrame์„ ์‚ฌ์šฉํ•˜์ž:
let ticking = false;

window.addEventListener('scroll', () => {
  if (!ticking) {
    requestAnimationFrame(() => {
      // ์Šคํฌ๋กค ์ฒ˜๋ฆฌ ๋กœ์ง
      updateScrollPosition();
      ticking = false;
    });
    ticking = true;
  }
});
๋ธŒ๋ผ์šฐ์ €์˜ ๋ฆฌํŽ˜์ธํŠธ ์ฃผ๊ธฐ์— ๋งž์ถฐ์„œ ์‹คํ–‰๋˜๋‹ˆ๊นŒ ํ›จ์”ฌ ๋ถ€๋“œ๋Ÿฌ์›Œ! ๐ŸŽฌ

6๏ธโƒฃ Debounce์™€ Throttle

์ด๋ฒคํŠธ๊ฐ€ ๋„ˆ๋ฌด ์ž์ฃผ ๋ฐœ์ƒํ•˜๋Š” ๊ฑธ ์ œ์–ดํ•˜๋Š” ๊ธฐ๋ฒ•์ด์•ผ:
// Debounce: ๋งˆ์ง€๋ง‰ ํ˜ธ์ถœ ํ›„ ์ผ์ • ์‹œ๊ฐ„์ด ์ง€๋‚˜์•ผ ์‹คํ–‰
function debounce(func, wait) {
  let timeout;
  return function executedFunction(...args) {
    clearTimeout(timeout);
    timeout = setTimeout(() => func(...args), wait);
  };
}

// ์‚ฌ์šฉ ์˜ˆ: ๊ฒ€์ƒ‰ ์ž…๋ ฅ
const searchInput = document.getElementById('search');
const debouncedSearch = debounce((value) => {
  // API ํ˜ธ์ถœ ๋“ฑ
  console.log('Searching for:', value);
}, 300);

searchInput.addEventListener('input', (e) => {
  debouncedSearch(e.target.value);
});

// Throttle: ์ผ์ • ์‹œ๊ฐ„๋งˆ๋‹ค ํ•œ ๋ฒˆ๋งŒ ์‹คํ–‰
function throttle(func, limit) {
  let inThrottle;
  return function(...args) {
    if (!inThrottle) {
      func(...args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

// ์‚ฌ์šฉ ์˜ˆ: ์Šคํฌ๋กค ์ด๋ฒคํŠธ
const throttledScroll = throttle(() => {
  console.log('Scroll position:', window.scrollY);
}, 100);

window.addEventListener('scroll', throttledScroll);
์–ธ์ œ ๋ญ˜ ์“ธ๊นŒ?
- Debounce: ๊ฒ€์ƒ‰ ์ž…๋ ฅ, ์œˆ๋„์šฐ ๋ฆฌ์‚ฌ์ด์ฆˆ (๋งˆ์ง€๋ง‰ ๋™์ž‘๋งŒ ์ค‘์š”ํ•  ๋•Œ)
- Throttle: ์Šคํฌ๋กค, ๋งˆ์šฐ์Šค ์ด๋™ (์ผ์ • ๊ฐ„๊ฒฉ์œผ๋กœ ๊ณ„์† ์‹คํ–‰ํ•ด์•ผ ํ•  ๋•Œ)

โšก ์‹ค์ „ ์ตœ์ ํ™” ์ „๋žต: ๊ฐ€์ƒ DOM ํŽธ

React๋‚˜ Vue๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋„ ์ตœ์ ํ™”๊ฐ€ ํ•„์š”ํ•ด! ๊ฐ€์ƒ DOM์ด ๋งŒ๋Šฅ์€ ์•„๋‹ˆ๊ฑฐ๋“ . ๐Ÿ˜…

1๏ธโƒฃ React ์ตœ์ ํ™” ๊ธฐ๋ฒ•

React.memo๋กœ ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง ๋ฐฉ์ง€
// ๐Ÿšซ ์ตœ์ ํ™” ์ „: ๋ถ€๋ชจ๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋˜๋ฉด ์ž์‹๋„ ๋ฌด์กฐ๊ฑด ๋ฆฌ๋ Œ๋”๋ง
function ChildComponent({ name, age }) {
  console.log('Child rendered!');
  return <div>{name} ({age})</div>;
}

// โœ… ์ตœ์ ํ™” ํ›„: props๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ ๋ฆฌ๋ Œ๋”๋ง
const ChildComponent = React.memo(({ name, age }) => {
  console.log('Child rendered!');
  return <div>{name} ({age})</div>;
});

// ๋” ์„ธ๋ฐ€ํ•œ ์ œ์–ด๊ฐ€ ํ•„์š”ํ•˜๋ฉด ๋‘ ๋ฒˆ์งธ ์ธ์ž ์‚ฌ์šฉ
const ChildComponent = React.memo(
  ({ name, age }) => {
    return <div>{name} ({age})</div>;
  },
  (prevProps, nextProps) => {
    // true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋ฆฌ๋ Œ๋”๋ง ์Šคํ‚ต
    return prevProps.name === nextProps.name && 
           prevProps.age === nextProps.age;
  }
);
useMemo์™€ useCallback ํ™œ์šฉ
function ExpensiveComponent({ data }) {
  // ๐Ÿšซ ๋‚˜์œ ์˜ˆ: ๋งค๋ฒˆ ์žฌ๊ณ„์‚ฐ
  const processedData = processLargeData(data);
  
  // โœ… ์ข‹์€ ์˜ˆ: data๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ ์žฌ๊ณ„์‚ฐ
  const processedData = useMemo(
    () => processLargeData(data),
    [data]
  );
  
  // ๐Ÿšซ ๋‚˜์œ ์˜ˆ: ๋งค๋ฒˆ ์ƒˆ๋กœ์šด ํ•จ์ˆ˜ ์ƒ์„ฑ
  const handleClick = () => {
    console.log('Clicked!');
  };
  
  // โœ… ์ข‹์€ ์˜ˆ: ํ•จ์ˆ˜ ์žฌ์‚ฌ์šฉ
  const handleClick = useCallback(() => {
    console.log('Clicked!');
  }, []); // ์˜์กด์„ฑ ๋ฐฐ์—ด์ด ๋น„์–ด์žˆ์œผ๋ฉด ํ•œ ๋ฒˆ๋งŒ ์ƒ์„ฑ
  
  return <ChildComponent data={processedData} onClick={handleClick} />;
}
โš ๏ธ ๊ณผ๋„ํ•œ ์ตœ์ ํ™” ์ฃผ์˜! useMemo์™€ useCallback๋„ ๋น„์šฉ์ด ๋“ค์–ด. ๊ฐ„๋‹จํ•œ ๊ณ„์‚ฐ์ด๋‚˜ ํ•จ์ˆ˜๋Š” ๊ทธ๋ƒฅ ๋‘๋Š” ๊ฒŒ ๋‚˜์„ ์ˆ˜ ์žˆ์–ด. ํ”„๋กœํŒŒ์ผ๋ง ๋จผ์ € ํ•˜๊ณ  ํ•„์š”ํ•œ ๊ณณ์—๋งŒ ์ ์šฉํ•˜์ž! ๐ŸŽฏ
๋ฆฌ์ŠคํŠธ ๊ฐ€์ƒํ™” (Virtualization) ์ˆ˜์ฒœ ๊ฐœ์˜ ์•„์ดํ…œ์„ ๋ Œ๋”๋งํ•  ๋•Œ๋Š” ๊ฐ€์ƒํ™”๊ฐ€ ํ•„์ˆ˜์•ผ!
// react-window ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ ์˜ˆ์‹œ
import { FixedSizeList } from 'react-window';

function VirtualizedList({ items }) {
  const Row = ({ index, style }) => (
    <div style={style}>
      {items[index].name}
    </div>
  );
  
  return (
    <FixedSizeList
      height={600}
      itemCount={items.length}
      itemSize={50}
      width="100%"
    >
      {Row}
    </FixedSizeList>
  );
}

// 10,000๊ฐœ ์•„์ดํ…œ๋„ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์Šคํฌ๋กค!
ํ™”๋ฉด์— ๋ณด์ด๋Š” ์•„์ดํ…œ๋งŒ ๋ Œ๋”๋งํ•˜๋‹ˆ๊นŒ ์„ฑ๋Šฅ์ด 100๋ฐฐ ์ด์ƒ ํ–ฅ์ƒ๋  ์ˆ˜ ์žˆ์–ด! ๐Ÿ˜ฒ

2๏ธโƒฃ Vue ์ตœ์ ํ™” ๊ธฐ๋ฒ•

v-once์™€ v-memo ๋””๋ ‰ํ‹ฐ๋ธŒ
<!-- v-once: ํ•œ ๋ฒˆ๋งŒ ๋ Œ๋”๋ง, ์ดํ›„ ์—…๋ฐ์ดํŠธ ๋ฌด์‹œ -->
<div v-once>
  <h1>{{ staticTitle }}</h1>
  <p>{{ staticContent }}</p>
</div>

<!-- v-memo: ์กฐ๊ฑด๋ถ€ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ (Vue 3.2+) -->
<div v-memo="[user.id, user.name]">
  <!-- user.id๋‚˜ user.name์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ ์—…๋ฐ์ดํŠธ -->
  <UserProfile :user="user" />
</div>
Computed vs Methods
export default {
  data() {
    return {
      items: [/* ๋งŽ์€ ๋ฐ์ดํ„ฐ */]
    };
  },
  
  // ๐Ÿšซ ๋‚˜์œ ์˜ˆ: methods๋Š” ๋งค๋ฒˆ ์‹คํ–‰
  methods: {
    filteredItems() {
      return this.items.filter(item => item.active);
    }
  },
  
  // โœ… ์ข‹์€ ์˜ˆ: computed๋Š” ์บ์‹ฑ๋จ
  computed: {
    filteredItems() {
      return this.items.filter(item => item.active);
    }
  }
}

// ํ…œํ”Œ๋ฆฟ์—์„œ
<div v-for="item in filteredItems" :key="item.id">
  {{ item.name }}
</div>
computed๋Š” ์˜์กดํ•˜๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ๋งŒ ์žฌ๊ณ„์‚ฐ๋˜๋‹ˆ๊นŒ ํ›จ์”ฌ ํšจ์œจ์ ์ด์•ผ! ๐Ÿš€

3๏ธโƒฃ ๊ณตํ†ต ์ตœ์ ํ™” ์ „๋žต

Key ์ตœ์ ํ™”
// ๐Ÿšซ ์ตœ์•…: key ์—†์Œ
<div v-for="item in items">{{ item.name }}</div>

// ๐Ÿšซ ๋‚˜์จ: index๋ฅผ key๋กœ ์‚ฌ์šฉ
<div v-for="(item, index) in items" :key="index">
  {{ item.name }}
</div>

// โœ… ์ข‹์Œ: ๊ณ ์œ ํ•œ ID ์‚ฌ์šฉ
<div v-for="item in items" :key="item.id">
  {{ item.name }}
</div>

// โœ… ์ตœ๊ณ : ๋ณตํ•ฉ key (ํ•„์š”ํ•œ ๊ฒฝ์šฐ)
<div v-for="item in items" :key="`${item.category}-${item.id}`">
  {{ item.name }}
</div>
์กฐ๊ฑด๋ถ€ ๋ Œ๋”๋ง ์ตœ์ ํ™”
<!-- v-if vs v-show -->

<!-- v-if: DOM์—์„œ ์™„์ „ํžˆ ์ œ๊ฑฐ/์ถ”๊ฐ€ (ํ† ๊ธ€ ๋น„์šฉ ๋†’์Œ) -->
<div v-if="isVisible">์ž์ฃผ ์•ˆ ๋ฐ”๋€Œ๋Š” ๋‚ด์šฉ</div>

<!-- v-show: display ์†์„ฑ๋งŒ ๋ณ€๊ฒฝ (ํ† ๊ธ€ ๋น„์šฉ ๋‚ฎ์Œ) -->
<div v-show="isVisible">์ž์ฃผ ๋ฐ”๋€Œ๋Š” ๋‚ด์šฉ</div>

<!-- ๊ทœ์น™: ์ž์ฃผ ํ† ๊ธ€๋˜๋ฉด v-show, ์•„๋‹ˆ๋ฉด v-if -->
์‹ค์ „ ํŒ: ์–ธ์ œ ๋ญ˜ ์“ธ๊นŒ? ๐Ÿค” v-if๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ:
- ์ดˆ๊ธฐ ๋ Œ๋”๋ง ๋น„์šฉ์„ ์ค„์ด๊ณ  ์‹ถ์„ ๋•Œ
- ์กฐ๊ฑด์ด ์ž์ฃผ ๋ณ€ํ•˜์ง€ ์•Š์„ ๋•Œ
- ์กฐ๊ฑด๋ถ€๋กœ ๋งŽ์€ ์ž์‹ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•  ๋•Œ

v-show๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ:
- ํ† ๊ธ€์ด ๋นˆ๋ฒˆํ•  ๋•Œ (ํƒญ, ๋ชจ๋‹ฌ ๋“ฑ)
- ์ดˆ๊ธฐ ๋ Œ๋”๋ง ๋น„์šฉ์ด ์ค‘์š”ํ•˜์ง€ ์•Š์„ ๋•Œ
- ๊ฐ„๋‹จํ•œ ์š”์†Œ์ผ ๋•Œ

๐Ÿ”ง ๊ฐœ๋ฐœ์ž ๋„๊ตฌ๋กœ ์„ฑ๋Šฅ ์ธก์ •ํ•˜๊ธฐ

์ตœ์ ํ™”ํ–ˆ๋‹ค๊ณ  ๋์ด ์•„๋‹ˆ์•ผ! ์‹ค์ œ๋กœ ๊ฐœ์„ ๋˜์—ˆ๋Š”์ง€ ์ธก์ •ํ•ด์•ผ์ง€. ๐Ÿ˜Š

1๏ธโƒฃ Chrome DevTools Performance ํƒญ

์‚ฌ์šฉ ๋ฐฉ๋ฒ•: 1. F12๋กœ ๊ฐœ๋ฐœ์ž ๋„๊ตฌ ์—ด๊ธฐ
2. Performance ํƒญ ์„ ํƒ
3. ๋…นํ™” ๋ฒ„ํŠผ(โ—) ํด๋ฆญ
4. ์ธก์ •ํ•˜๊ณ  ์‹ถ์€ ๋™์ž‘ ์ˆ˜ํ–‰
5. ์ •์ง€ ๋ฒ„ํŠผ ํด๋ฆญ
6. ๊ฒฐ๊ณผ ๋ถ„์„! ๐Ÿ“Š
์ฃผ๋ชฉํ•ด์•ผ ํ•  ์ง€ํ‘œ๋“ค:
์ง€ํ‘œ ์˜๋ฏธ ๋ชฉํ‘œ
FPS ์ดˆ๋‹น ํ”„๋ ˆ์ž„ ์ˆ˜ 60 FPS ์œ ์ง€
Scripting JavaScript ์‹คํ–‰ ์‹œ๊ฐ„ ์ตœ์†Œํ™”
Rendering ๋ ˆ์ด์•„์›ƒ, ํŽ˜์ธํŠธ ์‹œ๊ฐ„ ์ตœ์†Œํ™”
Painting ํ”ฝ์…€ ๊ทธ๋ฆฌ๊ธฐ ์‹œ๊ฐ„ ์ตœ์†Œํ™”
Idle ์œ ํœด ์‹œ๊ฐ„ ๋งŽ์„์ˆ˜๋ก ์ข‹์Œ

2๏ธโƒฃ React DevTools Profiler

React ์•ฑ์ด๋ผ๋ฉด ํ•„์ˆ˜ ๋„๊ตฌ์•ผ!
// ํ”„๋กœํŒŒ์ผ๋ง ์ฝ”๋“œ ์ถ”๊ฐ€
import { Profiler } from 'react';

function onRenderCallback(
  id, // ํ”„๋กœํŒŒ์ผ๋Ÿฌ ID
  phase, // "mount" ๋˜๋Š” "update"
  actualDuration, // ๋ Œ๋”๋ง์— ๊ฑธ๋ฆฐ ์‹œ๊ฐ„
  baseDuration, // ๋ฉ”๋ชจ์ด์ œ์ด์…˜ ์—†์ด ๊ฑธ๋ฆด ์‹œ๊ฐ„
  startTime, // ๋ Œ๋”๋ง ์‹œ์ž‘ ์‹œ๊ฐ„
  commitTime, // ์ปค๋ฐ‹ ์‹œ๊ฐ„
  interactions // ์ด ์—…๋ฐ์ดํŠธ์™€ ๊ด€๋ จ๋œ ์ƒํ˜ธ์ž‘์šฉ
) {
  console.log(`${id} took ${actualDuration}ms to render`);
}

function App() {
  return (
    <Profiler id="App" onRender={onRenderCallback}>
      <YourComponent />
    </Profiler>
  );
}

3๏ธโƒฃ Lighthouse ์‚ฌ์šฉํ•˜๊ธฐ

์ „์ฒด์ ์ธ ์„ฑ๋Šฅ ์ ์ˆ˜๋ฅผ ํ™•์ธํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด Lighthouse!
๐Ÿ’ก Lighthouse ์‹คํ–‰ ๋ฐฉ๋ฒ• 1. Chrome DevTools ์—ด๊ธฐ (F12)
2. Lighthouse ํƒญ ์„ ํƒ
3. "Generate report" ํด๋ฆญ
4. ์„ฑ๋Šฅ, ์ ‘๊ทผ์„ฑ, SEO ๋“ฑ ์ข…ํ•ฉ ์ ์ˆ˜ ํ™•์ธ! ๐ŸŽฏ

ํŠนํžˆ Performance ์ ์ˆ˜์™€ Opportunities ์„น์…˜์„ ์ฃผ๋ชฉํ•˜์ž. ๊ตฌ์ฒด์ ์ธ ๊ฐœ์„  ๋ฐฉ๋ฒ•์„ ์ œ์‹œํ•ด์ค˜!

4๏ธโƒฃ ์ปค์Šคํ…€ ์„ฑ๋Šฅ ์ธก์ •

์ง์ ‘ ์ฝ”๋“œ์—์„œ ์„ฑ๋Šฅ์„ ์ธก์ •ํ•  ์ˆ˜๋„ ์žˆ์–ด:
// Performance API ์‚ฌ์šฉ
const start = performance.now();

// ์ธก์ •ํ•˜๊ณ  ์‹ถ์€ ์ฝ”๋“œ
for (let i = 0; i < 1000; i++) {
  document.getElementById('list').appendChild(createItem(i));
}

const end = performance.now();
console.log(`Execution time: ${end - start}ms`);

// Performance Observer ์‚ฌ์šฉ (๋” ์ •๊ตํ•œ ์ธก์ •)
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log(`${entry.name}: ${entry.duration}ms`);
  }
});

observer.observe({ entryTypes: ['measure'] });

performance.mark('start-render');
// ๋ Œ๋”๋ง ์ฝ”๋“œ
performance.mark('end-render');
performance.measure('render-time', 'start-render', 'end-render');

๐ŸŽ“ ์‹ค์ „ ์ผ€์ด์Šค ์Šคํ„ฐ๋””

์ด๋ก ๋งŒ ๋ณด๋ฉด ์žฌ๋ฏธ์—†์œผ๋‹ˆ๊นŒ, ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ ๊ฒช์—ˆ๋˜ ์„ฑ๋Šฅ ๋ฌธ์ œ์™€ ํ•ด๊ฒฐ ๊ณผ์ •์„ ๊ณต์œ ํ• ๊ฒŒ! ๐Ÿ˜Š

๐Ÿ“ฑ ์ผ€์ด์Šค 1: ๋ฌดํ•œ ์Šคํฌ๋กค ํ”ผ๋“œ ์ตœ์ ํ™”

๋ฌธ์ œ ์ƒํ™ฉ SNS ํ”ผ๋“œ ๊ฐ™์€ ๋ฌดํ•œ ์Šคํฌ๋กค ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ–ˆ๋Š”๋ฐ, ์Šคํฌ๋กคํ• ์ˆ˜๋ก ์ ์  ๋А๋ ค์ง€๋Š” ํ˜„์ƒ ๋ฐœ์ƒ! 500๊ฐœ ์ •๋„ ๋กœ๋“œ๋˜๋ฉด ๋ฒ„๋ฒ…๊ฑฐ๋ฆฌ๊ธฐ ์‹œ์ž‘ํ–ˆ์–ด. ๐Ÿ˜ฐ
์›์ธ ๋ถ„์„:

1. ๋ชจ๋“  ์•„์ดํ…œ์ด DOM์— ๋‚จ์•„์žˆ์Œ (500๊ฐœ โ†’ 5000๊ฐœ๋กœ ์ฆ๊ฐ€)
2. ๊ฐ ์•„์ดํ…œ๋งˆ๋‹ค ์ด๋ฏธ์ง€ ๋กœ๋”ฉ
3. ์Šคํฌ๋กค ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋„ˆ๋ฌด ์ž์ฃผ ์‹คํ–‰๋จ

ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:
// 1. ๊ฐ€์ƒ ์Šคํฌ๋กค ๊ตฌํ˜„ (react-window ์‚ฌ์šฉ)
import { VariableSizeList } from 'react-window';
import InfiniteLoader from 'react-window-infinite-loader';

function FeedList({ items, loadMore, hasMore }) {
  const itemSizes = useRef({});
  
  const getItemSize = (index) => {
    return itemSizes.current[index] || 200; // ๊ธฐ๋ณธ ๋†’์ด
  };
  
  const setItemSize = (index, size) => {
    itemSizes.current[index] = size;
  };
  
  return (
    <InfiniteLoader
      isItemLoaded={(index) => index < items.length}
      itemCount={hasMore ? items.length + 1 : items.length}
      loadMoreItems={loadMore}
    >
      {({ onItemsRendered, ref }) => (
        <VariableSizeList
          height={window.innerHeight}
          itemCount={items.length}
          itemSize={getItemSize}
          onItemsRendered={onItemsRendered}
          ref={ref}
          width="100%"
        >
          {({ index, style }) => (
            <FeedItem
              item={items[index]}
              style={style}
              onResize={(height) => setItemSize(index, height)}
            />
          )}
        </VariableSizeList>
      )}
    </InfiniteLoader>
  );
}

// 2. ์ด๋ฏธ์ง€ Lazy Loading
function FeedItem({ item, style, onResize }) {
  const imgRef = useRef();
  
  useEffect(() => {
    const observer = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            const img = entry.target;
            img.src = img.dataset.src; // ์‹ค์ œ ์ด๋ฏธ์ง€ ๋กœ๋“œ
            observer.unobserve(img);
          }
        });
      },
      { rootMargin: '50px' } // 50px ์ „์— ๋ฏธ๋ฆฌ ๋กœ๋“œ
    );
    
    if (imgRef.current) {
      observer.observe(imgRef.current);
    }
    
    return () => observer.disconnect();
  }, []);
  
  return (
    <div style={style}>
      <img
        ref={imgRef}
        data-src={item.imageUrl}
        alt={item.title}
        onLoad={(e) => onResize(e.target.height)}
      />
      <p>{item.content}</p>
    </div>
  );
}

// 3. ์Šคํฌ๋กค ์ด๋ฒคํŠธ ์ตœ์ ํ™”
const handleScroll = useCallback(
  throttle(() => {
    // ์Šคํฌ๋กค ์ฒ˜๋ฆฌ ๋กœ์ง
  }, 100),
  []
);
๊ฒฐ๊ณผ:
- ์ดˆ๊ธฐ ๋ Œ๋”๋ง ์‹œ๊ฐ„: 2.5์ดˆ โ†’ 0.3์ดˆ (์•ฝ 8๋ฐฐ ๊ฐœ์„ !) ๐Ÿš€
- ๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰: 500MB โ†’ 50MB (10๋ฐฐ ๊ฐ์†Œ!)
- ์Šคํฌ๋กค FPS: 30 โ†’ 60 (๋ถ€๋“œ๋Ÿฌ์›€ 2๋ฐฐ ํ–ฅ์ƒ!)

๐Ÿ›’ ์ผ€์ด์Šค 2: ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ ํ…Œ์ด๋ธ” ์ตœ์ ํ™”

๋ฌธ์ œ ์ƒํ™ฉ ๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€์—์„œ 10,000๊ฐœ ์ด์ƒ์˜ ์ฃผ๋ฌธ ๋ฐ์ดํ„ฐ๋ฅผ ํ…Œ์ด๋ธ”๋กœ ๋ณด์—ฌ์ค˜์•ผ ํ–ˆ๋Š”๋ฐ, ํŽ˜์ด์ง€๊ฐ€ ์•„์˜ˆ ๋ฉˆ์ถฐ๋ฒ„๋ฆฌ๋Š” ์ˆ˜์ค€! ๐Ÿ˜ฑ
ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:
// 1. ์„œ๋ฒ„ ์‚ฌ์ด๋“œ ํŽ˜์ด์ง€๋„ค์ด์…˜ + ํด๋ผ์ด์–ธํŠธ ๊ฐ€์ƒํ™”
function OrderTable() {
  const [data, setData] = useState([]);
  const [page, setPage] = useState(0);
  const pageSize = 50;
  
  // ์„œ๋ฒ„์—์„œ ํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ๋งŒ ๊ฐ€์ ธ์˜ค๊ธฐ
  const fetchData = async (pageNum) => {
    const response = await fetch(
      `/api/orders?page=${pageNum}&size=${pageSize}`
    );
    const newData = await response.json();
    setData(newData);
  };
  
  // ๊ฐ€์ƒ ์Šคํฌ๋กค ์ ์šฉ
  return (
    <VirtualTable
      data={data}
      columns={columns}
      rowHeight={50}
      onPageChange={fetchData}
    />
  );
}

// 2. ์ •๋ ฌ/ํ•„ํ„ฐ๋ง ์ตœ์ ํ™”
function useOptimizedFilter(data, filterConfig) {
  return useMemo(() => {
    // Web Worker๋กœ ๋ฌด๊ฑฐ์šด ํ•„ํ„ฐ๋ง ์ž‘์—… ์ด๋™
    const worker = new Worker('filter-worker.js');
    
    return new Promise((resolve) => {
      worker.postMessage({ data, filterConfig });
      worker.onmessage = (e) => {
        resolve(e.data);
        worker.terminate();
      };
    });
  }, [data, filterConfig]);
}

// filter-worker.js
self.onmessage = function(e) {
  const { data, filterConfig } = e.data;
  
  const filtered = data.filter(item => {
    // ๋ณต์žกํ•œ ํ•„ํ„ฐ๋ง ๋กœ์ง
    return matchesFilter(item, filterConfig);
  });
  
  self.postMessage(filtered);
};
๊ฒฐ๊ณผ:
- ์ดˆ๊ธฐ ๋กœ๋”ฉ: 15์ดˆ โ†’ 1์ดˆ
- ํ•„ํ„ฐ๋ง ์†๋„: 5์ดˆ โ†’ 0.5์ดˆ
- ๋ฉ”๋ชจ๋ฆฌ: 1GB โ†’ 100MB
- ์‚ฌ์šฉ์ž ๋งŒ์กฑ๋„: ๐Ÿ“ˆ๐Ÿ“ˆ๐Ÿ“ˆ

๐ŸŽฎ ์ผ€์ด์Šค 3: ์‹ค์‹œ๊ฐ„ ๋Œ€์‹œ๋ณด๋“œ ์ตœ์ ํ™”

๋ฌธ์ œ ์ƒํ™ฉ ์‹ค์‹œ๊ฐ„์œผ๋กœ ์—…๋ฐ์ดํŠธ๋˜๋Š” ์ฐจํŠธ์™€ ํ†ต๊ณ„๊ฐ€ ์žˆ๋Š” ๋Œ€์‹œ๋ณด๋“œ์ธ๋ฐ, ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด์˜ฌ ๋•Œ๋งˆ๋‹ค ์ „์ฒด ํŽ˜์ด์ง€๊ฐ€ ๋ฆฌ๋ Œ๋”๋ง๋˜๋ฉด์„œ ๋ฒ„๋ฒ…๊ฑฐ๋ฆผ! ๐ŸŽฏ
ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•:
// 1. ์ปดํฌ๋„ŒํŠธ ๋ถ„๋ฆฌ ๋ฐ ๋ฉ”๋ชจ์ด์ œ์ด์…˜
const Dashboard = () => {
  const [realtimeData, setRealtimeData] = useState([]);
  
  return (
    <>
      {/* ์‹ค์‹œ๊ฐ„ ์—…๋ฐ์ดํŠธ๋˜๋Š” ๋ถ€๋ถ„๋งŒ ๋ถ„๋ฆฌ */}
      <RealtimeChart data={realtimeData} />
      
      {/* ์ •์ ์ธ ๋ถ€๋ถ„์€ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ */}
      <StaticStats />
      <StaticFilters />
    </>
  );
};

// 2. ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ
function useRealtimeData(socketUrl) {
  const [data, setData] = useState([]);
  const bufferRef = useRef([]);
  
  useEffect(() => {
    const socket = new WebSocket(socketUrl);
    
    socket.onmessage = (event) => {
      // ์ฆ‰์‹œ ์—…๋ฐ์ดํŠธํ•˜์ง€ ์•Š๊ณ  ๋ฒ„ํผ์— ์ €์žฅ
      bufferRef.current.push(JSON.parse(event.data));
    };
    
    // 100ms๋งˆ๋‹ค ํ•œ ๋ฒˆ์”ฉ ๋ฐฐ์น˜ ์—…๋ฐ์ดํŠธ
    const interval = setInterval(() => {
      if (bufferRef.current.length > 0) {
        setData(prev => [...prev, ...bufferRef.current]);
        bufferRef.current = [];
      }
    }, 100);
    
    return () => {
      socket.close();
      clearInterval(interval);
    };
  }, [socketUrl]);
  
  return data;
}

// 3. ์ฐจํŠธ ๋ Œ๋”๋ง ์ตœ์ ํ™”
const RealtimeChart = React.memo(({ data }) => {
  // Canvas ์‚ฌ์šฉ (SVG๋ณด๋‹ค ๋น ๋ฆ„)
  const canvasRef = useRef();
  
  useEffect(() => {
    const canvas = canvasRef.current;
    const ctx = canvas.getContext('2d');
    
    // requestAnimationFrame์œผ๋กœ ๋ถ€๋“œ๋Ÿฌ์šด ์• ๋‹ˆ๋ฉ”์ด์…˜
    let animationId;
    const render = () => {
      drawChart(ctx, data);
      animationId = requestAnimationFrame(render);
    };
    
    render();
    
    return () => cancelAnimationFrame(animationId);
  }, [data]);
  
  return <canvas ref={canvasRef} />;
}, (prev, next) => {
  // ๋ฐ์ดํ„ฐ๊ฐ€ ์‹ค์ œ๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ๋งŒ ๋ฆฌ๋ Œ๋”๋ง
  return prev.data.length === next.data.length;
});
๊ฒฐ๊ณผ:
- CPU ์‚ฌ์šฉ๋ฅ : 80% โ†’ 20%
- ํ”„๋ ˆ์ž„ ๋“œ๋กญ: ์ž์ฃผ ๋ฐœ์ƒ โ†’ ๊ฑฐ์˜ ์—†์Œ
- ๋ฐฐํ„ฐ๋ฆฌ ์†Œ๋ชจ: 50% ๊ฐ์†Œ (๋ชจ๋ฐ”์ผ)
- ์‹ค์‹œ๊ฐ„์„ฑ ์œ ์ง€ํ•˜๋ฉด์„œ๋„ ๋ถ€๋“œ๋Ÿฌ์šด UX ๋‹ฌ์„ฑ! โœจ

๐ŸŒŸ ํ”„๋ ˆ์ž„์›Œํฌ๋ณ„ ํŠน์ง•๊ณผ ์„ ํƒ ๊ฐ€์ด๋“œ

"๊ทธ๋ž˜์„œ ๋ญ˜ ์จ์•ผ ํ•ด?"๋ผ๋Š” ์งˆ๋ฌธ์„ ๋งŽ์ด ๋ฐ›์•„. ๊ฐ ํ”„๋ ˆ์ž„์›Œํฌ์˜ ํŠน์ง•์„ ๋น„๊ตํ•ด๋ณผ๊ฒŒ! ๐Ÿ˜Š

ํ”„๋ ˆ์ž„์›Œํฌ ๊ฐ€์ƒ DOM ์žฅ์  ์ ํ•ฉํ•œ ๊ฒฝ์šฐ
React โœ… ์‚ฌ์šฉ ๊ฑฐ๋Œ€ํ•œ ์ƒํƒœ๊ณ„, ํ’๋ถ€ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ, ๋†’์€ ์ฑ„์šฉ ์ˆ˜์š” ๋Œ€๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ, ํŒ€ ํ˜‘์—…, ๋ณต์žกํ•œ UI
Vue 3 โœ… ์‚ฌ์šฉ ๋ฐฐ์šฐ๊ธฐ ์‰ฌ์›€, ์œ ์—ฐํ•จ, ์ข‹์€ ๋ฌธ์„œ, ํ•œ๊ตญ์–ด ์ง€์› ์ค‘์†Œ๊ทœ๋ชจ ํ”„๋กœ์ ํŠธ, ๋น ๋ฅธ ๊ฐœ๋ฐœ, ์ ์ง„์  ๋„์ž…
Svelte โŒ ์ปดํŒŒ์ผ ๋ฒˆ๋“ค ํฌ๊ธฐ ์ž‘์Œ, ๋น ๋ฅธ ์„ฑ๋Šฅ, ๊ฐ„๊ฒฐํ•œ ๋ฌธ๋ฒ• ์„ฑ๋Šฅ์ด ์ค‘์š”ํ•œ ํ”„๋กœ์ ํŠธ, ์ž‘์€ ๋ฒˆ๋“ค ํฌ๊ธฐ ํ•„์š”
Solid โŒ ์„ธ๋ฐ€ํ•œ ๋ฐ˜์‘์„ฑ ์ตœ๊ณ  ์ˆ˜์ค€์˜ ์„ฑ๋Šฅ, React์™€ ์œ ์‚ฌํ•œ ๋ฌธ๋ฒ• ๊ทนํ•œ์˜ ์„ฑ๋Šฅ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ
Vanilla JS โŒ ์—†์Œ ์˜์กด์„ฑ ์—†์Œ, ์™„์ „ํ•œ ์ œ์–ด, ์ž‘์€ ํฌ๊ธฐ ๊ฐ„๋‹จํ•œ ํ”„๋กœ์ ํŠธ, ํ•™์Šต ๋ชฉ์ , ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ฐœ๋ฐœ

๐ŸŽฏ ์„ ํƒ ๊ธฐ์ค€

ํ”„๋กœ์ ํŠธ ๊ทœ๋ชจ๋ณ„ ์ถ”์ฒœ ์†Œ๊ทœ๋ชจ (๋žœ๋”ฉ ํŽ˜์ด์ง€, ๊ฐ„๋‹จํ•œ ์›น์•ฑ)
โ†’ Vanilla JS ๋˜๋Š” Svelte
์ด์œ : ์˜ค๋ฒ„ํ—ค๋“œ ์ตœ์†Œํ™”, ๋น ๋ฅธ ๋กœ๋”ฉ

์ค‘๊ทœ๋ชจ (๊ธฐ์—… ์›น์‚ฌ์ดํŠธ, ๊ด€๋ฆฌ์ž ํŽ˜์ด์ง€)
โ†’ Vue 3 ๋˜๋Š” React
์ด์œ : ์ƒ์‚ฐ์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์˜ ๊ท ํ˜•

๋Œ€๊ทœ๋ชจ (๋ณต์žกํ•œ SPA, ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ ์•ฑ)
โ†’ React ๋˜๋Š” Vue 3
์ด์œ : ํ’๋ถ€ํ•œ ์ƒํƒœ๊ณ„, ๊ฒ€์ฆ๋œ ์•„ํ‚คํ…์ฒ˜ ํŒจํ„ด

์„ฑ๋Šฅ์ด ์ตœ์šฐ์„  (๊ฒŒ์ž„, ์‹ค์‹œ๊ฐ„ ์•ฑ)
โ†’ Svelte ๋˜๋Š” Solid
์ด์œ : ์ตœ์†Œํ•œ์˜ ๋Ÿฐํƒ€์ž„ ์˜ค๋ฒ„ํ—ค๋“œ

๐Ÿ’ผ ์‹ค๋ฌด ๊ด€์ ์—์„œ์˜ ๊ณ ๋ ค์‚ฌํ•ญ

ํ”„๋ ˆ์ž„์›Œํฌ ์„ ํƒํ•  ๋•Œ ๊ธฐ์ˆ ์  ์„ฑ๋Šฅ๋งŒ ๋ณด๋ฉด ์•ˆ ๋ผ. ์ด๋Ÿฐ ๊ฒƒ๋“ค๋„ ์ค‘์š”ํ•ด:
1. ํŒ€์˜ ๊ธฐ์ˆ  ์Šคํƒ ํŒ€์›๋“ค์ด ์ด๋ฏธ ์•Œ๊ณ  ์žˆ๋Š” ๊ธฐ์ˆ ์„ ์“ฐ๋Š” ๊ฒŒ ์ƒ์‚ฐ์„ฑ์ด ๋†’์•„.

2. ์ฑ„์šฉ ์‹œ์žฅ
React ๊ฐœ๋ฐœ์ž๊ฐ€ ๊ฐ€์žฅ ๋งŽ๊ณ , Vue๋„ ๊ฝค ์žˆ์–ด. Svelte๋Š” ์•„์ง ์ ์€ ํŽธ.

3. ์ƒํƒœ๊ณ„์™€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธํ•ด๋ด. React๋Š” ๊ฑฐ์˜ ๋ชจ๋“  ๊ฒŒ ์žˆ์–ด!

4. ์žฅ๊ธฐ ์œ ์ง€๋ณด์ˆ˜
5๋…„ ํ›„์—๋„ ์ง€์›๋ ๊นŒ? ์ปค๋ฎค๋‹ˆํ‹ฐ๊ฐ€ ํ™œ๋ฐœํ• ๊นŒ?

5. ํ•™์Šต ๊ณก์„ 
๋นจ๋ฆฌ ๋ฐฐ์›Œ์•ผ ํ•œ๋‹ค๋ฉด Vue๊ฐ€ ์œ ๋ฆฌํ•ด. React๋Š” ๊ฐœ๋…์ด ์ข€ ๋” ๋งŽ์•„.
์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ํ”„๋ฆฌ๋žœ์„œ๋กœ ์ผํ•  ๋•Œ๋„ ์ด๋Ÿฐ ์š”์†Œ๋“ค์ด ์ค‘์š”ํ•ด. ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์›ํ•˜๋Š” ๊ธฐ์ˆ  ์Šคํƒ์ด ์žˆ์„ ์ˆ˜ ์žˆ๊ณ , ์œ ์ง€๋ณด์ˆ˜๋ฅผ ๊ณ ๋ คํ•ด์•ผ ํ•˜๊ฑฐ๋“ ! ๐ŸŽฏ

๐Ÿš€ ๋ฏธ๋ž˜์˜ DOM ์ตœ์ ํ™” ๊ธฐ์ˆ 

๊ธฐ์ˆ ์€ ๊ณ„์† ๋ฐœ์ „ํ•˜๊ณ  ์žˆ์–ด! ์•ž์œผ๋กœ ์ฃผ๋ชฉํ•ด์•ผ ํ•  ํŠธ๋ Œ๋“œ๋“ค์„ ์‚ดํŽด๋ณด์ž. ๐Ÿ”ฎ

1๏ธโƒฃ ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ (React Server Components)

React 18์—์„œ ๋„์ž…๋œ ํ˜์‹ ์ ์ธ ๊ฐœ๋…์ด์•ผ!
// ์„œ๋ฒ„ ์ปดํฌ๋„ŒํŠธ (Server Component)
// ์„œ๋ฒ„์—์„œ๋งŒ ์‹คํ–‰๋˜๊ณ , ํด๋ผ์ด์–ธํŠธ๋กœ HTML๋งŒ ์ „์†ก
async function ProductList() {
  // ์„œ๋ฒ„์—์„œ ์ง์ ‘ DB ์กฐํšŒ ๊ฐ€๋Šฅ!
  const products = await db.products.findMany();
  
  return (
    <div>
      {products.map(product => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  );
}

// ํด๋ผ์ด์–ธํŠธ ์ปดํฌ๋„ŒํŠธ (Client Component)
'use client'; // ์ด ์ง€์‹œ์–ด๋กœ ๊ตฌ๋ถ„

function AddToCartButton({ productId }) {
  const [isAdding, setIsAdding] = useState(false);
  
  // ํด๋ผ์ด์–ธํŠธ์—์„œ๋งŒ ์‹คํ–‰๋˜๋Š” ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ ๋กœ์ง
  const handleClick = () => {
    setIsAdding(true);
    // ...
  };
  
  return <button onClick={handleClick}>Add to Cart</button>;
}
์žฅ์ :
- JavaScript ๋ฒˆ๋“ค ํฌ๊ธฐ ๋Œ€ํญ ๊ฐ์†Œ ๐Ÿ“ฆ
- ์„œ๋ฒ„ ๋ฆฌ์†Œ์Šค ์ง์ ‘ ์ ‘๊ทผ ๊ฐ€๋Šฅ
- ์ดˆ๊ธฐ ๋กœ๋”ฉ ์†๋„ ํ–ฅ์ƒ
- SEO ๊ฐœ์„ 

2๏ธโƒฃ Islands Architecture (์•„์ผ๋žœ๋“œ ์•„ํ‚คํ…์ฒ˜)

Astro ๊ฐ™์€ ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ๊ฐœ๋…์ด์•ผ.
๐Ÿ’ก ํ•ต์‹ฌ ์•„์ด๋””์–ด ํŽ˜์ด์ง€์˜ ๋Œ€๋ถ€๋ถ„์€ ์ •์  HTML๋กœ ๋งŒ๋“ค๊ณ , ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ๊ฐ€ ํ•„์š”ํ•œ ๋ถ€๋ถ„๋งŒ "์„ฌ(Island)"์ฒ˜๋Ÿผ JavaScript๋ฅผ ์ถ”๊ฐ€ํ•˜๋Š” ๋ฐฉ์‹!

์˜ˆ๋ฅผ ๋“ค์–ด, ๋ธ”๋กœ๊ทธ ํฌ์ŠคํŠธ๋Š” ์ •์  HTML์ด๊ณ , ๋Œ“๊ธ€ ์„น์…˜๋งŒ React ์ปดํฌ๋„ŒํŠธ๋กœ ๋งŒ๋“œ๋Š” ๊ฑฐ์•ผ.
---
// Astro ์˜ˆ์‹œ
import Header from '../components/Header.astro'; // ์ •์ 
import BlogPost from '../components/BlogPost.astro'; // ์ •์ 
import Comments from '../components/Comments.jsx'; // ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ
---

<html>
  <Header />
  <BlogPost />
  
  <!-- ์ด ๋ถ€๋ถ„๋งŒ JavaScript ๋กœ๋“œ -->
  <Comments client:load />
</html>
๊ฒฐ๊ณผ:
- JavaScript ๋ฒˆ๋“ค: 500KB โ†’ 50KB
- ์ดˆ๊ธฐ ๋กœ๋”ฉ: 3์ดˆ โ†’ 0.5์ดˆ
- Lighthouse ์ ์ˆ˜: 70 โ†’ 95

3๏ธโƒฃ Resumability (์žฌ๊ฐœ ๊ฐ€๋Šฅ์„ฑ)

Qwik ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ์ œ์•ˆํ•˜๋Š” ์ƒˆ๋กœ์šด ํŒจ๋Ÿฌ๋‹ค์ž„์ด์•ผ.
๊ธฐ์กด ๋ฐฉ์‹ (Hydration) 1. ์„œ๋ฒ„์—์„œ HTML ์ƒ์„ฑ
2. ํด๋ผ์ด์–ธํŠธ๋กœ ์ „์†ก
3. JavaScript ๋‹ค์šด๋กœ๋“œ
4. ์ „์ฒด ์•ฑ์„ ๋‹ค์‹œ ์‹คํ–‰ํ•ด์„œ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ๋“ฑ๋ก (Hydration)

์ƒˆ๋กœ์šด ๋ฐฉ์‹ (Resumability)
1. ์„œ๋ฒ„์—์„œ HTML + ์ƒํƒœ ์ •๋ณด ์ƒ์„ฑ
2. ํด๋ผ์ด์–ธํŠธ๋กœ ์ „์†ก
3. ํ•„์š”ํ•œ ๋ถ€๋ถ„๋งŒ ์ฆ‰์‹œ ์ธํ„ฐ๋ž™ํ‹ฐ๋ธŒ (Hydration ๋ถˆํ•„์š”!)
4. ๋‚˜๋จธ์ง€๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ํด๋ฆญํ•  ๋•Œ ๋กœ๋“œ
// Qwik ์˜ˆ์‹œ
import { component$, useSignal } from '@builder.io/qwik';

export const Counter = component$(() => {
  const count = useSignal(0);
  
  return (
    <div>
      <p>Count: {count.value}</p>
      <button onClick$={() => count.value++}>
        Increment
      </button>
    </div>
  );
});

// onClick$์˜ $๋Š” "์ด ์ฝ”๋“œ๋ฅผ ๋‚˜์ค‘์— ๋กœ๋“œํ•ด๋„ ๋ผ"๋ผ๋Š” ์˜๋ฏธ!
ํ˜์‹ ์ ์ธ ์ :
- Time to Interactive(TTI)๊ฐ€ ๊ฑฐ์˜ 0์— ๊ฐ€๊นŒ์›Œ์ง! โšก
- ์ดˆ๊ธฐ JavaScript ์‹คํ–‰ ์—†์Œ
- ๋Œ€๊ทœ๋ชจ ์•ฑ์—์„œ๋„ ๋น ๋ฅธ ์‹œ์ž‘

4๏ธโƒฃ WebAssembly (WASM) ํ†ตํ•ฉ

๋ฌด๊ฑฐ์šด ๊ณ„์‚ฐ์€ WebAssembly๋กœ!
// Rust๋กœ ์ž‘์„ฑ๋œ ๋ฌด๊ฑฐ์šด ๋กœ์ง
// heavy_calculation.rs
#[wasm_bindgen]
pub fn process_large_data(data: &[u8]) -> Vec<u8> {
    // ๋ณต์žกํ•œ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ
    // JavaScript๋ณด๋‹ค 10๋ฐฐ ๋น ๋ฆ„!
}

// JavaScript์—์„œ ์‚ฌ์šฉ
import init, { process_large_data } from './heavy_calculation.wasm';

async function processData(data) {
  await init();
  const result = process_large_data(data);
  return result;
}
์‚ฌ์šฉ ์‚ฌ๋ก€:
- ์ด๋ฏธ์ง€/๋น„๋””์˜ค ์ฒ˜๋ฆฌ ๐ŸŽจ
- ์•”ํ˜ธํ™”/๋ณตํ˜ธํ™” ๐Ÿ”
- ๊ฒŒ์ž„ ์—”์ง„ ๐ŸŽฎ
- ๊ณผํ•™ ๊ณ„์‚ฐ ๐Ÿ”ฌ

๐Ÿ“š ์‹ค๋ฌด ์ฒดํฌ๋ฆฌ์ŠคํŠธ: ์„ฑ๋Šฅ ์ตœ์ ํ™” ๊ฐ€์ด๋“œ

ํ”„๋กœ์ ํŠธ์—์„œ ๋ฐ”๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ฒดํฌ๋ฆฌ์ŠคํŠธ๋ฅผ ๋งŒ๋“ค์–ด๋ดค์–ด! ํ•˜๋‚˜์”ฉ ์ฒดํฌํ•˜๋ฉด์„œ ์ตœ์ ํ™”ํ•ด๋ณด์ž. โœ…

๐ŸŽฏ ์ดˆ๊ธฐ ๋กœ๋”ฉ ์ตœ์ ํ™”

  • โ˜ ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ… ์ ์šฉ (๋™์  import)
  • โ˜ ์ด๋ฏธ์ง€ ์ตœ์ ํ™” (WebP, lazy loading)
  • โ˜ ํฐํŠธ ์ตœ์ ํ™” (font-display: swap)
  • โ˜ CSS ์ตœ์†Œํ™” ๋ฐ ์ธ๋ผ์ธ ํฌ๋ฆฌํ‹ฐ์ปฌ CSS
  • โ˜ JavaScript ๋ฒˆ๋“ค ํฌ๊ธฐ ๋ถ„์„ (webpack-bundle-analyzer)
  • โ˜ Tree shaking ํ™•์ธ
  • โ˜ Gzip/Brotli ์••์ถ• ํ™œ์„ฑํ™”
  • โ˜ CDN ์‚ฌ์šฉ
  • โ˜ HTTP/2 ๋˜๋Š” HTTP/3 ์‚ฌ์šฉ

โšก ๋Ÿฐํƒ€์ž„ ์„ฑ๋Šฅ ์ตœ์ ํ™”

  • โ˜ ๋ถˆํ•„์š”ํ•œ ๋ฆฌ๋ Œ๋”๋ง ์ œ๊ฑฐ (React.memo, useMemo)
  • โ˜ ๊ฐ€์ƒ ์Šคํฌ๋กค ๊ตฌํ˜„ (๊ธด ๋ฆฌ์ŠคํŠธ)
  • โ˜ ๋””๋ฐ”์šด์Šค/์“ฐ๋กœํ‹€ ์ ์šฉ (์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ)
  • โ˜ requestAnimationFrame ์‚ฌ์šฉ (์• ๋‹ˆ๋ฉ”์ด์…˜)
  • โ˜ Web Worker ํ™œ์šฉ (๋ฌด๊ฑฐ์šด ๊ณ„์‚ฐ)
  • โ˜ ์ด๋ฒคํŠธ ์œ„์ž„ ์‚ฌ์šฉ
  • โ˜ DOM ์กฐ์ž‘ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ
  • โ˜ CSS ์• ๋‹ˆ๋ฉ”์ด์…˜ ์šฐ์„  (transform, opacity)
  • โ˜ will-change ์†์„ฑ ์ ์ ˆํžˆ ์‚ฌ์šฉ

๐Ÿ” ๋ชจ๋‹ˆํ„ฐ๋ง ๋ฐ ์ธก์ •

โ˜ Lighthouse ์ ์ˆ˜ 90์  ์ด์ƒ
โ˜ Core Web Vitals ํ†ต๊ณผ
- LCP (Largest Contentful Paint) < 2.5s
- FID (First Input Delay) < 100ms
- CLS (Cumulative Layout Shift) < 0.1
โ˜ Performance ํƒญ์œผ๋กœ ํ”„๋กœํŒŒ์ผ๋ง
โ˜ React DevTools Profiler ํ™•์ธ
โ˜ ์‹ค์ œ ์‚ฌ์šฉ์ž ๋ชจ๋‹ˆํ„ฐ๋ง (RUM) ์„ค์ •
โ˜ ์—๋Ÿฌ ์ถ”์  ๋„๊ตฌ ์—ฐ๋™ (Sentry ๋“ฑ)

๐Ÿ“ฑ ๋ชจ๋ฐ”์ผ ์ตœ์ ํ™”

  • โ˜ ํ„ฐ์น˜ ์ด๋ฒคํŠธ ์ตœ์ ํ™”
  • โ˜ ๋ทฐํฌํŠธ ์„ค์ • ํ™•์ธ
  • โ˜ ๋ชจ๋ฐ”์ผ ๋„คํŠธ์›Œํฌ ํ…Œ์ŠคํŠธ (3G ํ™˜๊ฒฝ)
  • โ˜ ์˜คํ”„๋ผ์ธ ์ง€์› (Service Worker)
  • โ˜ ๋ฐฐํ„ฐ๋ฆฌ ์†Œ๋ชจ ์ตœ์†Œํ™”
  • โ˜ ๋ฐ˜์‘ํ˜• ์ด๋ฏธ์ง€ ์‚ฌ์šฉ (srcset)
  • โ˜ ํ„ฐ์น˜ ํƒ€๊ฒŸ ํฌ๊ธฐ ์ถฉ๋ถ„ํžˆ (์ตœ์†Œ 44x44px)

๐Ÿ›ก๏ธ ๋ณด์•ˆ ๋ฐ ์ ‘๊ทผ์„ฑ

  • โ˜ XSS ๋ฐฉ์ง€ (์ž…๋ ฅ ๊ฒ€์ฆ, ์ด์Šค์ผ€์ดํ•‘)
  • โ˜ CSRF ํ† ํฐ ์‚ฌ์šฉ
  • โ˜ HTTPS ์‚ฌ์šฉ
  • โ˜ Content Security Policy ์„ค์ •
  • โ˜ ํ‚ค๋ณด๋“œ ๋„ค๋น„๊ฒŒ์ด์…˜ ์ง€์›
  • โ˜ ์Šคํฌ๋ฆฐ ๋ฆฌ๋” ํ˜ธํ™˜์„ฑ
  • โ˜ ARIA ๋ ˆ์ด๋ธ” ์ ์ ˆํžˆ ์‚ฌ์šฉ
  • โ˜ ์ƒ‰์ƒ ๋Œ€๋น„ ์ถฉ๋ถ„ํžˆ (WCAG AA ์ด์ƒ)
โš ๏ธ ๊ณผ๋„ํ•œ ์ตœ์ ํ™” ์ฃผ์˜! "์กฐ๊ธฐ ์ตœ์ ํ™”๋Š” ๋ชจ๋“  ์•…์˜ ๊ทผ์›์ด๋‹ค" - Donald Knuth

๋จผ์ € ์ž‘๋™ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ค๊ณ , ํ”„๋กœํŒŒ์ผ๋ง์œผ๋กœ ๋ณ‘๋ชฉ์„ ์ฐพ์€ ๋‹ค์Œ, ํ•„์š”ํ•œ ๋ถ€๋ถ„๋งŒ ์ตœ์ ํ™”ํ•˜์ž. ๋ชจ๋“  ๊ฑธ ๋‹ค ์ตœ์ ํ™”ํ•˜๋ ค๋‹ค๊ฐ€ ๊ฐœ๋ฐœ ์‹œ๊ฐ„๋งŒ ๋Š˜์–ด๋‚  ์ˆ˜ ์žˆ์–ด! ๐ŸŽฏ

๐Ÿ’ฌ ์ž์ฃผ ๋ฌป๋Š” ์งˆ๋ฌธ (FAQ)

์‹ค๋ฌด์—์„œ ์ž์ฃผ ๋ฐ›๋Š” ์งˆ๋ฌธ๋“ค์„ ์ •๋ฆฌํ•ด๋ดค์–ด! ๐Ÿ˜Š

Q1. ๊ฐ€์ƒ DOM์ด ํ•ญ์ƒ ๋น ๋ฅธ๊ฐ€์š”?

A: ์•„๋‹ˆ์•ผ! ์ƒํ™ฉ์— ๋”ฐ๋ผ ๋‹ค๋ฅด์ง€.

๊ฐ€์ƒ DOM์ด ๋น ๋ฅธ ๊ฒฝ์šฐ:
- ๋ณต์žกํ•œ UI์—์„œ ๋ถ€๋ถ„์ ์ธ ์—…๋ฐ์ดํŠธ๊ฐ€ ๋งŽ์„ ๋•Œ
- ์—ฌ๋Ÿฌ ์ƒํƒœ ๋ณ€๊ฒฝ์ด ๋™์‹œ์— ์ผ์–ด๋‚  ๋•Œ
- ๊ฐœ๋ฐœ์ž๊ฐ€ ์ตœ์ ํ™”๋ฅผ ์‹ ๊ฒฝ ์“ฐ์ง€ ์•Š์•„๋„ ๋  ๋•Œ

์ˆœ์ˆ˜ DOM์ด ๋น ๋ฅธ ๊ฒฝ์šฐ:
- ๋‹จ์ˆœํ•œ UI
- ํ•œ ๋ฒˆ๋งŒ ๋ Œ๋”๋งํ•˜๊ณ  ๋
- ์ •ํ™•ํžˆ ์–ด๋–ค ๋ถ€๋ถ„์„ ์—…๋ฐ์ดํŠธํ• ์ง€ ์•Œ ๋•Œ

์˜ˆ๋ฅผ ๋“ค์–ด, ๋‹จ์ˆœํžˆ ํ…์ŠคํŠธ ํ•˜๋‚˜ ๋ฐ”๊พธ๋Š” ๊ฑฐ๋ผ๋ฉด element.textContent = 'new text'๊ฐ€ ๊ฐ€์ƒ DOM๋ณด๋‹ค ๋นจ๋ผ!

Q2. React vs Vue, ์„ฑ๋Šฅ ์ฐจ์ด๊ฐ€ ์žˆ๋‚˜์š”?

A: ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๊ฑฐ์˜ ์ฐจ์ด ์—†์–ด!

๋ฒค์น˜๋งˆํฌ ํ…Œ์ŠคํŠธ์—์„œ๋Š” Vue 3๊ฐ€ ์•ฝ๊ฐ„ ๋น ๋ฅด๊ฒŒ ๋‚˜์˜ค๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์ง€๋งŒ, ์‹ค๋ฌด์—์„œ๋Š” ์ฝ”๋“œ ํ’ˆ์งˆ๊ณผ ์ตœ์ ํ™” ๊ธฐ๋ฒ•์ด ํ›จ์”ฌ ์ค‘์š”ํ•ด.

๋‘˜ ๋‹ค ์ถฉ๋ถ„ํžˆ ๋น ๋ฅด๋‹ˆ๊นŒ, ํŒ€์˜ ์„ ํ˜ธ๋„๋‚˜ ์ƒํƒœ๊ณ„๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์„ ํƒํ•˜๋Š” ๊ฒŒ ์ข‹์•„! ๐ŸŽฏ

Q3. key๋ฅผ index๋กœ ์“ฐ๋ฉด ์•ˆ ๋˜๋Š” ์ด์œ ๊ฐ€ ๋ญ”๊ฐ€์š”?

A: ๋ฆฌ์ŠคํŠธ ์ˆœ์„œ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ๋ฌธ์ œ๊ฐ€ ์ƒ๊ฒจ!

// ์ดˆ๊ธฐ ์ƒํƒœ
[
  { id: 1, name: 'Alice' },  // index: 0
  { id: 2, name: 'Bob' },    // index: 1
  { id: 3, name: 'Charlie' } // index: 2
]

// Alice๋ฅผ ์‚ญ์ œํ•˜๋ฉด?
[
  { id: 2, name: 'Bob' },    // index: 0 (์ด์ „์—๋Š” 1)
  { id: 3, name: 'Charlie' } // index: 1 (์ด์ „์—๋Š” 2)
]
index๋ฅผ key๋กœ ์“ฐ๋ฉด, React๋Š” "index 0์˜ ๋‚ด์šฉ์ด Alice์—์„œ Bob์œผ๋กœ ๋ฐ”๋€Œ์—ˆ๋„ค?"๋ผ๊ณ  ์ƒ๊ฐํ•ด์„œ ๋ถˆํ•„์š”ํ•œ ์—…๋ฐ์ดํŠธ๋ฅผ ํ•ด. ์‹ฌ์ง€์–ด input ๊ฐ’์ด๋‚˜ ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ƒํƒœ๊ฐ€ ๊ผฌ์ผ ์ˆ˜๋„ ์žˆ์–ด! ๐Ÿ˜ฑ

ํ•ด๊ฒฐ์ฑ…: ํ•ญ์ƒ ๊ณ ์œ ํ•œ ID๋ฅผ key๋กœ ์‚ฌ์šฉํ•˜์ž!

Q4. useMemo์™€ useCallback์„ ์–ธ์ œ ์จ์•ผ ํ•˜๋‚˜์š”?

A: ์ธก์ • ๊ฐ€๋Šฅํ•œ ์„ฑ๋Šฅ ๋ฌธ์ œ๊ฐ€ ์žˆ์„ ๋•Œ๋งŒ!

useMemo๋ฅผ ์“ธ ๋•Œ:
- ๊ณ„์‚ฐ ๋น„์šฉ์ด ๋†’์€ ์ž‘์—… (๋ณต์žกํ•œ ํ•„ํ„ฐ๋ง, ์ •๋ ฌ ๋“ฑ)
- ์ฐธ์กฐ ๋™์ผ์„ฑ์ด ์ค‘์š”ํ•œ ๊ฒฝ์šฐ (์˜์กด์„ฑ ๋ฐฐ์—ด์— ๋“ค์–ด๊ฐ€๋Š” ๊ฐ์ฒด/๋ฐฐ์—ด)

useCallback์„ ์“ธ ๋•Œ:
- ์ž์‹ ์ปดํฌ๋„ŒํŠธ์— props๋กœ ์ „๋‹ฌ๋˜๋Š” ํ•จ์ˆ˜
- ๋‹ค๋ฅธ Hook์˜ ์˜์กด์„ฑ ๋ฐฐ์—ด์— ๋“ค์–ด๊ฐ€๋Š” ํ•จ์ˆ˜

์“ฐ์ง€ ๋ง์•„์•ผ ํ•  ๋•Œ:
- ๋‹จ์ˆœํ•œ ๊ณ„์‚ฐ (์ˆซ์ž ๋”ํ•˜๊ธฐ ๋“ฑ)
- ๋ Œ๋”๋ง ๋น„์šฉ์ด ๋‚ฎ์€ ์ปดํฌ๋„ŒํŠธ
- ํ”„๋กœํŒŒ์ผ๋ง ์—†์ด "ํ˜น์‹œ ๋ชฐ๋ผ์„œ"

๊ธฐ์–ตํ•ด: useMemo์™€ useCallback๋„ ๋น„์šฉ์ด ๋“ค์–ด! ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ์˜์กด์„ฑ ๋น„๊ต ๋กœ์ง์ด ์‹คํ–‰๋˜๊ฑฐ๋“ .

Q5. ๋ฒˆ๋“ค ํฌ๊ธฐ๋ฅผ ์ค„์ด๋Š” ๊ฐ€์žฅ ํšจ๊ณผ์ ์ธ ๋ฐฉ๋ฒ•์€?

A: ์ด ์ˆœ์„œ๋Œ€๋กœ ํ•ด๋ด!

1. ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ์ฝ”๋“œ ์ œ๊ฑฐ
- webpack-bundle-analyzer๋กœ ๋ถ„์„
- ์•ˆ ์“ฐ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ œ๊ฑฐ
- Tree shaking ํ™•์ธ

2. ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ…
// ๋™์  import ์‚ฌ์šฉ
const HeavyComponent = lazy(() => import('./HeavyComponent'));

3. ๊ฐ€๋ฒผ์šด ๋Œ€์•ˆ ์‚ฌ์šฉ
- moment.js (288KB) โ†’ date-fns (13KB)
- lodash (์ „์ฒด) โ†’ lodash-es (ํ•„์š”ํ•œ ๊ฒƒ๋งŒ)
- axios โ†’ fetch API

4. ์••์ถ• ๋ฐ ์ตœ์ ํ™”
- Terser๋กœ minify
- Gzip/Brotli ์••์ถ•
- ์ด๋ฏธ์ง€ ์ตœ์ ํ™”

Q6. ์žฌ๋Šฅ๋„ท์—์„œ ํ”„๋ฆฌ๋žœ์„œ๋กœ ์ผํ•  ๋•Œ ์„ฑ๋Šฅ ์ตœ์ ํ™” ์š”๊ตฌ์‚ฌํ•ญ์ด ์žˆ๋‚˜์š”?

A: ํด๋ผ์ด์–ธํŠธ๋งˆ๋‹ค ๋‹ค๋ฅด์ง€๋งŒ, ์ผ๋ฐ˜์ ์œผ๋กœ ์ด๋Ÿฐ ๊ฑธ ์š”๊ตฌํ•ด:

- Lighthouse ์ ์ˆ˜ 90์  ์ด์ƒ
- ๋ชจ๋ฐ”์ผ์—์„œ 3์ดˆ ์ด๋‚ด ๋กœ๋”ฉ
- ๋ถ€๋“œ๋Ÿฌ์šด ์Šคํฌ๋กค ๋ฐ ์• ๋‹ˆ๋ฉ”์ด์…˜
- ํฌ๋กœ์Šค ๋ธŒ๋ผ์šฐ์ € ํ˜ธํ™˜์„ฑ

์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ํ”„๋กœ์ ํŠธ๋ฅผ ๋ฐ›์„ ๋•Œ๋Š” ์„ฑ๋Šฅ ๊ธฐ์ค€์„ ๋ช…ํ™•ํžˆ ํ•˜๊ณ  ๊ณ„์•ฝ์„œ์— ํฌํ•จ์‹œํ‚ค๋Š” ๊ฒŒ ์ข‹์•„. ๋‚˜์ค‘์— ๋ถ„์Ÿ์„ ํ”ผํ•  ์ˆ˜ ์žˆ๊ฑฐ๋“ ! ๐Ÿ“

๐ŸŽ“ ํ•™์Šต ๋ฆฌ์†Œ์Šค ๋ฐ ๋‹ค์Œ ๋‹จ๊ณ„

๋” ๊นŠ์ด ๊ณต๋ถ€ํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ์ด๋Ÿฐ ์ž๋ฃŒ๋“ค์„ ์ถ”์ฒœํ•ด! ๐Ÿ“š

๐Ÿ“– ํ•„๋… ๋ฌธ์„œ

๊ณต์‹ ๋ฌธ์„œ - React ๊ณต์‹ ๋ฌธ์„œ: react.dev
- Vue ๊ณต์‹ ๋ฌธ์„œ: vuejs.org
- MDN Web Docs: developer.mozilla.org

์„ฑ๋Šฅ ๊ด€๋ จ
- web.dev (Google์˜ ์›น ์„ฑ๋Šฅ ๊ฐ€์ด๋“œ)
- JavaScript.info (๊นŠ์ด ์žˆ๋Š” JavaScript ํ•™์Šต)
- React Performance ๊ฐ€์ด๋“œ
- Vue Performance ๊ฐ€์ด๋“œ

๐Ÿ› ๏ธ ์œ ์šฉํ•œ ๋„๊ตฌ๋“ค

๋„๊ตฌ ์šฉ๋„ ์ถ”์ฒœ๋„
Chrome DevTools ์„ฑ๋Šฅ ํ”„๋กœํŒŒ์ผ๋ง, ๋””๋ฒ„๊น… โญโญโญโญโญ
React DevTools React ์ปดํฌ๋„ŒํŠธ ๋ถ„์„ โญโญโญโญโญ
Lighthouse ์ „์ฒด ์„ฑ๋Šฅ ์ ์ˆ˜ โญโญโญโญโญ
webpack-bundle-analyzer ๋ฒˆ๋“ค ํฌ๊ธฐ ๋ถ„์„ โญโญโญโญโญ
React Query ๋ฐ์ดํ„ฐ ํŽ˜์นญ ์ตœ์ ํ™” โญโญโญโญ
SWR ๋ฐ์ดํ„ฐ ์บ์‹ฑ โญโญโญโญ
Vite ๋น ๋ฅธ ๊ฐœ๋ฐœ ์„œ๋ฒ„ โญโญโญโญโญ

๐ŸŽฏ ์‹ค์Šต ํ”„๋กœ์ ํŠธ ์•„์ด๋””์–ด

์ด๋ก ๋งŒ ๋ณด์ง€ ๋ง๊ณ  ์ง์ ‘ ๋งŒ๋“ค์–ด๋ณด์ž! ์ด๋Ÿฐ ํ”„๋กœ์ ํŠธ๋“ค์„ ์ถ”์ฒœํ•ด:
1. ๋ฌดํ•œ ์Šคํฌ๋กค ํ”ผ๋“œ ์•ฑ - ๊ฐ€์ƒ ์Šคํฌ๋กค ๊ตฌํ˜„
- ์ด๋ฏธ์ง€ lazy loading
- ์Šคํฌ๋กค ์ด๋ฒคํŠธ ์ตœ์ ํ™”

2. ์‹ค์‹œ๊ฐ„ ๋Œ€์‹œ๋ณด๋“œ
- WebSocket ์—ฐ๋™
- ์ฐจํŠธ ๋ Œ๋”๋ง ์ตœ์ ํ™”
- ๋ฐ์ดํ„ฐ ๋ฐฐ์น˜ ์—…๋ฐ์ดํŠธ

3. ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ ํ…Œ์ด๋ธ”
- ๊ฐ€์ƒ ์Šคํฌ๋กค
- ์ •๋ ฌ/ํ•„ํ„ฐ๋ง ์ตœ์ ํ™”
- ํŽ˜์ด์ง€๋„ค์ด์…˜

4. ์ด๋ฏธ์ง€ ๊ฐค๋Ÿฌ๋ฆฌ
- ์ด๋ฏธ์ง€ ์ตœ์ ํ™”
- Intersection Observer
- ํ”„๋ฆฌ๋กœ๋”ฉ ์ „๋žต

๐Ÿ’ก ๋‹ค์Œ ๋‹จ๊ณ„

์ด ๊ธ€์„ ๋‹ค ์ฝ์—ˆ๋‹ค๋ฉด, ์ด์ œ ์ด๋Ÿฐ ๊ฑธ ํ•ด๋ณด์ž:
1์ฃผ์ฐจ: ๊ธฐ์ดˆ ๋‹ค์ง€๊ธฐ - ์ˆœ์ˆ˜ JavaScript๋กœ TODO ์•ฑ ๋งŒ๋“ค๊ธฐ
- DOM ์กฐ์ž‘ ์ตœ์ ํ™” ๊ธฐ๋ฒ• ์ ์šฉ
- Performance ํƒญ์œผ๋กœ ์ธก์ •

2์ฃผ์ฐจ: ํ”„๋ ˆ์ž„์›Œํฌ ์ตํžˆ๊ธฐ
- React ๋˜๋Š” Vue๋กœ ๊ฐ™์€ ์•ฑ ๋งŒ๋“ค๊ธฐ
- ๊ฐ€์ƒ DOM์˜ ๋™์ž‘ ์ดํ•ด
- ์„ฑ๋Šฅ ๋น„๊ต

3์ฃผ์ฐจ: ๊ณ ๊ธ‰ ์ตœ์ ํ™”
- ์ฝ”๋“œ ์Šคํ”Œ๋ฆฌํŒ… ์ ์šฉ
- ๋ฉ”๋ชจ์ด์ œ์ด์…˜ ๊ธฐ๋ฒ•
- ๋ฒˆ๋“ค ํฌ๊ธฐ ์ตœ์ ํ™”

4์ฃผ์ฐจ: ์‹ค์ „ ํ”„๋กœ์ ํŠธ
- ๋ณต์žกํ•œ ์•ฑ ๋งŒ๋“ค๊ธฐ
- ๋ชจ๋“  ์ตœ์ ํ™” ๊ธฐ๋ฒ• ์ ์šฉ
- Lighthouse 90์  ์ด์ƒ ๋‹ฌ์„ฑ!

๐ŸŽฌ ๋งˆ๋ฌด๋ฆฌํ•˜๋ฉฐ

์™€, ์—ฌ๊ธฐ๊นŒ์ง€ ์ฝ์—ˆ๋‹ค๋‹ˆ ๋Œ€๋‹จํ•ด! ๐Ÿ‘๐Ÿ‘๐Ÿ‘

DOM ์กฐ์ž‘๊ณผ ๊ฐ€์ƒ DOM์˜ ์„ฑ๋Šฅ ์ฐจ์ด์— ๋Œ€ํ•ด ์ •๋ง ๋งŽ์€ ๊ฑธ ๋‹ค๋ค˜์–ด. ํ•ต์‹ฌ์„ ๋‹ค์‹œ ํ•œ๋ฒˆ ์ •๋ฆฌํ•˜๋ฉด:

ํ•ต์‹ฌ ์š”์•ฝ ๐ŸŽฏ 1. DOM ์กฐ์ž‘์€ ๋А๋ฆฌ์ง€ ์•Š๋‹ค. ๊ทธ ํ›„์† ์ž‘์—…(Reflow, Repaint)์ด ๋А๋ฆฐ ๊ฑฐ๋‹ค.
โ†’ ์ฝ๊ธฐ์™€ ์“ฐ๊ธฐ๋ฅผ ๋ถ„๋ฆฌํ•˜๊ณ , ๋ฐฐ์น˜ ์ฒ˜๋ฆฌํ•˜์ž!

2. ๊ฐ€์ƒ DOM์€ ๋งŒ๋Šฅ์ด ์•„๋‹ˆ๋‹ค. ์ƒํ™ฉ์— ๋งž๊ฒŒ ์„ ํƒํ•˜์ž.
โ†’ ๋ณต์žกํ•œ UI์—๋Š” ๊ฐ€์ƒ DOM, ๋‹จ์ˆœํ•œ ์ž‘์—…์€ ์ˆœ์ˆ˜ DOM

3. ์ตœ์ ํ™”๋Š” ์ธก์ • ํ›„์— ํ•˜์ž.
โ†’ ํ”„๋กœํŒŒ์ผ๋ง ๋จผ์ €, ์ตœ์ ํ™”๋Š” ๋‚˜์ค‘์—

4. ํ”„๋ ˆ์ž„์›Œํฌ ์„ ํƒ์€ ๊ธฐ์ˆ ์  ์š”์†Œ๋งŒ ๊ณ ๋ คํ•˜์ง€ ๋ง์ž.
โ†’ ํŒ€, ์ƒํƒœ๊ณ„, ์œ ์ง€๋ณด์ˆ˜์„ฑ๋„ ์ค‘์š”ํ•ด

5. ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด ์ตœ์šฐ์„ ์ด๋‹ค.
โ†’ ์ˆซ์ž๋ณด๋‹ค ์‹ค์ œ ์ฒด๊ฐ ์„ฑ๋Šฅ์ด ์ค‘์š”ํ•ด
์„ฑ๋Šฅ ์ตœ์ ํ™”๋Š” ๋์ด ์—†๋Š” ์—ฌ์ •์ด์•ผ. ๊ธฐ์ˆ ๋„ ๊ณ„์† ๋ฐœ์ „ํ•˜๊ณ , ์ƒˆ๋กœ์šด ํŒจํ„ด๋„ ๋‚˜์˜ค๊ณ  ์žˆ์–ด. ํ•˜์ง€๋งŒ ๊ธฐ๋ณธ ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•˜๋ฉด, ์–ด๋–ค ๊ธฐ์ˆ ์ด ๋‚˜์™€๋„ ๋น ๋ฅด๊ฒŒ ์ ์‘ํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์•ผ! ๐Ÿ’ช

์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ํ”„๋ฆฌ๋žœ์„œ๋กœ ์ผํ•˜๋“ , ํšŒ์‚ฌ์—์„œ ์ •๊ทœ์ง์œผ๋กœ ์ผํ•˜๋“ , ์„ฑ๋Šฅ ์ตœ์ ํ™” ๋Šฅ๋ ฅ์€ ์ •๋ง ์ค‘์š”ํ•œ ์Šคํ‚ฌ์ด์•ผ. ํด๋ผ์ด์–ธํŠธ๋‚˜ ์‚ฌ์šฉ์ž๋“ค์ด "์ด ์‚ฌ์ดํŠธ ๋น ๋ฅด๋„ค!"๋ผ๊ณ  ๋งํ•  ๋•Œ์˜ ๊ทธ ๋ฟŒ๋“ฏํ•จ์ด๋ž€! ๐Ÿ˜Š

๐Ÿš€ ์‹ค์ฒœ์ด ์ค‘์š”ํ•ด! ์ด ๊ธ€์„ ์ฝ๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋Š” ๋ถ€์กฑํ•ด. ์ง์ ‘ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ , ํ”„๋กœํŒŒ์ผ๋งํ•˜๊ณ , ์ตœ์ ํ™”ํ•ด๋ด์•ผ ์ง„์งœ ์‹ค๋ ฅ์ด ๋Š˜์–ด. ์˜ค๋Š˜ ๋‹น์žฅ ์ž‘์€ ํ”„๋กœ์ ํŠธ๋ผ๋„ ์‹œ์ž‘ํ•ด๋ณด์ž!

๋ง‰ํžˆ๋Š” ๋ถ€๋ถ„์ด ์žˆ์œผ๋ฉด ๊ฐœ๋ฐœ์ž ์ปค๋ฎค๋‹ˆํ‹ฐ์— ์งˆ๋ฌธํ•˜๊ณ , ๋‹ค๋ฅธ ์‚ฌ๋žŒ์˜ ์ฝ”๋“œ๋„ ์ฝ์–ด๋ณด๊ณ , ๊ณ„์† ํ•™์Šตํ•˜์ž. ์šฐ๋ฆฌ ๋ชจ๋‘ ์„ฑ์žฅํ•˜๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ๋˜์ž! ๐ŸŒฑ
์ด ๊ธ€์ด ๋„์›€์ด ๋˜์—ˆ๊ธธ ๋ฐ”๋ผ! ๊ถ๊ธˆํ•œ ์ ์ด๋‚˜ ์ถ”๊ฐ€๋กœ ์•Œ๊ณ  ์‹ถ์€ ๋‚ด์šฉ์ด ์žˆ์œผ๋ฉด ์–ธ์ œ๋“  ๋ฌผ์–ด๋ด. ํ•จ๊ป˜ ์„ฑ์žฅํ•˜๋Š” ๊ฐœ๋ฐœ์ž ์ปค๋ฎค๋‹ˆํ‹ฐ๋ฅผ ๋งŒ๋“ค์–ด๊ฐ€์ž! ๐ŸŽ‰

Happy coding! ๐Ÿ’ปโœจ
Thank You! ๐ŸŽ‰ ์„ฑ๋Šฅ ์ตœ์ ํ™”์˜ ์—ฌ์ •์„ ํ•จ๊ป˜ํ•ด์ค˜์„œ ๊ณ ๋งˆ์›Œ! ๐Ÿš€ Keep Learning ๐Ÿ’ช Keep Coding ๐ŸŒŸ Keep Growing The journey of a thousand miles begins with a single step.
๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0
๋Œ“๊ธ€์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘์ž…๋‹ˆ๋‹ค.

์•„์ง ๋Œ“๊ธ€์ด ์—†์Šต๋‹ˆ๋‹ค.