Pink Bobblehead Bunny μ‚¬μš©μžκ°€ μ°½ λ‹«κΈ° μ „, 1초 λ§Œμ— νŽ˜μ΄μ§€ λ„μš°λŠ” λΉ„κ²°
 

μ‚¬μš©μžκ°€ μ°½ λ‹«κΈ° μ „, 1초 λ§Œμ— νŽ˜μ΄μ§€ λ„μš°λŠ” λΉ„κ²°

μ˜ˆμ „μ— ν”„λ‘œμ νŠΈλ₯Ό μ§„ν–‰ν•˜λ©΄μ„œ μ—΄μ‹¬νžˆ λ§Œλ“  κΈ°λŠ₯이 'λŠλ¦¬λ‹€'λŠ” 이유둜 외면받을 λ•Œ κ°€μž₯ μ†μƒν•˜λ”λΌκ³ μš”.

ν˜Ήμ‹œ μ—¬λŸ¬λΆ„λ„ 이런 κ²½ν—˜ μ—†μœΌμ‹ κ°€μš”? κΈ°λŠ₯은 μ™„λ²½ν•œλ°, νŽ˜μ΄μ§€ λ‘œλ”©μ΄ 5초, 10μ΄ˆμ”© κ±Έλ €μ„œ μ‚¬μš©μžκ°€ λ‹€ λ– λ‚˜λ²„λ¦¬λŠ” 상황 λ§μ΄μ—μš”. μ‚¬μš©μžλŠ” μ ˆλŒ€ κΈ°λ‹€λ €μ£Όμ§€ μ•ŠμŠ΅λ‹ˆλ‹€. μ•„λ§ˆμ‘΄μ˜ 연ꡬ에 λ”°λ₯΄λ©΄, λ‘œλ”© μ‹œκ°„μ΄ 단 0.1초만 λŠλ €μ Έλ„ 맀좜이 1% κ°μ†Œν•œλ‹€κ³  ν•΄μš”.

이 κΈ€μ—μ„œλŠ” λ³΅μž‘ν•˜κ³  μ–΄λ €μš΄ 이둠 λŒ€μ‹ , λ‹Ήμž₯ μ—¬λŸ¬λΆ„μ˜ ν”„λ‘œμ νŠΈμ— μ μš©ν•΄μ„œ λˆˆμ— λ„λŠ” μ›Ή μ„±λŠ₯ μ΅œμ ν™”λ₯Ό 이뀄낼 수 μžˆλŠ” ν˜„μ‹€μ μΈ 방법 3κ°€μ§€λ₯Ό μ•Œλ €λ“œλ¦΄κ²Œμš”. 이 글을 λ‹€ 읽고 λ‚˜λ©΄, 더 이상 'λŠλ¦¬λ‹€'λŠ” ν”Όλ“œλ°±μ— μž‘μ•„μ§€μ§€ μ•Šμ„ κ±°μ˜ˆμš”.


1. μ™œ μ›Ή μ„±λŠ₯이 'κΈ°λŠ₯'만큼 μ€‘μš”ν• κΉŒμš”?

μš°λ¦¬λŠ” μ’…μ’… μƒˆλ‘œμš΄ κΈ°λŠ₯을 μΆ”κ°€ν•˜λŠ” 데 μ§‘μ€‘ν•œ λ‚˜λ¨Έμ§€, μ‚¬μš©μžκ°€ κ·Έ κΈ°λŠ₯을 κ²½ν—˜ν•˜κΈ°κΉŒμ§€μ˜ '속도'λ₯Ό κ°„κ³Όν•©λ‹ˆλ‹€. ꡬ글은 쒋은 μ‚¬μš©μž κ²½ν—˜μ„ μ œκ³΅ν•˜λŠ” μ›Ήμ‚¬μ΄νŠΈλ₯Ό νŒλ‹¨ν•˜κΈ° μœ„ν•΄ Core Web VitalsλΌλŠ” 핡심 μ§€ν‘œλ₯Ό μ‚¬μš©ν•˜λŠ”λ°μš”, 그쀑 κ°€μž₯ μ€‘μš”ν•œ μ§€ν‘œ 쀑 ν•˜λ‚˜κ°€ λ°”λ‘œ LCP(Largest Contentful Paint)μž…λ‹ˆλ‹€.

LCPλŠ” μ‰½κ²Œ 말해 'μ‚¬μš©μžκ°€ νŽ˜μ΄μ§€μ— μ ‘μ†ν–ˆμ„ λ•Œ, ν™”λ©΄μ˜ κ°€μž₯ 큰 μ΄λ―Έμ§€λ‚˜ ν…μŠ€νŠΈ 블둝이 ν‘œμ‹œλ˜κΈ°κΉŒμ§€ κ±Έλ¦¬λŠ” μ‹œκ°„'을 μ˜λ―Έν•΄μš”. 이 μ‹œκ°„μ΄ κΈΈμ–΄μ§€λ©΄ μ‚¬μš©μžλŠ” "μ•„, 이 μ‚¬μ΄νŠΈλŠ” λŠλ¦¬κ΅¬λ‚˜"라고 직감적으둜 느끼고, κ°€μ°¨ 없이 λ’€λ‘œ κ°€κΈ° λ²„νŠΌμ„ λˆ„λ¦…λ‹ˆλ‹€. 즉, 아무리 λ©‹μ§„ κΈ°λŠ₯을 λ§Œλ“€μ–΄λ„ LCPκ°€ 느리면 μ‚¬μš©μžλŠ” κ²½ν—˜ν•  기회쑰차 κ°–μ§€ λͺ»ν•˜λŠ” κ±°μ£ . μ›Ή μ„±λŠ₯ μ΅œμ ν™”λŠ” 더 λ§Žμ€ μ‚¬μš©μžλ₯Ό λΆ™μž‘μ•„λ‘κΈ° μœ„ν•œ 첫 번째 κ΄€λ¬Έμž…λ‹ˆλ‹€.


2. 이미지 μ΅œμ ν™”, μš©λŸ‰λ§Œ 쀄이면 λμΌκΉŒμš”?

느린 μ›Ήμ‚¬μ΄νŠΈμ˜ 주범을 찾아보면 μ‹­μ€‘νŒ”κ΅¬ '이미지'κ°€ μžˆμŠ΅λ‹ˆλ‹€. λ§Žμ€ ν”„λ‘ νŠΈμ—”λ“œ κ°œλ°œμžκ°€ 이미지 μ••μΆ•μ˜ μ€‘μš”μ„±μ€ μ•Œμ§€λ§Œ, κ±°κΈ°μ„œ λ©ˆμΆ”λŠ” κ²½μš°κ°€ λ§Žμ•„μš”.

βœ… μ°¨μ„ΈλŒ€ 포맷 WebP μ‚¬μš©ν•˜κΈ°

아직도 JPGλ‚˜ PNG만 κ³ μ§‘ν•˜κ³  μžˆλ‚˜μš”? WebPλŠ” ꡬ글이 κ°œλ°œν•œ μ°¨μ„ΈλŒ€ 이미지 포맷으둜, 같은 ν™”μ§ˆμ„ μœ μ§€ν•˜λ©΄μ„œλ„ μš©λŸ‰μ€ JPG보닀 μ•½ 25~35% 더 μž‘μŠ΅λ‹ˆλ‹€. λŒ€λΆ€λΆ„μ˜ μ΅œμ‹  λΈŒλΌμš°μ €μ—μ„œ μ§€μ›ν•˜λ―€λ‘œ, νŠΉλ³„ν•œ μ΄μœ κ°€ μ—†λ‹€λ©΄ 이미지 포맷을 WebP둜 λ°”κΎΈλŠ” κ²ƒλ§ŒμœΌλ‘œλ„ μ—„μ²­λ‚œ μ„±λŠ₯ κ°œμ„  효과λ₯Ό λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

βœ… Lazy Loading (μ§€μ—° λ‘œλ”©) μ μš©ν•˜κΈ°

ν•œ νŽ˜μ΄μ§€μ— 이미지가 20μž₯ μžˆλ‹€κ³  κ°€μ •ν•΄ λ΄…μ‹œλ‹€. μ‚¬μš©μžκ°€ μ ‘μ†ν•˜μžλ§ˆμž 20μž₯을 μ „λΆ€ 뢈러올 ν•„μš”κ°€ μžˆμ„κΉŒμš”? 화면에 보이지 μ•ŠλŠ” μ΄λ―Έμ§€κΉŒμ§€ μ²˜μŒλΆ€ν„° λ‘œλ”©ν•˜λŠ” 건 μ—„μ²­λ‚œ λ‚­λΉ„μž…λ‹ˆλ‹€. μ΄λ•Œ ν•„μš”ν•œ 것이 λ°”λ‘œ Lazy Loadingμž…λ‹ˆλ‹€.

Lazy Loading은 μ‚¬μš©μžκ°€ μŠ€ν¬λ‘€μ„ λ‚΄λ €μ„œ ν•΄λ‹Ή 이미지가 화면에 보일 λ•Œμ―€ λ‘œλ”©μ„ μ‹œμž‘ν•˜λŠ” κΈ°μˆ μ΄μ—μš”. HTML <img> νƒœκ·Έμ— loading="lazy" 속성 ν•˜λ‚˜λ§Œ μΆ”κ°€ν•˜λ©΄ κ°„λ‹¨ν•˜κ²Œ μ μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.


3. λ Œλ”λ§μ„ λ§‰λŠ” 범인을 μž‘μ•„λΌ

μ›Ή λΈŒλΌμš°μ €κ°€ 화면을 κ·Έλ¦¬λŠ” 과정을 'λ Œλ”λ§'이라고 ν•©λ‹ˆλ‹€. 그런데 μ–΄λ–€ μ½”λ“œλ“€μ€ 이 λ Œλ”λ§ 과정을 쀑간에 λ©ˆμΆ”κ³  μžμ‹ μ„ λ¨Όμ € μ²˜λ¦¬ν•˜λΌκ³  μš”κ΅¬ν•΄μš”. 이λ₯Ό λ Œλ”λ§ 차단 λ¦¬μ†ŒμŠ€(Render-Blocking Resources)'라고 λΆ€λ₯΄λ©°, 주둜 CSS와 JavaScript 파일이 여기에 ν•΄λ‹Ήν•©λ‹ˆλ‹€

βœ… CSSλŠ” head에, JavaScriptλŠ” body 맨 μ•„λž˜μ—

  • CSSλŠ” 화면을 μ–΄λ–»κ²Œ 그릴지 κ²°μ •ν•˜λŠ” '섀계도'μ΄λ―€λ‘œ, λΈŒλΌμš°μ €λŠ” CSSλ₯Ό μ „λΆ€ λ‹€μš΄λ‘œλ“œν•˜κ³  ν•΄μ„ν•˜κΈ° μ „κΉŒμ§€ λ Œλ”λ§μ„ μ‹œμž‘ν•˜μ§€ μ•Šμ•„μš”. κ·Έλž˜μ„œ CSS νŒŒμΌμ€ <head> νƒœκ·Έ μ•ˆμͺ½μ— 두어 μ΅œλŒ€ν•œ 빨리 λΆˆλŸ¬μ˜€λŠ” 게 μ’‹μŠ΅λ‹ˆλ‹€.
  • 반면 JavaScriptλŠ” 주둜 클릭 이벀트 μ²˜λ¦¬λ‚˜ 동적인 κΈ°λŠ₯처럼 λ Œλ”λ§μ΄ λλ‚œ 후에 ν•„μš”ν•œ κ²½μš°κ°€ λ§ŽμŠ΅λ‹ˆλ‹€. λ§Œμ•½ 무거운 JavaScript νŒŒμΌμ„ <head>μ—μ„œ λ¨Όμ € 뢈러였면, κ·Έ νŒŒμΌμ„ λ‹€μš΄λ‘œλ“œν•˜κ³  μ‹€ν–‰ν•˜λŠ” λ™μ•ˆ 화면은 κ·Έλƒ₯ λ°±μ§€λ‘œ λ‚¨μ•„μžˆκ²Œ λ©λ‹ˆλ‹€. λ”°λΌμ„œ νŠΉλ³„ν•œ κ²½μš°κ°€ μ•„λ‹ˆλΌλ©΄ <body> νƒœκ·Έκ°€ λ‹«νžˆκΈ° 직전에 λ‘λŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.

βœ… async와 defer 속성 ν™œμš©ν•˜κΈ°

κ·Έλž˜λ„ JavaScriptλ₯Ό 일찍 λΆˆλŸ¬μ™€μ•Ό ν•œλ‹€λ©΄, asyncλ‚˜ defer 속성을 ν™œμš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 

  • defer: HTML λ Œλ”λ§μ„ 막지 μ•Šκ³  슀크립트λ₯Ό λ‹€μš΄λ‘œλ“œν•œ λ’€, λ Œλ”λ§μ΄ λͺ¨λ‘ λλ‚œ 후에 μ‹€ν–‰ν•©λ‹ˆλ‹€. μ‹€ν–‰ μˆœμ„œκ°€ 보μž₯λ˜μ–΄ μ˜μ‘΄μ„±μ΄ μžˆλŠ” μ—¬λŸ¬ μŠ€ν¬λ¦½νŠΈμ— μ‚¬μš©ν•˜κΈ° μ’‹μŠ΅λ‹ˆλ‹€.
  • async: HTML λ Œλ”λ§μ„ 막지 μ•Šκ³  슀크립트λ₯Ό λ‹€μš΄λ‘œλ“œν•˜κ³ , λ‹€μš΄λ‘œλ“œκ°€ λλ‚˜λ©΄ μ¦‰μ‹œ μ‹€ν–‰ν•©λ‹ˆλ‹€. μ‹€ν–‰ μˆœμ„œκ°€ 보μž₯λ˜μ§€ μ•ŠμœΌλ―€λ‘œ λ‹€λ₯Έ μŠ€ν¬λ¦½νŠΈμ™€ μ˜μ‘΄μ„±μ΄ μ—†λŠ” 경우(ex. κ΄‘κ³ , 뢄석 슀크립트)에 μ‚¬μš©ν•©λ‹ˆλ‹€.

첫 번째 병λͺ© ν˜„μƒ: 반볡문 μ•ˆμ—μ„œμ˜ λ¬΄μžλΉ„ν•œ DOM μ‘°μž‘

APIλ₯Ό 톡해 받은 데이터 배열을 화면에 리슀트둜 λΏŒλ €μ£ΌλŠ” 것은 κ°€μž₯ ν”ν•œ μž‘μ—… 쀑 ν•˜λ‚˜μž…λ‹ˆλ‹€. λ§Žμ€ 뢄듀이 μ•„λž˜μ™€ 같이 μ½”λ“œλ₯Ό μž‘μ„±ν•˜κ³€ ν•©λ‹ˆλ‹€.

const $container = document.getElementById('list-container');
const data = Array.from({ length: 1000 }, (_, i) => `μ•„μ΄ν…œ ${i + 1}`); // 1000개의 데이터

function displayItems() {
  console.time('DOM μ‘°μž‘ μ‹œκ°„');
  data.forEach(itemText => {
    const $li = document.createElement('li');
    $li.textContent = itemText;
    $container.appendChild($li); // πŸ‘ˆ 반볡문 μ•ˆμ—μ„œ 맀번 DOM에 μ ‘κ·Ό!
  });
  console.timeEnd('DOM μ‘°μž‘ μ‹œκ°„');
}

displayItems();

이 μ½”λ“œλŠ” 1000개의 μ•„μ΄ν…œμ„ 화면에 잘 λ³΄μ—¬μ€λ‹ˆλ‹€. ν•˜μ§€λ§Œ 데이터가 수천 개둜 λŠ˜μ–΄λ‚˜λ©΄ λˆˆμ— λ„κ²Œ 버벅거리기 μ‹œμž‘ν•˜μ£ .

μ™œ λŠλ €μ§ˆκΉŒμš”? Reflow와 Repaint의 함정

λ¬Έμ œλŠ” appendChildκ°€ 반볡문 μ•ˆμ—μ„œ 1000번 ν˜ΈμΆœλœλ‹€λŠ” μ μž…λ‹ˆλ‹€. λΈŒλΌμš°μ €κ°€ 화면을 κ·Έλ¦¬λŠ” 과정은 생각보닀 λΉ„μš©μ΄ λΉ„μŒ‰λ‹ˆλ‹€. DOM에 μƒˆλ‘œμš΄ μš”μ†Œκ°€ μΆ”κ°€λ˜κ±°λ‚˜ 크기, μœ„μΉ˜κ°€ 변경될 λ•Œλ§ˆλ‹€ λΈŒλΌμš°μ €λŠ” λ¦¬ν”Œλ‘œμš°(Reflow)와 리페인트(Repaint)λΌλŠ” 과정을 κ±°μΉ©λ‹ˆλ‹€.

  • Reflow (λ¦¬ν”Œλ‘œμš°): μš”μ†Œμ˜ ν¬κΈ°λ‚˜ μœ„μΉ˜κ°€ 변경될 λ•Œ, λ‹€λ₯Έ μš”μ†Œλ“€μ˜ λ ˆμ΄μ•„μ›ƒμ— λ―ΈμΉ˜λŠ” 영ν–₯을 λ‹€μ‹œ κ³„μ‚°ν•˜λŠ” κ³Όμ •. (가ꡬ 재배치)
  • Repaint (리페인트): Reflow ν›„, λ³€κ²½λœ 뢀뢄을 화면에 λ‹€μ‹œ κ·Έλ¦¬λŠ” κ³Όμ •. (λ²½μ§€ λ‹€μ‹œ μΉ ν•˜κΈ°)

μœ„ μ½”λ“œλŠ” <li>λ₯Ό ν•˜λ‚˜ μΆ”κ°€ν•  λ•Œλ§ˆλ‹€ "가ꡬ 재배치"와 "λ²½μ§€ μΉ ν•˜κΈ°"λ₯Ό 1000번 λ°˜λ³΅ν•˜λŠ” 것과 κ°™μŠ΅λ‹ˆλ‹€. μ΄λŠ” λΈŒλΌμš°μ €μ—κ²Œ μ—„μ²­λ‚œ 뢀담을 μ€λ‹ˆλ‹€.

μ–΄λ–»κ²Œ μˆ˜μ •ν•΄μ•Ό ν• κΉŒμš”? DocumentFragment μ‚¬μš©ν•˜κΈ°

이 문제λ₯Ό ν•΄κ²°ν•˜λŠ” μ—΄μ‡ λŠ” DocumentFragmentμž…λ‹ˆλ‹€. DocumentFragmentλŠ” DOM의 일뢀λ₯Ό 담을 수 μžˆλŠ” κ°€μƒμ˜ 'λ©”λͺ¨λ¦¬μƒ DOM'μž…λ‹ˆλ‹€. 여기에 λͺ¨λ“  변경사항을 ν•œ λ²ˆμ— λ‹΄μ•„λ‘μ—ˆλ‹€κ°€, μ‹€μ œ DOMμ—λŠ” 단 ν•œ 번만 μΆ”κ°€ν•˜λŠ” λ°©μ‹μž…λ‹ˆλ‹€.

const $container = document.getElementById('list-container');
const data = Array.from({ length: 1000 }, (_, i) => `μ•„μ΄ν…œ ${i + 1}`);

function displayItemsOptimized() {
  console.time('μ΅œμ ν™”λœ DOM μ‘°μž‘ μ‹œκ°„');
  
  const fragment = document.createDocumentFragment(); // πŸ‘ˆ κ°€μƒμ˜ μ»¨ν…Œμ΄λ„ˆ 생성
  
  data.forEach(itemText => {
    const $li = document.createElement('li');
    $li.textContent = itemText;
    fragment.appendChild($li); // πŸ‘ˆ λ©”λͺ¨λ¦¬μƒμ—μ„œλ§Œ μž‘μ—…
  });

  $container.appendChild(fragment); // πŸ‘ˆ μ‹€μ œ DOM 접근은 단 ν•œ 번!
  
  console.timeEnd('μ΅œμ ν™”λœ DOM μ‘°μž‘ μ‹œκ°„');
}

displayItemsOptimized();

μ΅œμ ν™”λŠ” μ‹€μ œλ‘œ μ–΄λ–»κ²Œ λ™μž‘ν•˜λ‚˜μš”?

DocumentFragmentλŠ” μ‹€μ œ DOM 트리의 일뢀가 μ•„λ‹™λ‹ˆλ‹€. λ”°λΌμ„œ fragment 내뢀에 μš”μ†Œλ₯Ό 아무리 μΆ”κ°€ν•˜κ³  변경해도 Reflowλ‚˜ Repaintκ°€ λ°œμƒν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λͺ¨λ“  <li> μš”μ†Œκ°€ μ€€λΉ„λœ fragmentλ₯Ό λ§ˆμ§€λ§‰μ— $container에 appendChildν•˜λŠ” μˆœκ°„, λΈŒλΌμš°μ €λŠ” 단 ν•œ 번의 Reflow/Repaint만으둜 λͺ¨λ“  변경사항을 화면에 κ·Έλ €λƒ…λ‹ˆλ‹€. 1000번의 λΉ„νš¨μœ¨μ μΈ μž‘μ—…μ„ 단 ν•œ 번의 효율적인 μž‘μ—…μœΌλ‘œ λ°”κΎΌ μ…ˆμ΄μ£ .


두 번째 병λͺ© ν˜„μƒ: 수백, 수천 개의 이벀트 λ¦¬μŠ€λ„ˆ

이제 μœ„μ—μ„œ λ§Œλ“  리슀트의 각 μ•„μ΄ν…œμ„ 클릭할 수 있게 λ§Œλ“€μ–΄μ•Ό ν•©λ‹ˆλ‹€. κ°€μž₯ 직관적인 방법은 각 <li> μš”μ†Œμ— addEventListenerλ₯Ό μΆ”κ°€ν•˜λŠ” 것이겠죠.

// ... displayItems() ν•¨μˆ˜ λ‚΄λΆ€ ...
data.forEach(itemText => {
  const $li = document.createElement('li');
  $li.textContent = itemText;
  
  // πŸ‘ˆ 1000개의 μš”μ†Œμ— 각각 이벀트 λ¦¬μŠ€λ„ˆλ₯Ό 등둝
  $li.addEventListener('click', () => {
    console.log(`${itemText} 클릭됨!`);
  });
  
  $container.appendChild($li);
});

μ™œ λŠλ €μ§ˆκΉŒμš”? λ©”λͺ¨λ¦¬ λˆ„μˆ˜

이벀트 λ¦¬μŠ€λ„ˆ ν•˜λ‚˜ν•˜λ‚˜λŠ” μž‘μ€ ν•¨μˆ˜ κ°μ²΄μž…λ‹ˆλ‹€. ν•˜μ§€λ§Œ 이것이 1000개, 10000κ°œκ°€ 되면 λ¬΄μ‹œν•  수 μ—†λŠ” λ©”λͺ¨λ¦¬λ₯Ό μ°¨μ§€ν•˜κ²Œ λ©λ‹ˆλ‹€. μ΄λŠ” 특히 저사양 κΈ°κΈ°λ‚˜ λͺ¨λ°”일 ν™˜κ²½μ—μ„œ νŽ˜μ΄μ§€ μ „μ²΄μ˜ λ°˜μ‘μ„±μ„ λ–¨μ–΄λœ¨λ¦¬λŠ” 주범이 λ©λ‹ˆλ‹€. λ˜ν•œ, λ‚˜μ€‘μ— λ™μ μœΌλ‘œ μ•„μ΄ν…œμ΄ 좔가될 λ•Œλ§ˆλ‹€ 또 이벀트 λ¦¬μŠ€λ„ˆλ₯Ό λΆ™μ—¬μ€˜μ•Ό ν•˜λŠ” λ²ˆκ±°λ‘œμ›€λ„ μžˆμŠ΅λ‹ˆλ‹€.

μ–΄λ–»κ²Œ μˆ˜μ •ν•΄μ•Ό ν• κΉŒμš”? 이벀트 μœ„μž„(Event Delegation) ν™œμš©ν•˜κΈ°

해결책은 이벀트 μœ„μž„ νŒ¨ν„΄μ— μžˆμŠ΅λ‹ˆλ‹€. 각 μžμ‹ μš”μ†Œ(<li>)에 이벀트λ₯Ό ν•˜λ‚˜μ”© λ‹¬μ•„μ£ΌλŠ” λŒ€μ‹ , κ·Έλ“€μ˜ λΆ€λͺ¨ μš”μ†Œ(<ul> 즉, $container)에 단 ν•˜λ‚˜μ˜ 이벀트 λ¦¬μŠ€λ„ˆλ₯Ό λ‹€λŠ” κ²ƒμž…λ‹ˆλ‹€.

μ΅œμ ν™”λœ μ½”λ“œ μ˜ˆμ‹œ

const $container = document.getElementById('list-container');

// ... (displayItemsOptimized ν•¨μˆ˜λ‘œ 리슀트λ₯Ό λ Œλ”λ§ν•œ ν›„) ...

// πŸ‘ˆ λΆ€λͺ¨ μš”μ†Œμ— 단 ν•˜λ‚˜μ˜ 이벀트 λ¦¬μŠ€λ„ˆλ₯Ό 등둝!
$container.addEventListener('click', (event) => {
  // 클릭된 μš”μ†Œκ°€ LIκ°€ λ§žλŠ”μ§€ 확인
  if (event.target.tagName === 'LI') {
    console.log(`${event.target.textContent} 클릭됨! (μœ„μž„)`);
  }
});

μ΅œμ ν™”λŠ” μ‹€μ œλ‘œ μ–΄λ–»κ²Œ λ™μž‘ν•˜λ‚˜μš”?

μ΄λŠ” 이벀트 버블링(Event Bubbling)μ΄λΌλŠ” λΈŒλΌμš°μ €μ˜ λ™μž‘ 원리λ₯Ό μ΄μš©ν•œ κ²ƒμž…λ‹ˆλ‹€. μžμ‹ μš”μ†Œμ—μ„œ λ°œμƒν•œ μ΄λ²€νŠΈλŠ” DOM 트리λ₯Ό 따라 λΆ€λͺ¨ μš”μ†Œλ‘œ, 또 κ·Έ λΆ€λͺ¨ μš”μ†Œλ‘œ κ³„μ†ν•΄μ„œ μ „νŒŒλ˜λŠ”(마치 λ¬Όκ±°ν’ˆμ²˜λŸΌ μ˜¬λΌκ°€λŠ”) νŠΉμ§•μ΄ μžˆμŠ΅λ‹ˆλ‹€.

μš°λ¦¬λŠ” 이 원리λ₯Ό μ΄μš©ν•΄ λΆ€λͺ¨μΈ $containerμ—μ„œ λͺ¨λ“  μžμ‹(<li>)의 클릭 이벀트λ₯Ό 'μž‘μ•„μ±„λŠ”' κ²ƒμž…λ‹ˆλ‹€. event.target ν”„λ‘œνΌν‹°λ₯Ό μ‚¬μš©ν•˜λ©΄ μ‹€μ œλ‘œ μ΄λ²€νŠΈκ°€ μ‹œμž‘λœ μš”μ†Œλ₯Ό μ •ν™•νžˆ μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€. 결과적으둜 단 ν•˜λ‚˜μ˜ 이벀트 λ¦¬μŠ€λ„ˆλ§ŒμœΌλ‘œ 수천 개의 μžμ‹ μš”μ†Œλ₯Ό λͺ¨λ‘ μ œμ–΄ν•  수 있게 λ˜μ–΄, λ©”λͺ¨λ¦¬ μ‚¬μš©λŸ‰μ„ 획기적으둜 쀄이고 μ½”λ“œλ„ 훨씬 κΉ”λ”ν•΄μ§‘λ‹ˆλ‹€.


🀯 μ΄κ²ƒλ§Œμ€ ν”Όν•˜μ„Έμš”!

κ°€μž₯ ν”νžˆ ν•˜λŠ” μ‹€μˆ˜ 쀑 ν•˜λ‚˜λŠ” λΈŒλΌμš°μ € 캐싱(Browser Caching)을 μ „ν˜€ κ³ λ €ν•˜μ§€ μ•ŠλŠ” κ²ƒμž…λ‹ˆλ‹€. μ‚¬μš©μžκ°€ 우리 μ›Ήμ‚¬μ΄νŠΈμ— μž¬λ°©λ¬Έν–ˆμ„ λ•Œ, 둜고 μ΄λ―Έμ§€λ‚˜ CSS 파일처럼 λ°”λ€Œμ§€ μ•ŠλŠ” νŒŒμΌλ“€μ„ 또 λ‹€μš΄λ‘œλ“œν•˜κ²Œ λ§Œλ“œλŠ” 건 λΆˆν•„μš”ν•œ λ‚­λΉ„μž…λ‹ˆλ‹€.

μ„œλ²„ 섀정을 톡해 이런 정적 파일(Static Assets)에 μ μ ˆν•œ μΊμ‹œ 정책을 μ μš©ν•˜λ©΄, λΈŒλΌμš°μ €λŠ” νŒŒμΌμ„ ν•œ 번 λ‹€μš΄λ‘œλ“œν•œ λ’€ νŠΉμ • κΈ°κ°„ λ™μ•ˆ λ©”λͺ¨λ¦¬λ‚˜ λ””μŠ€ν¬μ— μ €μž₯ν•΄ 두고 μž¬μ‚¬μš©ν•©λ‹ˆλ‹€. μ΄λŠ” 재방문 μ‹œ νŽ˜μ΄μ§€ λ‘œλ”© 속도λ₯Ό λΉ„μ•½μ μœΌλ‘œ ν–₯μƒν•©λ‹ˆλ‹€. μ›ΉνŒ©(Webpack) 같은 λ²ˆλ“€λŸ¬λ₯Ό μ‚¬μš©ν•œλ‹€λ©΄ 파일 이름에 ν•΄μ‹œ(hash)λ₯Ό μΆ”κ°€ν•˜μ—¬ μΊμ‹œλ₯Ό 효율적으둜 관리할 수 μžˆμœΌλ‹ˆ κΌ­ 확인해 λ³΄μ„Έμš”.