μ ν΅μ μΈ νμ΄λ° μΈ‘μ λΆν° μ½μ΄ μΉ λ°μ΄νκ³Ό κ°μ μ΅μ μ¬μ©μ μ€μ¬ μ§νκΉμ§, μΉ μ±λ₯ APIμ λν μ¬μΈ΅ λΆμκ³Ό μ±λ₯μ λν μ 체μ μΈ μκ°μ μν΄ μ΄λ€μ μ°κ²°νλ λ°©λ²μ μμλ΄ λλ€.
μκ³λ₯Ό λμ΄μ: μΉ μ±λ₯ APIμ μ€μ μ¬μ©μ κ²½ν μ°κ²°νκΈ°
λμ§νΈ κ²½μ μμ μλλ λ¨μν κΈ°λ₯μ΄ μλλΌ μ¬μ©μ κ²½νμ κΈ°λ°μ
λλ€. λλ¦° μΉμ¬μ΄νΈλ μ¬μ©μμ λΆλ§, λμ μ΄νλ₯ , κ·Έλ¦¬κ³ μμ΅μ μ§μ μ μΈ μν₯μ λ―ΈμΉ μ μμ΅λλ€. μλ
κ° κ°λ°μλ€μ μ±λ₯μ μΈ‘μ νκΈ° μν΄ window.onload
μ κ°μ νμ΄λ° μ§νμ μμ‘΄ν΄ μμ΅λλ€. νμ§λ§ λΉ λ₯Έ λ‘λ© μκ°μ΄ μ λ§ ν볡ν μ¬μ©μλ₯Ό μλ―Έν κΉμ? λ΅μ 'μλμ€'μΌ λκ° λ§μ΅λλ€.
νμ΄μ§κ° λͺ¨λ κΈ°μ μ 리μμ€λ₯Ό 1μ΄ μ΄λ΄μ λ‘λ μλ£νλλΌλ, μ€μ μ¬μ©μκ° μνΈμμ©μ μλν λλ λλ¦¬κ³ μ¬μ©νκΈ° μ΄λ ΅κ² λκ»΄μ§ μ μμ΅λλ€. μ΄λ¬ν 괴리λ μΉ κ°λ°μ μ€μν μ§ν, μ¦ κΈ°μ μ νμ΄λ° μΈ‘μ μμ μΈκ°μ κ²½νμ μμΉννλ κ²μΌλ‘μ μ νμ κ°μ‘°ν©λλ€. νλ μΉ μ±λ₯μ λ κ°μ§ κ΄μ μ μ΄μΌκΈ°μ λλ€: μΉ μ±λ₯ APIκ° μ 곡νλ μΈλΆνλ μ μμ€ λ°μ΄ν°μ ꡬκΈμ μ½μ΄ μΉ λ°μ΄νκ³Ό κ°μ κ³ μμ€μ μ¬μ©μ μ€μ¬ μ§νμ λλ€.
μ΄ ν¬κ΄μ μΈ κ°μ΄λλ κ·Έ κ°κ·Ήμ λ©μΈ κ²μ λλ€. μ°λ¦¬λ μ§λ¨ λꡬ μν μ νλ κ°λ ₯ν μΉ μ±λ₯ API μ νκ΅°μ νμν κ²μ λλ€. κ·Έλ° λ€μ, μ±λ₯μ΄ μ΄λ»κ² *λκ»΄μ§λμ§* μλ €μ£Όλ μ΅μ μ¬μ©μ κ²½ν μ§νμ λν΄ κΉμ΄ νκ³ λ€ κ²μ λλ€. κ°μ₯ μ€μνκ²λ, μ μμ€ νμ΄λ° λ°μ΄ν°λ₯Ό μ¬μ©νμ¬ μ μΈκ³ κ³ κ°μ μ μ‘°ν μ¬μ©μ κ²½νμ κ·Όλ³Έ μμΈμ μ§λ¨νκ³ μμ νλ λ°©λ²μ 보μ¬μ€μΌλ‘μ¨ μ λ€μ μ°κ²°ν κ²μ λλ€.
κΈ°μ΄: μΉ μ±λ₯ API μ΄ν΄νκΈ°
μΉ μ±λ₯ APIλ μΉ νμ΄μ§μ νμ λ° λ λλ§κ³Ό κ΄λ ¨λ λ§€μ° μμΈνκ³ μ νν νμ΄λ° λ°μ΄ν°μ κ°λ°μκ° μ κ·Όν μ μλλ‘ νλ νμ€νλ λΈλΌμ°μ μΈν°νμ΄μ€ μ§ν©μ λλ€. μ΄κ²λ€μ μ±λ₯ μΈ‘μ μ κΈ°λ°μΌλ‘, λ¨μν μ€ν±μμΉλ₯Ό λμ΄ λ€νΈμν¬ μμ², νμ±, λ λλ§μ 볡μ‘ν μνΈμμ©μ μ΄ν΄ν μ μκ² ν΄μ€λλ€.
Navigation Timing API: νμ΄μ§μ μ¬μ
Navigation Timing APIλ μ£Ό λ¬Έμλ₯Ό λ‘λνλ λ° κ±Έλ¦¬λ μκ°μ λν μμΈν λΆμμ μ 곡ν©λλ€. μ¬μ©μκ° λ§ν¬λ₯Ό ν΄λ¦νλ κ²κ³Ό κ°μ΄ νμμ μμνλ μκ°λΆν° νμ΄μ§κ° μμ ν λ‘λλλ μκ°κΉμ§μ μ΄μ νλ₯Ό ν¬μ°©ν©λλ€. μ΄κ²μ νμ΄μ§ λ‘λ νλ‘μΈμ€μ λν μ°λ¦¬μ 첫 λ²μ§Έμ΄μ κ°μ₯ κΈ°λ³Έμ μΈ μκ°μ λλ€.
κ°λ¨ν μλ°μ€ν¬λ¦½νΈ νΈμΆλ‘ μ΄ λ°μ΄ν°μ μ κ·Όν μ μμ΅λλ€:
const navigationEntry = performance.getEntriesByType('navigation')[0];
console.log(navigationEntry.toJSON());
μ΄κ²μ νμμ€ν¬νλ‘ κ°λ μ°¬ κ°μ²΄λ₯Ό λ°νν©λλ€. λͺ κ°μ§ μ£Όμ μμ±μ λ€μκ³Ό κ°μ΅λλ€:
- fetchStart: λΈλΌμ°μ κ° λ¬Έμ κ°μ Έμ€κΈ°λ₯Ό μμν λ.
- responseStart: λΈλΌμ°μ κ° μλ²λ‘λΆν° μλ΅μ 첫 λ°μ΄νΈλ₯Ό λ°μ λ.
fetchStart
μresponseStart
μ¬μ΄μ μκ°μ μ’ μ’ μ΅μ΄ λ°μ΄νΈκΉμ§μ μκ°(TTFB)μ΄λΌκ³ ν©λλ€. - domContentLoadedEventEnd: μ€νμΌμνΈ, μ΄λ―Έμ§, μλΈνλ μμ λ‘λ©μ κΈ°λ€λ¦¬μ§ μκ³ μ΄κΈ° HTML λ¬Έμκ° μμ ν λ‘λλκ³ νμ±λμμ λ.
- loadEventEnd: νμ΄μ§μ λͺ¨λ 리μμ€(μ΄λ―Έμ§, CSS λ± ν¬ν¨)κ° μμ ν λ‘λλμμ λ.
μ€λ«λμ loadEventEnd
λ ν©κΈ νμ€μ΄μμ΅λλ€. κ·Έλ¬λ κ·Έ νκ³λ μ¬κ°ν©λλ€: μ¬μ©μκ° μλ―Έ μλ μ½ν
μΈ λ₯Ό *보λ* μμ μ΄λ νμ΄μ§μ *μνΈμμ©*ν μ μλ μμ μ λν΄μλ μ무κ²λ λ§ν΄μ£Όμ§ μμ΅λλ€. μ΄κ²μ κΈ°μ μ μΈ μ΄μ νμ΄μ§, μΈκ°μ μΈ μ΄μ νκ° μλλλ€.
Resource Timing API: κ΅¬μ± μμ λΆν΄νκΈ°
μΉ νμ΄μ§λ κ±°μ λ¨μΌ νμΌμ΄ μλλλ€. HTML, CSS, μλ°μ€ν¬λ¦½νΈ, μ΄λ―Έμ§, ν°νΈ, API νΈμΆμ μ§ν©μ²΄μ λλ€. Resource Timing APIλ₯Ό μ¬μ©νλ©΄ μ΄λ¬ν κ°λ³ 리μμ€ κ°κ°μ λν λ€νΈμν¬ νμ΄λ°μ κ²μ¬ν μ μμ΅λλ€.
μ΄κ²μ λ³λͺ© νμμ μλ³νλ λ° λ§€μ° κ°λ ₯ν©λλ€. λ€λ₯Έ λλ₯μ μ½ν μΈ μ μ‘ λ€νΈμν¬(CDN)μμ μ¨ ν¬κ³ μ΅μ νλμ§ μμ νμ΄λ‘ μ΄λ―Έμ§κ° μ΄κΈ° λ λλ§μ λ¦μΆκ³ μμ΅λκΉ? μλνν° λΆμ μ€ν¬λ¦½νΈκ° λ©μΈ μ€λ λλ₯Ό μ°¨λ¨νκ³ μμ΅λκΉ? Resource Timingμ μ΄λ¬ν μ§λ¬Έμ λ΅νλ λ° λμμ μ€λλ€.
λ€μκ³Ό κ°μ΄ λͺ¨λ 리μμ€ λͺ©λ‘μ μ»μ μ μμ΅λλ€:
const resourceEntries = performance.getEntriesByType('resource');
resourceEntries.forEach(resource => {
if (resource.duration > 200) { // 200ms μ΄μ κ±Έλ¦° 리μμ€ μ°ΎκΈ°
console.log(`λλ¦° 리μμ€: ${resource.name}, μμ μκ°: ${resource.duration}ms`);
}
});
μ£Όμ μμ±μλ name
(리μμ€μ URL), initiatorType
(리μμ€λ₯Ό λ‘λνκ² λ§λ μμΈ, μ: 'img', 'script'), duration
(κ°μ Έμ€λ λ° κ±Έλ¦° μ΄ μκ°)μ΄ ν¬ν¨λ©λλ€.
User Timing API: μ ν리μΌμ΄μ λ‘μ§ μΈ‘μ νκΈ°
λλ‘λ μ±λ₯ λ³λͺ© νμμ΄ μμ°μ λ‘λνλ λ° μλ κ²μ΄ μλλΌ ν΄λΌμ΄μΈνΈ μΈ‘ μ½λ μ체μ μμ΅λλ€. μ±κΈ νμ΄μ§ μ ν리μΌμ΄μ (SPA)μ΄ APIμμ λ°μ΄ν°λ₯Ό λ°μ ν 볡μ‘ν μ»΄ν¬λνΈλ₯Ό λ λλ§νλ λ° μΌλ§λ 걸릴κΉμ? User Timing APIλ₯Ό μ¬μ©νλ©΄ μ ν리μΌμ΄μ λ³ λ§μΆ€ μΈ‘μ μ μμ±ν μ μμ΅λλ€.
μ΄κ²μ λ κ°μ§ μ£Όμ λ©μλλ‘ μλν©λλ€:
- performance.mark(name): μ±λ₯ λ²νΌμ μ΄λ¦μ΄ μ§μ λ νμμ€ν¬νλ₯Ό μμ±ν©λλ€.
- performance.measure(name, startMark, endMark): λ λ§ν¬ μ¬μ΄μ μ§μ μκ°μ κ³μ°νκ³ μ΄λ¦μ΄ μ§μ λ μΈ‘μ μ μμ±ν©λλ€.
μμ: μ ν λͺ©λ‘ μ»΄ν¬λνΈμ λ λλ§ μκ° μΈ‘μ .
// λ°μ΄ν° κ°μ Έμ€κΈ°λ₯Ό μμν λ
performance.mark('product-list-fetch-start');
fetch('/api/products')
.then(response => response.json())
.then(data => {
// κ°μ Έμ¨ ν, λ λλ§νκΈ° μ
performance.mark('product-list-render-start');
renderProductList(data);
// λ λλ§μ΄ μλ£λ μ§ν
performance.mark('product-list-render-end');
// μΈ‘μ μμ±
performance.measure(
'Product List Render Time',
'product-list-render-start',
'product-list-render-end'
);
});
μ΄λ₯Ό ν΅ν΄ μ¬μ©μμ μμ νλ¦μ κ°μ₯ μ€μν μ ν리μΌμ΄μ λΆλΆμ μ λ°νκ² μΈ‘μ ν μ μμ΅λλ€.
PerformanceObserver: νλμ μ΄κ³ ν¨μ¨μ μΈ μ κ·Ό λ°©μ
μ§μμ μΌλ‘ `performance.getEntriesByType()`μ ν΄λ§νλ κ²μ λΉν¨μ¨μ μ λλ€. `PerformanceObserver` APIλ μ±λ₯ μνΈλ¦¬λ₯Ό μμ νλ ν¨μ¬ λ μ’μ λ°©λ²μ μ 곡ν©λλ€. νΉμ μνΈλ¦¬ νμ μ ꡬλ νλ©΄, λΈλΌμ°μ λ ν΄λΉ μνΈλ¦¬κ° κΈ°λ‘λ λ λΉλκΈ°μ μΌλ‘ μ½λ°± ν¨μμ μ립λλ€. μ΄κ²μ μ ν리μΌμ΄μ μ μ€λ²ν€λλ₯Ό μΆκ°νμ§ μκ³ μ±λ₯ λ°μ΄ν°λ₯Ό μμ§νλ κΆμ₯ λ°©λ²μ λλ€.
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(`μνΈλ¦¬ νμ
: ${entry.entryType}, μ΄λ¦: ${entry.name}`);
}
});
observer.observe({ entryTypes: ['resource', 'navigation', 'mark', 'measure'] });
μ΄ μ΅μ λ²λ μμμ μΈκΈν μ ν΅μ μΈ μ§νλΏλ§ μλλΌ λ€μμ λ Όμν νλμ μΈ μ¬μ©μ μ€μ¬ μ§νλ₯Ό μμ§νλ ν΅μ¬μ λλ€.
μ¬μ©μ μ€μ¬μ£Όμλ‘μ μ ν: μ½μ΄ μΉ λ°μ΄ν
νμ΄μ§κ° 2μ΄ λ§μ λ‘λλμλ€λ κ²μ μλ κ²μ μ μ©νμ§λ§, μ€μν μ§λ¬Έμλ λ΅νμ§ λͺ»ν©λλ€: κ·Έ 2μ΄ λμ μ¬μ©μλ λΉ νλ©΄μ λ³΄κ³ μμμκΉμ? νμ΄μ§μ μνΈμμ©ν μ μμμκΉμ, μλλ©΄ λ©μΆ° μμμκΉμ? μ½μΌλ €κ³ ν λ μ½ν μΈ κ° μμμΉ λͺ»νκ² μ΄λ¦¬μ 리 μμ§μμκΉμ?
μ΄λ₯Ό ν΄κ²°νκΈ° μν΄ κ΅¬κΈμ μ½μ΄ μΉ λ°μ΄ν(CWV)μ λμ νμ΅λλ€. μ΄λ λ‘λ©, μνΈμμ©μ±, μκ°μ μμ μ±μ΄λΌλ μΈ κ°μ§ μ£Όμ μ°¨μμμ νμ΄μ§μ μ€μ μ¬μ©μ κ²½νμ μΈ‘μ νλλ‘ μ€κ³λ μ§ν μ§ν©μ λλ€.
μ΅λ μ½ν μΈ ν νμΈνΈ(LCP): μΈμ§λλ λ‘λ© μλ μΈ‘μ
LCPλ λ·°ν¬νΈ λ΄μ 보μ΄λ κ°μ₯ ν° μ΄λ―Έμ§ λλ ν μ€νΈ λΈλ‘μ λ λλ§ μκ°μ μΈ‘μ ν©λλ€. μ΄λ μ¬μ©μκ° νμ΄μ§μ μ£Όμ μ½ν μΈ κ° λ‘λλμλ€κ³ λλΌλ μμ μ λνλ΄λ νλ₯ν λ리 μ§νμ λλ€. μ΄λ "μ΄ νμ΄μ§κ° μ΄μ μ μ©νκ°?"λΌλ μ¬μ©μμ μ§λ¬Έμ μ§μ μ μΌλ‘ λ΅ν©λλ€.
- μ’μ: 2.5μ΄ λ―Έλ§
- κ°μ νμ: 2.5μ΄μ 4.0μ΄ μ¬μ΄
- λμ¨: 4.0μ΄ μ΄κ³Ό
`loadEventEnd`μ λ¬λ¦¬, LCPλ μ¬μ©μκ° λ¨Όμ 보λ κ²μ μ΄μ μ λ§μΆλ―λ‘ μΈμ§λλ λ‘λ μλλ₯Ό ν¨μ¬ λ μ ννκ² λ°μν©λλ€.
λ€μ νμΈνΈμ λν μνΈμμ©(INP): λ°μμ± μΈ‘μ
INPλ μ΅μ΄ μ λ ₯ μ§μ°(FID)μ νμ μ§νμ΄λ©° 2024λ 3μμ 곡μ μ½μ΄ μΉ λ°μ΄νμ΄ λμμ΅λλ€. FIDκ° *첫 λ²μ§Έ* μνΈμμ©μ μ§μ° μκ°λ§ μΈ‘μ ν λ°λ©΄, INPλ νμ΄μ§ μλͺ μ£ΌκΈ° λμμ *λͺ¨λ * μ¬μ©μ μνΈμμ©(ν΄λ¦, ν, ν€ μ λ ₯)μ μ§μ° μκ°μ μΈ‘μ ν©λλ€. μ΄λ κ°μ₯ κΈ΄ μνΈμμ©μ λ³΄κ³ νμ¬ μ¬μ©μκ° κ²½ννλ μ΅μ μ λ°μμ±μ ν¨κ³Όμ μΌλ‘ μλ³ν©λλ€.
INPλ μ¬μ©μ μ λ ₯λΆν° λ€μ νλ μμ΄ κ·Έλ €μ§ λκΉμ§μ μ 체 μκ°μ μΈ‘μ νμ¬ μκ°μ νΌλλ°±μ λ°μν©λλ€. μ΄λ "μ΄ λ²νΌμ ν΄λ¦νλ©΄ νμ΄μ§κ° λΉ λ₯΄κ² λ°μνλμ?"λΌλ μ¬μ©μμ μ§λ¬Έμ λ΅ν©λλ€.
- μ’μ: 200λ°λ¦¬μ΄ λ―Έλ§
- κ°μ νμ: 200msμ 500ms μ¬μ΄
- λμ¨: 500ms μ΄κ³Ό
λμ INPλ μΌλ°μ μΌλ‘ λ©μΈ μ€λ λκ° λ°λΉ μ λ°μνλ©°, μ₯μκ° μ€νλλ μλ°μ€ν¬λ¦½νΈ μμ μ΄ λΈλΌμ°μ κ° μ¬μ©μ μ λ ₯μ μλ΅νλ κ²μ λ°©ν΄ν©λλ€.
λμ λ μ΄μμ μ΄λ(CLS): μκ°μ μμ μ± μΈ‘μ
CLSλ νμ΄μ§μ μκ°μ μμ μ±μ μΈ‘μ ν©λλ€. λ‘λ© κ³Όμ μμ μ½ν μΈ κ° μκΈ°μΉ μκ² νλ©΄μμ μΌλ§λ λ§μ΄ μμ§μ΄λμ§λ₯Ό μμΉνν©λλ€. λμ CLS μ μλ μ¬μ©μμ λΆλ§μ μ λ°νλ μΌλ°μ μΈ μμΈμ λλ€. μλ₯Ό λ€μ΄ λ²νΌμ ν΄λ¦νλ €κ³ ν λ κ·Έ μμ κ΄κ³ κ° λ‘λλμ΄ λ²νΌμ μλλ‘ λ°μ΄λ΄κ³ κ²°κ΅ κ΄κ³ λ₯Ό ν΄λ¦νκ² λλ κ²½μ°μ λλ€.
CLSλ "μμλ€μ΄ μ¬λ°©μΌλ‘ μμ§μ΄μ§ μκ³ μ΄ νμ΄μ§λ₯Ό μ¬μ©ν μ μλμ?"λΌλ μ¬μ©μμ μ§λ¬Έμ λ΅ν©λλ€.
- μ’μ: 0.1 λ―Έλ§
- κ°μ νμ: 0.1κ³Ό 0.25 μ¬μ΄
- λμ¨: 0.25 μ΄κ³Ό
λμ CLSμ μΌλ°μ μΈ μμΈμΌλ‘λ ν¬κΈ°κ° μ§μ λμ§ μμ μ΄λ―Έμ§λ iframe, λ¦κ² λ‘λλλ μΉ ν°νΈ, λλ 곡κ°μ μμ½νμ§ μκ³ νμ΄μ§μ λμ μΌλ‘ μ£Όμ λλ μ½ν μΈ λ±μ΄ μμ΅λλ€.
κ°κ·Ή λ©μ°κΈ°: APIλ₯Ό μ¬μ©ν΄ μ μ‘°ν μ¬μ©μ κ²½ν μ§λ¨νκΈ°
μ¬κΈ°μ λͺ¨λ κ²μ΄ νλλ‘ ν©μ³μ§λλ€. μ½μ΄ μΉ λ°μ΄νμ *무μμ΄* μΌμ΄λ¬λμ§(μ: λλ¦° LCP) μλ €μ£Όκ³ , μΉ μ±λ₯ APIλ *μ* κ·Έκ²μ΄ μΌμ΄λ¬λμ§ μλ €μ€λλ€. μ΄ λμ κ²°ν©ν¨μΌλ‘μ¨ μ°λ¦¬λ λ¨μν μ±λ₯μ κ΄μ°°νλ κ²μμ μ κ·Ήμ μΌλ‘ μ§λ¨νκ³ μμ νλ κ²μΌλ‘ μ νν μ μμ΅λλ€.
λλ¦° LCP μ§λ¨νκΈ°
μ€μ μ¬μ©μ λͺ¨λν°λ§(RUM) λꡬμμ νΉμ μ§μ μ¬μ©μμ LCPκ° 4.5μ΄λ‘ μ μ‘°νλ€κ³ λ³΄κ³ λμλ€κ³ μμν΄ λ΄ μλ€. μ΄λ»κ² ν΄κ²°ν κΉμ? LCP μκ°μ κ΅¬μ± μμλ‘ λΆν΄ν΄μΌ ν©λλ€.
- μ΅μ΄ λ°μ΄νΈκΉμ§μ μκ°(TTFB): μλ² μλ΅μ΄ λλ¦°κ°μ? Navigation Timing APIλ₯Ό μ¬μ©νμΈμ. `responseStart - requestStart`μ μ§μ μκ°μ μ νν TTFBλ₯Ό μ 곡ν©λλ€. μ΄ κ°μ΄ λλ€λ©΄ λ¬Έμ λ νλ‘ νΈμλκ° μλλΌ λ°±μλ, μλ² κ΅¬μ± λλ λ°μ΄ν°λ² μ΄μ€μ μμ΅λλ€.
- 리μμ€ λ‘λ μ§μ° λ° μκ°: LCP μμ μμ²΄κ° λλ¦¬κ² λ‘λλλμ? λ¨Όμ LCP μμ(μ: νμ΄λ‘ μ΄λ―Έμ§)λ₯Ό μλ³νμΈμ. `'largest-contentful-paint'`μ λν `PerformanceObserver`λ₯Ό μ¬μ©νμ¬ μμ μ체λ₯Ό μ»μ μ μμ΅λλ€. κ·Έλ° λ€μ Resource Timing APIλ₯Ό μ¬μ©νμ¬ ν΄λΉ μμμ URLμ λν μνΈλ¦¬λ₯Ό μ°ΎμΌμΈμ. νμλΌμΈμ λΆμνμΈμ: `connectStart`μμ `connectEnd`κΉμ§ κΈ΄ μκ°μ΄ κ±Έλ Έλμ(λλ¦° λ€νΈμν¬)? `responseStart`μμ `responseEnd`κΉμ§ κΈΈμλμ(κ±°λν νμΌ ν¬κΈ°)? CSSλ μλ°μ€ν¬λ¦½νΈμ κ°μ λ€λ₯Έ λ λλ§ μ°¨λ¨ λ¦¬μμ€μ μν΄ `fetchStart`κ° μ§μ°λμλμ?
- μμ λ λλ§ μ§μ°: 리μμ€ λ‘λ©μ΄ μλ£λ ν μ€μ λ‘ νλ©΄μ κ·Έλ €μ§ λκΉμ§μ μκ°μ λλ€. μ΄λ λ©μΈ μ€λ λκ° ν° μλ°μ€ν¬λ¦½νΈ λ²λ€μ μ€ννλ λ± λ€λ₯Έ μμ μΌλ‘ λ°λΉ μ λ°μν μ μμ΅λλ€.
Navigation λ° Resource Timingμ μ¬μ©νλ©΄ λλ¦° LCPκ° λλ¦° μλ², λ λλ§ μ°¨λ¨ μ€ν¬λ¦½νΈ λλ κ±°λνκ³ μ΅μ νλμ§ μμ μ΄λ―Έμ§ λλ¬ΈμΈμ§ μ νν μ°ΎμλΌ μ μμ΅λλ€.
μ μ‘°ν INP μ‘°μ¬νκΈ°
μ¬μ©μλ€μ΄ "μ₯λ°κ΅¬λμ μΆκ°" λ²νΌμ ν΄λ¦νλ κ²μ΄ λλ¦¬κ² λκ»΄μ§λ€κ³ λΆνν©λλ€. INP μ§νκ° "λμ¨" λ²μμ μμ΅λλ€. μ΄κ²μ κ±°μ νμ λ©μΈ μ€λ λ λ¬Έμ μ λλ€.
- κΈ΄ μμ μλ³: Long Tasks APIκ° μ£Όμ λꡬμ λλ€. λ©μΈ μ€λ λμμ 50ms μ΄μ 걸리λ λͺ¨λ μμ μ λ³΄κ³ νλλ°, μ΄λ³΄λ€ κΈΈλ©΄ μ¬μ©μμκ² λμ λλ μ§μ°μ μ΄λν μνμ΄ μμ΅λλ€. `'longtask'` μνΈλ¦¬λ₯Ό μμ νλλ‘ `PerformanceObserver`λ₯Ό μ€μ νμΈμ.
- μ¬μ©μ νλκ³Ό μ°κ΄μν€κΈ°: κΈ΄ μμ μ μ¬μ©μκ° μνΈμμ©νλ €κ³ ν λ λ°μν΄μΌλ§ λ¬Έμ μ λλ€. INP μ΄λ²€νΈμ `startTime`(`'event'` νμ μ λν `PerformanceObserver`λ‘ κ΄μ°°)μ λΉμ·ν μκ°μ λ°μν κΈ΄ μμ μ νμ΄λ°κ³Ό μ°κ΄μν¬ μ μμ΅λλ€. μ΄κ²μ μ νν μ΄λ€ μλ°μ€ν¬λ¦½νΈ ν¨μκ° μ¬μ©μμ μνΈμμ©μ μ°¨λ¨νλμ§ μλ €μ€λλ€.
- νΉμ νΈλ€λ¬ μΈ‘μ : User Timing APIλ₯Ό μ¬μ©νμ¬ λ μΈλΆννμΈμ. "μ₯λ°κ΅¬λμ μΆκ°"μ 'click' νΈλ€λ¬μ κ°μ μ€μν μ΄λ²€νΈ νΈλ€λ¬λ₯Ό `performance.mark()`μ `performance.measure()`λ‘ κ°μΈμΈμ. μ΄κ²μ μμ μ μ½λκ° μ€νλλ λ° μΌλ§λ 걸리λμ§, κ·Έλ¦¬κ³ κ·Έκ²μ΄ κΈ΄ μμ μ μμΈμΈμ§ μ νν μλ €μ€ κ²μ λλ€.
λμ CLS ν΄κ²°νκΈ°
μ¬μ©μλ€μ΄ λͺ¨λ°μΌ κΈ°κΈ°μμ κΈ°μ¬λ₯Ό μ½λ λμ ν μ€νΈκ° μ΄λ¦¬μ 리 μμ§μΈλ€κ³ λ³΄κ³ ν©λλ€. CLS μ μλ 0.3μ λλ€.
- λ μ΄μμ μ΄λ κ΄μ°°: `PerformanceObserver`λ₯Ό μ¬μ©νμ¬ `'layout-shift'` μνΈλ¦¬λ₯Ό μμ νμΈμ. κ° μνΈλ¦¬μλ `value`(CLS μ μμ λν κΈ°μ¬λ)μ μ΄λν DOM μμμΈ `sources` λͺ©λ‘μ΄ μμ΅λλ€. μ΄κ²μ *무μμ΄* μμ§μλμ§ μλ €μ€λλ€.
- μμΈ λ¦¬μμ€ μ°ΎκΈ°: λ€μ μ§λ¬Έμ *μ* μμ§μλκ°μ λλ€. μΌλ°μ μΈ μ΄μ λ 리μμ€κ° λ¦κ² λ‘λλμ΄ λ€λ₯Έ μ½ν μΈ λ₯Ό μλλ‘ λ°μ΄λ΄λ κ²μ λλ€. `layout-shift` μνΈλ¦¬μ `startTime`μ Resource Timing APIμ μνΈλ¦¬λ€μ `responseEnd` μκ°κ³Ό μ°κ΄μν¬ μ μμ΅λλ€. κ΄κ³ μ€ν¬λ¦½νΈλ ν° μ΄λ―Έμ§ λ‘λ©μ΄ λλ μ§νμ λ μ΄μμ μ΄λμ΄ λ°μνλ©΄, λ²μΈμ μ°Ύμμ κ°λ₯μ±μ΄ λμ΅λλ€.
- μ¬μ ν΄κ²°μ±
: ν΄κ²°μ±
μ μ’
μ’
μ΄λ―Έμ§μ κ΄κ³ μ ν¬κΈ°λ₯Ό μ 곡νκ±°λ(`
`) λμ μ½ν μΈ κ° λ‘λλκΈ° μ μ νμ΄μ§μ 곡κ°μ μμ½νλ κ²μ ν¬ν¨ν©λλ€. Resource Timingμ μ΄λ€ 리μμ€μ λν΄ μ¬μ μ μ‘°μΉν΄μΌ νλμ§ μλ³νλ λ° λμμ μ€λλ€.
μ€μ©μ μΈ κ΅¬ν: κΈλ‘λ² λͺ¨λν°λ§ μμ€ν ꡬμΆνκΈ°
μ΄λ¬ν APIλ₯Ό μ΄ν΄νλ κ²κ³Ό μ μΈκ³ μ¬μ©μ κΈ°λ°μ κ²½νμ λͺ¨λν°λ§νκΈ° μν΄ λ°°ν¬νλ κ²μ λ€μ λ¨κ³μ λλ€. μ΄κ²μ΄ λ°λ‘ μ€μ μ¬μ©μ λͺ¨λν°λ§(RUM)μ μμμ λλ€.
PerformanceObserver
λ‘ λͺ¨λ κ² ν΅ν©νκΈ°
μ΄ λͺ¨λ μ€μν λ°μ΄ν°λ₯Ό μμ§νκΈ° μν΄ λ¨μΌμ κ°λ ₯ν μ€ν¬λ¦½νΈλ₯Ό λ§λ€ μ μμ΅λλ€. λͺ©νλ μΈ‘μ νλ €λ μ±λ₯μ μν₯μ μ£Όμ§ μμΌλ©΄μ μ§νμ κ·Έ λ§₯λ½μ μμ§νλ κ²μ λλ€.
λ€μμ κ²¬κ³ ν μ΅μ λ² μ€μ μ κ°λ μ μ€λν«μ λλ€:
const collectedMetrics = {};
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.entryType === 'largest-contentful-paint') {
collectedMetrics.lcp = entry.startTime;
} else if (entry.entryType === 'layout-shift') {
collectedMetrics.cls = (collectedMetrics.cls || 0) + entry.value;
} else if (entry.entryType === 'event') {
// μ΄κ²μ INP κ³μ°μ λ¨μνλ 보기μ
λλ€
const duration = entry.duration;
if (duration > (collectedMetrics.inp || 0)) {
collectedMetrics.inp = duration;
}
}
// ... 'longtask'μ κ°μ λ€λ₯Έ μνΈλ¦¬ νμ
μ λν΄μλ λ§μ°¬κ°μ§
}
});
observer.observe({ entryTypes: ['largest-contentful-paint', 'layout-shift', 'event', 'longtask'] });
μμ μ μΌλ‘ λ°μ΄ν° μ μ‘νκΈ°
λ°μ΄ν°λ₯Ό μμ§ν νμλ μ μ₯ λ° λΆμμ μν΄ λΆμ λ°±μλλ‘ λ³΄λ΄μΌ ν©λλ€. νμ΄μ§ μΈλ‘λλ₯Ό μ§μ°μν€κ±°λ νμ 빨리 λ«λ μ¬μ©μμ λ°μ΄ν°λ₯Ό μμ§ μκ³ μ΄ μμ μ μννλ κ²μ΄ μ€μν©λλ€.
`navigator.sendBeacon()` APIκ° μ΄μ μλ²½ν©λλ€. νμ΄μ§κ° μΈλ‘λλλ μ€μλ μλμ λ°μ΄ν°λ₯Ό μλ²λ‘ μμ μ μ΄κ³ λΉλκΈ°μ μΌλ‘ λ³΄λΌ μ μλ λ°©λ²μ μ 곡ν©λλ€. μλ΅μ κΈ°λνμ§ μμΌλ―λ‘ κ°λ³κ³ μ°¨λ¨λμ§ μμ΅λλ€.
window.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
const payload = JSON.stringify(collectedMetrics);
navigator.sendBeacon('/api/performance-analytics', payload);
}
});
κΈλ‘λ² κ΄μ μ μ€μμ±
Lighthouseμ κ°μ μ€νμ€ ν μ€νΈ λꡬλ λ§€μ° μ€μνμ§λ§, ν΅μ λ νκ²½μμ μ€νλ©λλ€. μ΄λ¬ν APIμμ μμ§λ RUM λ°μ΄ν°λ λ€μν κ΅κ°, λ€νΈμν¬ μ‘°κ±΄ λ° μ₯μΉμ κ±Έμ³ μ¬μ©μκ° κ²½ννλ μ€μ νμ€μ μλ €μ€λλ€.
λ°μ΄ν°λ₯Ό λΆμν λλ νμ μΈλΆνν΄μΌ ν©λλ€. λ€μκ³Ό κ°μ μ¬μ€μ λ°κ²¬ν μ μμ΅λλ€:
- μ£Όμ μ΄λ―Έμ§ μλ²κ° λ―Έκ΅μ μκΈ° λλ¬Έμ λΆλ―Έ μ¬μ©μμκ²λ LCPκ° νλ₯νμ§λ§ νΈμ£Ό μ¬μ©μμκ²λ μ μ‘°ν©λλ€.
- μλ°μ€ν¬λ¦½νΈκ° CPUλ₯Ό λ무 λ§μ΄ μ¬μ©νμ¬ μ ν₯ μμ₯μμ μΈκΈ° μλ μ€κΈ μλλ‘μ΄λ κΈ°κΈ°μμ INPκ° λμ΅λλ€.
- CSS λ―Έλμ΄ μΏΌλ¦¬λ‘ μΈν΄ κ΄κ³ ν¬κΈ°κ° λΆμ μ νκ² μ‘°μ λλ νΉμ νλ©΄ ν¬κΈ°μμλ§ CLSκ° λ¬Έμ κ° λ©λλ€.
μ΄λ¬ν μμ€μ μΈλΆνλ ν΅μ°°λ ₯μ μ€μ μ¬μ©μ κΈ°λ°μ΄ μ΄λμ μλ κ°μ₯ ν° μν₯μ λ―ΈμΉ μ΅μ νμ μ°μ μμλ₯Ό μ ν μ μκ² ν΄μ€λλ€.
κ²°λ‘ : μΈ‘μ μμ μλ¬κΉμ§
μΉ μ±λ₯μ μΈκ³λ μ±μν΄μ‘μ΅λλ€. μ°λ¦¬λ λ¨μν κΈ°μ μ νμ΄λ°μμ μ¬μ©μμ μΈμ§λ κ²½νμ λν μ κ΅ν μ΄ν΄λ‘ μ΄λνμ΅λλ€. μ΄ μ¬μ μ μΈ κ°μ§ μ£Όμ λ¨κ³λ₯Ό ν¬ν¨ν©λλ€:
- κ²½ν μΈ‘μ : `PerformanceObserver`λ₯Ό μ¬μ©νμ¬ μ½μ΄ μΉ λ°μ΄ν(LCP, INP, CLS)μ μμ§ν©λλ€. μ΄κ²μ *무μμ΄* μΌμ΄λκ³ μκ³ μ¬μ©μμκ² *μ΄λ»κ² λκ»΄μ§λμ§* μλ €μ€λλ€.
- μμΈ μ§λ¨: κΈ°μ΄μ μΈ νμ΄λ° API(Navigation, Resource, User, Long Tasks)λ₯Ό μ¬μ©νμ¬ λ κΉμ΄ νκ³ λλλ€. μ΄κ²μ κ²½νμ΄ *μ* μ μ‘°νμ§ μλ €μ€λλ€.
- μ λ°νκ² μ‘°μΉ: κ²°ν©λ λ°μ΄ν°λ₯Ό μ¬μ©νμ¬ νΉμ μ¬μ©μ μΈκ·Έλ¨ΌνΈμ λν λ¬Έμ μ κ·Όλ³Έ μμΈμ ν΄κ²°νλ μ 보μ μ κ°ν νμ μ΅μ νλ₯Ό μνν©λλ€.
κ³ μμ€μ μ¬μ©μ μ§νμ μ μμ€μ μ§λ¨ APIλ₯Ό λͺ¨λ μλ¬ν¨μΌλ‘μ¨ μ 체μ μΈ μ±λ₯ μ λ΅μ ꡬμΆν μ μμ΅λλ€. μΆμΈ‘μ λ©μΆκ³ , μ μΈκ³ λͺ¨λ κΈ°κΈ°μ λͺ¨λ μ¬μ©μμκ² κΈ°μ μ μΌλ‘ λΉ λ₯Ό λΏλ§ μλλΌ λΉ λ₯΄κ³ , λ°μμ΄ μ’κ³ , μ¦κ²κ² λκ»΄μ§λ μΉ κ²½νμ μ€κ³νκΈ° μμν©λλ€.