Files
2nd/10_Wiki/Topics/Coding/Frontend_SVG_Patterns.md
T
2026-05-09 22:47:42 +09:00

398 lines
8.4 KiB
Markdown

---
id: frontend-svg-patterns
title: SVG — Scaling / Animation / Sprite / React
category: Coding
status: draft
source_trust_level: B
verification_status: conceptual
created_at: 2026-05-09
updated_at: 2026-05-09
tags: [frontend, svg, vector, vibe-coding]
tech_stack: { language: "SVG / CSS / TS", applicable_to: ["Frontend"] }
applied_in: []
aliases: [SVG, vector graphics, SVG sprite, viewBox, lucide-react, animation]
---
# SVG Patterns
> Vector graphics. **Scalable, small, scriptable, themeable**. Icon / illustration / chart / animation. PNG 보다 거의 항상 좋음 (단순 graphic).
## 📖 핵심 개념
- viewBox: coordinate system.
- preserveAspectRatio: scaling.
- currentColor: 부모 색 따름.
- Sprite: 여러 icon 한 file.
## 💻 코드 패턴
### 기본 SVG
```html
<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<circle cx="50" cy="50" r="40" fill="hotpink" />
<text x="50" y="55" text-anchor="middle" fill="white">Hi</text>
</svg>
```
### viewBox 가 핵심
```html
<!-- 항상 viewBox 사용 -->
<svg viewBox="0 0 24 24">
<!-- 0,0 부터 24x24 coordinate -->
</svg>
<!-- width / height 안 — CSS 로 -->
<svg viewBox="0 0 24 24" style="width: 24px; height: 24px;">
```
→ Scalable. CSS 로 size 제어.
### currentColor (theme 친화)
```html
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M5 13l4 4L19 7" />
</svg>
```
```css
.icon { color: blue; } /* SVG fill 도 blue */
.icon:hover { color: red; } /* 자동 hover */
```
→ 부모 color 따름. Theme / dark mode 자동.
### Inline SVG (modern)
```tsx
function CheckIcon() {
return (
<svg viewBox="0 0 24 24" width="24" height="24" fill="currentColor">
<path d="M5 13l4 4L19 7" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round" />
</svg>
);
}
```
### lucide-react (icon library)
```bash
yarn add lucide-react
```
```tsx
import { Heart, Home, Settings, ChevronRight } from 'lucide-react';
<Heart size={24} className="text-red-500" />
<Home className="size-6 text-gray-600" />
```
→ Tree-shakable. 큰 set.
### Icon system (자체)
```tsx
// icons/index.ts
export { default as CheckIcon } from './check.svg';
export { default as CloseIcon } from './close.svg';
// ...
// 사용
import { CheckIcon } from '@/icons';
<CheckIcon className="size-4" />
```
```ts
// vite.config.ts — SVG → React
import svgr from 'vite-plugin-svgr';
plugins: [svgr()];
```
→ SVG file → React component 자동.
### SVG sprite (1 fetch, 많은 icon)
```html
<!-- icons.svg -->
<svg xmlns="http://www.w3.org/2000/svg" style="display:none">
<symbol id="check" viewBox="0 0 24 24">
<path d="M5 13l4 4L19 7" />
</symbol>
<symbol id="close" viewBox="0 0 24 24">
<path d="M6 6L18 18M6 18L18 6" />
</symbol>
</svg>
<!-- Use -->
<svg width="24" height="24"><use href="/icons.svg#check" /></svg>
```
→ 한 fetch — cache. 100 icon 도 OK.
### Stroke-based icon
```html
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10" />
<line x1="12" y1="8" x2="12" y2="12" />
<circle cx="12" cy="16" r="0.5" fill="currentColor" />
</svg>
```
→ Lucide / Tabler / Phosphor 의 style.
### Filled icon
```html
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M12 21l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.18L12 21z" />
</svg>
```
→ Solid icon (Material).
### CSS animation
```html
<svg viewBox="0 0 24 24">
<circle class="loader" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="2" fill="none" />
</svg>
```
```css
.loader {
stroke-dasharray: 60;
stroke-dashoffset: 0;
animation: loading 1s linear infinite;
}
@keyframes loading {
to { stroke-dashoffset: 60; }
}
```
→ SVG path = stroke-dash.
### SMIL animation (built-in)
```html
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="red">
<animate attributeName="r" from="40" to="50" dur="1s" repeatCount="indefinite" />
</circle>
</svg>
```
→ JS 없이 animation. Browser 지원 OK.
### Path morphing (SVGator / GSAP / Lottie)
```ts
// Path A → Path B
gsap.to('#shape', {
attr: { d: 'M10,10 L90,90' },
duration: 1,
});
```
### Logo / illustration
```
Vector design tools:
- Figma → SVG export
- Illustrator
- Inkscape (OSS)
→ Path / shape 직접 export.
```
### Optimization
```bash
# SVGO
npx svgo input.svg
npx svgo *.svg
# 또는 SVGOMG (web)
```
→ 50% 작아지는 보통 — comments / metadata 제거.
### React + SVG
```tsx
// Inline (small icons)
<svg viewBox="0 0 24 24"><path d="..." /></svg>
// React component (vite-plugin-svgr)
import Icon from './icon.svg?react';
<Icon className="size-4" />
// img tag (큰 / 변동 X)
<img src="/logo.svg" alt="Logo" />
// 또는 url
import logoUrl from './logo.svg';
<img src={logoUrl} alt="Logo" />
```
→ Inline = themeable. img = cacheable.
### Charts (SVG-based)
```ts
// d3 / visx — SVG 직접
const path = d3.line()(data.map(d => [d.x, d.y]));
return <path d={path} stroke="blue" fill="none" />;
```
→ SVG = chart 의 자연.
### Patterns / gradients
```html
<svg viewBox="0 0 200 100">
<defs>
<linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="red" />
<stop offset="100%" stop-color="blue" />
</linearGradient>
<pattern id="dots" width="10" height="10" patternUnits="userSpaceOnUse">
<circle cx="5" cy="5" r="2" fill="black" />
</pattern>
</defs>
<rect width="200" height="50" fill="url(#grad)" />
<rect y="50" width="200" height="50" fill="url(#dots)" />
</svg>
```
### Filters
```html
<svg>
<defs>
<filter id="blur">
<feGaussianBlur stdDeviation="3" />
</filter>
<filter id="shadow">
<feDropShadow dx="2" dy="2" stdDeviation="3" />
</filter>
</defs>
<text filter="url(#shadow)">Shadow</text>
</svg>
```
### A11y
```html
<svg role="img" aria-labelledby="title">
<title id="title">Heart icon</title>
<path d="..." />
</svg>
<!-- 또는 decorative -->
<svg aria-hidden="true">...</svg>
```
→ Screen reader 친화.
### 1-line / Tailwind utility
```html
<svg class="size-6 text-red-500">...</svg>
```
→ Tailwind 가 SVG 자연.
### MathML / chart 기타
```
SVG: 자유 형식 vector.
Canvas: pixel — 큰 rendering.
WebGL: 3D / GPU.
→ Static / scalable / theme-friendly = SVG.
```
### Use cases
```
- Icon (Lucide / Heroicons)
- Logo
- Chart (D3 / Visx)
- Illustration
- Loading spinner
- Diagram (Mermaid / draw.io)
- Map / floor plan
```
### Bundle size
```
Inline SVG icon: ~500 bytes
PNG @1x / @2x / @3x: 5-50 KB
→ Icon = SVG 거의 항상.
```
### Generate at build
```ts
// 자동 component generation
import { generateSvgComponents } from 'svg-to-jsx';
generateSvgComponents('./icons/', './src/components/icons/');
```
### Optimization (icon font 보다)
```
Icon font:
+ 1 file load
- A11y 약함
- Fixed color 어려움
- CSS 만 styling
SVG sprite / inline:
+ A11y OK
+ 색 / size 자유
+ Animation 가능
+ Better fallback
→ 2024+ = SVG 가 더 좋음.
```
### Common 사이즈
```
size-4 (16px): inline text icon
size-5 (20px): button icon
size-6 (24px): main icon
size-8 (32px): large
size-12 (48px): hero
```
### Colored icons (multi-color)
```svg
<svg viewBox="0 0 24 24">
<path d="..." fill="red" />
<path d="..." fill="blue" />
</svg>
```
→ Theme 어려움. CSS variable 사용:
```svg
<svg viewBox="0 0 24 24" style="--primary: red; --secondary: blue;">
<path d="..." fill="var(--primary)" />
<path d="..." fill="var(--secondary)" />
</svg>
```
## 🤔 의사결정 기준
| 사용 | 추천 |
|---|---|
| Icon system | Lucide / 자체 SVG sprite |
| Logo | Inline SVG |
| Chart | SVG (D3 / Visx) |
| Illustration | SVG |
| Photo | PNG / WebP / AVIF |
| 3D | WebGL / Three.js |
## ❌ 안티패턴
- **viewBox 없음**: 안 scale.
- **Hard-coded color**: theme X. currentColor.
- **PNG icon (multi-resolution)**: 매 size 별 file. SVG 하나면.
- **Inline SVG 큰 (100+ path)**: HTML bloat. external file.
- **No optimization (raw export)**: 50% 큰.
- **A11y 무시**: title / aria-label.
## 🤖 LLM 활용 힌트
- viewBox + currentColor + sprite.
- Lucide / Heroicons / Tabler 가 modern.
- SVGO 자동 optimize.
- vite-plugin-svgr = React component.
## 🔗 관련 문서
- [[Frontend_Image_Optimization]]
- [[React_Charts_Library_Comparison]]
- [[Frontend_A11y_Testing]]