μμ μ νλ‘μ νΈλ₯Ό μ§ννλ©΄μ μ΄μ¬ν λ§λ κΈ°λ₯μ΄ 'λ리λ€'λ μ΄μ λ‘ μΈλ©΄λ°μ λ κ°μ₯ μμνλλΌκ³ μ.
νΉμ μ¬λ¬λΆλ μ΄λ° κ²½ν μμΌμ κ°μ? κΈ°λ₯μ μλ²½νλ°, νμ΄μ§ λ‘λ©μ΄ 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)λ₯Ό μΆκ°νμ¬ μΊμλ₯Ό ν¨μ¨μ μΌλ‘ κ΄λ¦¬ν μ μμΌλ κΌ νμΈν΄ 보μΈμ.
'πΊJS & TS' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
| "μ½λ© 1λ λͺ°λΌλ OK! 10λΆ λ§μ λ΄ λμ€μ½λ μλ²μ λ΄ λ§λ€κΈ°" (0) | 2025.10.01 |
|---|---|
| TypeScriptκ° λλ €μ§λ Nκ°μ§ μ΄μ : μ»΄νμΌ μ±λ₯ μ΅μ ν μ€μ κ°μ΄λ (3) | 2025.08.30 |
| νμ μ€ν¬λ¦½νΈ νμ νΈνμ± (4) | 2025.08.28 |
| π JavaScript thisμ λ°μΈλ© μλ²½ κ°μ΄λ (2) | 2025.08.26 |
| π TypeScriptμ Tailwind CSSλ‘ κ°λ° νκ²½ κ΅¬μΆ (1) | 2025.08.26 |