[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -2,96 +2,210 @@
|
||||
id: wiki-2026-0508-웹-프론트엔드-성능-최적화
|
||||
title: 웹 프론트엔드 성능 최적화
|
||||
category: 10_Wiki/Topics
|
||||
status: needs_review
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [P-Reinforce-AUTO-921FA9]
|
||||
aliases: [Frontend performance, Web perf, Core Web Vitals]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
tags: [auto-reinforced]
|
||||
confidence_score: 0.95
|
||||
verification_status: applied
|
||||
tags: [performance, frontend, core-web-vitals, optimization]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-04-20
|
||||
github_commit: "[P-Reinforce] Continuous Worker - 웹 프론트엔드 성능 최적화"
|
||||
inferred_by: Claude Opus 4.7 (auto-normalize 2026-05-08)
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: unspecified
|
||||
framework: unspecified
|
||||
language: TypeScript
|
||||
framework: Next.js / Vite
|
||||
---
|
||||
|
||||
# [[웹 프론트엔드 성능 최적화|웹 프론트엔드 성능 최적화]]
|
||||
# 웹 프론트엔드 성능 최적화
|
||||
|
||||
## 📌 한 줄 통찰 (The Karpathy Summary)
|
||||
> 웹 프론트엔드 성능 최적화는 브라우저 내 [[JavaScript|JavaScript]] 엔진의 메모리 관리 및 가비지 컬렉션(GC) 효율을 높여 애플리케이션의 지연(Jank)이나 충돌을 방지하는 과정입니다 [1, 2]. 개발자는 메모리 누수 원인을 식별하고 이를 해결하여 렌더링 파이프라인과 메인 스레드 성능을 원활하게 유지해야 합니다 [2, 3]. 이를 위해 [[Chrome DevTools|Chrome DevTools]]와 같은 프로파일링 도구를 적극적으로 활용하여 메모리 할당과 객체 보존 상태를 추적할 수 있습니다 [4, 5].
|
||||
## 매 한 줄
|
||||
> **"매 LCP/INP/CLS 의 3 metric 을 따라 image, JS, layout 을 deliberate optimize"**. 2026 매 INP (Interaction to Next Paint) 가 FID 대체 → 매 long task 의 적극 splitting. 매 RUM 으로 매 측정, lab data 의 보완.
|
||||
|
||||
## 📖 구조화된 지식 (Synthesized Content)
|
||||
* **메모리 누수 패턴 및 원인 파악**
|
||||
프론트엔드 애플리케이션의 주요 성능 저하 원인은 더 이상 필요 없는 객체가 가비지 컬렉터에 의해 회수되지 못하고 참조가 유지되는 메모리 누수입니다 [3, 6]. 대표적인 누수 패턴으로는 문서에서 제거되었으나 JS 변수에 의해 참조되는 분리된 DOM 노드(Detached DOM nodes), 반복적인 렌더링 주기에서 누적되는 이벤트 리스너, 공유 스코프에 의해 유지되는 클로저, 그리고 대상이 제거되었음에도 연결이 끊기지 않은 타이머(setInterval)와 옵저버 등이 있습니다 [7-9]. 특히 단일 페이지 애플리케이션(SPA)의 라우트 전환은 이전 라우트의 리스너나 전역 상태 참조를 제대로 정리하지 않을 경우 가장 빈번한 누수 원인이 됩니다 [10].
|
||||
## 매 핵심
|
||||
|
||||
* **[[Chrome|Chrome]] DevTools를 이용한 진단 및 프로파일링**
|
||||
메모리 누수와 성능 병목을 분석하기 위해 Chrome DevTools의 다양한 기능이 사용됩니다 [5]. '[[Heap Snapshot|Heap Snapshot]]'을 통해 특정 시점의 전체 메모리 객체 그래프를 캡처할 수 있으며, 3개의 스냅샷을 비교하는 기법(Three-snapshot technique)을 사용하여 1회성 할당을 필터링하고 지속적으로 살아남은 누수 의심 객체를 효과적으로 찾을 수 있습니다 [4, 11]. 또한 '[[Allocation Timeline|Allocation Timeline]]'을 활용하여 시간에 따른 할당을 기록하고 스택 트레이스를 분석함으로써, 메모리를 점유하고 있는 객체가 생성된 정확한 코드 위치를 추적할 수 있습니다 [4, 5, 12].
|
||||
### 매 Core Web Vitals (2026)
|
||||
- **LCP (Largest Contentful Paint)**: ≤ 2.5s — 매 hero image/text 의 render 시간.
|
||||
- **INP (Interaction to Next Paint)**: ≤ 200ms — 매 user interaction 의 response.
|
||||
- **CLS (Cumulative Layout Shift)**: ≤ 0.1 — 매 layout 안정성.
|
||||
|
||||
* **가비지 컬렉션(GC) 최적화에 따른 성능 영향**
|
||||
가비지 컬렉션이 과도하게 실행되거나 오래 걸리면 예측할 수 없는 멈춤 현상([[Stop-the-world|Stop-the-world]] pause)을 일으켜 애니메이션과 같은 인터랙티브 시스템의 사용자 경험을 방해하게 됩니다 [13, 14]. V8 엔진은 대부분의 객체가 생성 직후 쓸모없어진다는 '세대별 가설(Generational Hypothesis)'에 기반하여 힙 메모리를 관리합니다 [15, 16]. 최신 V8의 [[Orinoco|Orinoco]] 프로젝트는 병렬(Parallel), 점진적(Incremental), 동시(Concurrent) 기술 및 유휴 시간(Idle-time) GC를 도입하여 메인 스레드의 중단 시간을 최소화함으로써 브라우저가 초당 60프레임 렌더링 등의 프론트엔드 성능을 유지할 수 있도록 최적화하고 있습니다 [17-19].
|
||||
### 매 optimization layer
|
||||
1. **Network**: HTTP/3, CDN, preload, prefetch, brotli.
|
||||
2. **JS**: code splitting, tree shake, defer, async, lazy import.
|
||||
3. **CSS**: critical CSS inline, remove unused, contain.
|
||||
4. **Image**: AVIF/WebP, responsive srcset, lazy, dimension hints.
|
||||
5. **Render**: Server Components, streaming SSR, partial hydration.
|
||||
|
||||
## ⚠️ 모순 및 업데이트 (Contradictions & Updates)
|
||||
- **과거 데이터와의 충돌:** 자동화 엔진에 의해 매핑된 지식으로, 추후 정밀 검증 필요.
|
||||
- **정책 변화:** Programming & Language 분야의 자동 자산화 수행.
|
||||
### 매 응용
|
||||
1. Next.js App Router 매 RSC + streaming.
|
||||
2. Astro 매 island architecture.
|
||||
3. Image CDN (Cloudinary, Cloudflare Images) 매 auto format/size.
|
||||
|
||||
## 🔗 지식 연결 (Graph)
|
||||
- **Related Topics:** `메모리 누수 ([[Memory Leaks|Memory Leaks]])`, `가비지 컬렉션 (Garbage Collection)`, `V8 엔진 ([[V8 Engine|V8 Engine]])`, `Chrome DevTools`, `[[Orinoco|Orinoco]]`
|
||||
- **Projects/Contexts:** `SPA 라우트 전환 (SPA Route Transitions)`, `Three-snapshot technique`, `[[Allocation Timeline|Allocation Timeline]]`
|
||||
- **Contradictions/Notes:** WeakRef나 FinalizationRegistry와 같은 최신 도구들은 메모리 누수를 줄이는 데 도움을 줄 수 있지만, 가비지 컬렉터의 실행이 자체적인 일정에 따라 비결정적(Non-deterministic)으로 동작하므로 애플리케이션의 적절한 수명 주기 관리(Lifecycle [[Management|Management]])를 완전히 대체할 수는 없습니다 [9].
|
||||
## 💻 패턴
|
||||
|
||||
---
|
||||
*Last updated: 2026-04-19*
|
||||
### Image — 매 next/image
|
||||
```tsx
|
||||
import Image from 'next/image';
|
||||
|
||||
---
|
||||
|
||||
## 🤖 LLM 활용 힌트 (How to Use This Knowledge)
|
||||
|
||||
**언제 이 지식을 쓰는가:**
|
||||
- *(TODO)*
|
||||
|
||||
**언제 쓰면 안 되는가:**
|
||||
- *(TODO)*
|
||||
|
||||
## 🧪 검증 상태 (Validation)
|
||||
|
||||
- **정보 상태:** needs_review
|
||||
- **출처 신뢰도:** A
|
||||
- **검토 이유:** *(P-Reinforce Phase 1 자동 정규화. 본문 검증 필요.)*
|
||||
|
||||
## 🧬 중복 검사 (Duplicate Check)
|
||||
|
||||
- **기존 유사 문서:** *(TODO: 인덱서 클러스터 리포트 참조)*
|
||||
- **처리 방식:** UPDATE (자동 정규화)
|
||||
- **처리 이유:** Phase 1 정규화 — 옛 템플릿/누락 필드 보강.
|
||||
|
||||
## 🕓 변경 이력 (Changelog)
|
||||
|
||||
| 날짜 | 변경 내용 | 처리 방식 | 신뢰도 |
|
||||
|------|-----------|-----------|--------|
|
||||
| 2026-05-08 | P-Reinforce Phase 1 정규화 (frontmatter + 헤더 표준화) | UPDATE | A |
|
||||
|
||||
## 💻 코드 패턴 (Code Patterns)
|
||||
|
||||
**패턴 1:** *(TODO: 이 프로젝트 컨벤션 반영한 구조 스켈레톤)*
|
||||
|
||||
```text
|
||||
# TODO
|
||||
<Image
|
||||
src="/hero.jpg"
|
||||
alt="Hero"
|
||||
width={1600}
|
||||
height={900}
|
||||
priority // 매 above-fold
|
||||
sizes="(max-width: 768px) 100vw, 80vw"
|
||||
placeholder="blur"
|
||||
blurDataURL="data:image/jpeg;base64,..."
|
||||
/>
|
||||
```
|
||||
|
||||
## 🤔 의사결정 기준 (Decision Criteria)
|
||||
### Code splitting — 매 dynamic import
|
||||
```typescript
|
||||
// 매 route-level: Next.js 자동
|
||||
// 매 component-level: dynamic
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
**선택 A를 써야 할 때:**
|
||||
- *(TODO)*
|
||||
const HeavyChart = dynamic(() => import('./HeavyChart'), {
|
||||
loading: () => <Skeleton />,
|
||||
ssr: false, // 매 client-only
|
||||
});
|
||||
```
|
||||
|
||||
**선택 B를 써야 할 때:**
|
||||
- *(TODO)*
|
||||
### LCP — 매 preload critical
|
||||
```html
|
||||
<link rel="preload" as="image" href="/hero.avif"
|
||||
imagesrcset="/hero-mobile.avif 480w, /hero.avif 1600w"
|
||||
imagesizes="100vw" fetchpriority="high">
|
||||
|
||||
**기본값:**
|
||||
> *(TODO)*
|
||||
<link rel="preconnect" href="https://cdn.example.com" crossorigin>
|
||||
<link rel="dns-prefetch" href="https://analytics.example.com">
|
||||
```
|
||||
|
||||
## ❌ 안티패턴 (Anti-Patterns)
|
||||
### INP — 매 long task splitting
|
||||
```typescript
|
||||
async function processLargeList(items: Item[]) {
|
||||
for (const [i, item] of items.entries()) {
|
||||
process(item);
|
||||
if (i % 50 === 0) {
|
||||
await new Promise((r) => setTimeout(r, 0)); // 매 yield
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- **[안티패턴]:** *(TODO: 무엇을 하면 안 되는가 + 이유 + 대신 무엇을)*
|
||||
// 매 또는 scheduler API
|
||||
import { scheduler } from 'scheduler';
|
||||
await scheduler.yield();
|
||||
```
|
||||
|
||||
### CLS — 매 dimension reserve
|
||||
```css
|
||||
/* 매 aspect-ratio */
|
||||
.thumb {
|
||||
aspect-ratio: 16 / 9;
|
||||
width: 100%;
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
/* 매 font-display: optional → CLS 0 */
|
||||
@font-face {
|
||||
font-family: 'Inter';
|
||||
src: url('/inter.woff2') format('woff2');
|
||||
font-display: optional; /* 매 swap 매 layout shift, optional 매 안정 */
|
||||
}
|
||||
```
|
||||
|
||||
### Server Components — 매 zero-JS by default
|
||||
```tsx
|
||||
// app/products/page.tsx — Server Component
|
||||
async function ProductsPage() {
|
||||
const products = await db.products.findAll();
|
||||
return (
|
||||
<ul>
|
||||
{products.map((p) => <ProductCard key={p.id} {...p} />)}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
// 매 client component 만 JS 의 hydrate
|
||||
```
|
||||
|
||||
### RUM — 매 web-vitals 수집
|
||||
```typescript
|
||||
import { onLCP, onINP, onCLS } from 'web-vitals';
|
||||
|
||||
function send(metric) {
|
||||
navigator.sendBeacon('/rum',
|
||||
JSON.stringify({ name: metric.name, value: metric.value, id: metric.id })
|
||||
);
|
||||
}
|
||||
|
||||
onLCP(send);
|
||||
onINP(send);
|
||||
onCLS(send);
|
||||
```
|
||||
|
||||
### Critical CSS — 매 inline
|
||||
```html
|
||||
<head>
|
||||
<style>
|
||||
/* 매 above-fold critical CSS — beasties/critters 등으로 추출 */
|
||||
body { margin: 0; font-family: system-ui; }
|
||||
.header { background: #fff; height: 64px; }
|
||||
</style>
|
||||
<link rel="preload" href="/main.css" as="style" onload="this.rel='stylesheet'">
|
||||
</head>
|
||||
```
|
||||
|
||||
### Service Worker — 매 stale-while-revalidate
|
||||
```javascript
|
||||
// sw.js
|
||||
self.addEventListener('fetch', (e) => {
|
||||
if (e.request.destination === 'image') {
|
||||
e.respondWith((async () => {
|
||||
const cache = await caches.open('img-v1');
|
||||
const cached = await cache.match(e.request);
|
||||
const networked = fetch(e.request).then((res) => {
|
||||
cache.put(e.request, res.clone()); return res;
|
||||
});
|
||||
return cached || networked;
|
||||
})());
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## 매 결정 기준
|
||||
| 상황 | Approach |
|
||||
|---|---|
|
||||
| 매 hero image | priority + preload + AVIF |
|
||||
| 매 below-fold widget | dynamic import + ssr:false |
|
||||
| 매 long table | virtualization (react-window) |
|
||||
| 매 large list iteration | scheduler.yield |
|
||||
| 매 third-party script | next/script with strategy |
|
||||
|
||||
**기본값**: RSC + next/image + dynamic + Web Vitals RUM.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Web Performance]] · [[Frontend Architecture]]
|
||||
- 변형: [[INP Optimization]] · [[LCP Optimization]] · [[CLS]]
|
||||
- 응용: [[Next.js]] · [[Astro]] · [[Remix]]
|
||||
- Adjacent: [[Lighthouse]] · [[Web Vitals]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 perf audit (Lighthouse JSON 분석), 매 bundle size review, 매 RUM data triage.
|
||||
**언제 X**: 매 actual measurement (lab/RUM 도구 가 더 정확).
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **모든 component client**: 매 RSC 무시 매 JS bundle 폭발.
|
||||
- **lazy load hero image**: 매 LCP 악화.
|
||||
- **fixed-pixel layout**: 매 mobile 매 CLS.
|
||||
- **synchronous third-party**: 매 main thread block.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (web.dev, Next.js docs, Chrome DevRel).
|
||||
- 신뢰도 A.
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — Core Web Vitals (LCP/INP/CLS), RSC, RUM 패턴 정리 |
|
||||
|
||||
Reference in New Issue
Block a user