이 글은 SVG(Scalable Vector Graphic)에 대한 전반적인 내용을 다룹니다. SVG의 정의와 특징, 그리고 왜 웹 환경에서 널리 사용되는지에 대한 이유를 설명하며 시작합니다. 이어서 SVG의 장단점을 분석하고, 실제로 SVG를 사용하는 다양한 방법들을 예제로 소개합니다. 또한, SVG를 최적화하는 기술과 자동화 도구를 활용한 효율적인 관리 방안도 다룹니다.
마지막으로, SVG를 디자인 시스템과 개발 프로세스에서 중요한 자원으로써 체계적으로 관리하는 방법까지 탐구해봅니다. 이 글을 통해 SVG를 더 잘 이해하고 실무에서 효과적으로 활용할 수 있는 다양한 인사이트를 얻을 수 있길 바랍니다.
SVG: Scalable Vector Graphics | MDN
SVG(Scalable Vector Graphic)는 XML 기반의 벡터 그래픽 파일 형식으로, 선, 도형, 텍스트를 수학적 정의를 통해 표현합니다. 이 특징 덕분에 해상도와 관계없이 선명하게 렌더링되며, 웹 환경에서 특히 많이 사용됩니다.
해상도 독립성
벡터 기반으로 작동하여 크기를 키우거나 줄여도 품질 저하가 없습니다.
파일 크기 효율성
복잡하지 않은 그래픽의 경우 비트맵 이미지(PNG, JPG)보다 용량이 작아 웹 페이지 로딩 속도를 개선할 수 있습니다.
스타일링과 인터랙션의 유연성
CSS와 JavaScript를 통해 동적으로 스타일을 변경하거나 애니메이션을 적용할 수 있습니다.
SEO와 접근성 지원
XML 구조 덕분에 검색 엔진에서 인덱싱할 수 있고, 스크린 리더와 같은 접근성 도구와의 호환성이 좋습니다.
크로스 플랫폼 호환성
주요 브라우저와 모든 디바이스에서 별도 플러그인 없이 사용할 수 있습니다.
복잡한 이미지 표현의 비효율성
사진처럼 픽셀 단위의 세부 묘사가 필요한 경우, SVG는 비효율적이며 용량이 커질 수 있습니다.
보안 취약성(XML Injection)
외부 입력을 통해 악성 코드가 삽입될 가능성이 있으므로, 신뢰할 수 없는 SVG는 반드시 sanitize 처리가 필요합니다.
// **SVG Sanitization**: 신뢰할 수 없는 SVG를 로드할 때 DOMPurify와 같은 도구를 활용해 잠재적 위협 요소를 제거.
import DOMPurify from 'dompurify';
const sanitizedSVG = DOMPurify.sanitize(rawSVG);
브라우저 렌더링 성능 이슈
지나치게 복잡한 SVG는 렌더링 성능 저하를 일으킬 수 있습니다.
HTML에 직접 SVG 코드를 삽입하여 사용하는 방식입니다.
<svg width="100" height="100">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
</svg>
<img>
태그로 사용SVG 파일을 외부 리소스로 불러오는 방식입니다.
<img src="icon.svg" alt="Icon" />
CSS를 활용하여 SVG를 배경으로 설정합니다.
.my-icon {
background-image: url('icon.svg');
}
SVG를 React 컴포넌트로 가져와 JSX에서 직접 사용합니다.
import { ReactComponent as Icon } from './icon.svg';
function App() {
return <Icon width={100} height={100} />;
}
SVG는 간단한 최적화 작업만으로도 크기와 성능을 크게 개선할 수 있습니다.
SVG는 XML 기반이므로 사람이 읽기 쉬운 구조를 가지고 있지만, 이로 인해 불필요한 태그와 속성이 포함되는 경우가 많습니다. 최적화를 통해 파일 크기를 줄이고 렌더링 성능을 개선할 수 있습니다.
불필요한 태그와 속성 제거
SVG 파일은 디자인 툴에서 내보낼 때 불필요한 메타데이터, 빈 태그, 기본값과 중복된 속성을 포함하는 경우가 많습니다. 이를 제거하면 크기를 줄일 수 있습니다.
최적화 전:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="500px" height="500px" viewBox="0 0 500 500">
<title>Example SVG</title>
<desc>Created in Adobe Illustrator</desc>
<g>
<rect x="0" y="0" width="500" height="500" fill="white" />
<circle cx="250" cy="250" r="200" fill="#FF0000" stroke="#000000" stroke-width="1"></circle>
</g>
</svg>
최적화 후:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500">
<circle cx="250" cy="250" r="200" fill="red" stroke="black" stroke-width="1"/>
</svg>
무엇을 최적화 했을까요?
위 방법을 통한 최적화 결과
숫자를 소수점 이하 몇 자리까지만 유지
SVG에서 좌표값이나 크기를 나타내는 숫자가 과도하게 길어지면 파일 크기가 증가합니다. 소수점 이하의 불필요한 자릿수를 줄이면 크기를 줄이고 렌더링을 단순화할 수 있습니다.
최적화 전:
<path d="M0.0001 0.0001 L100.12345 200.98765 Z"/>
최적화 후:
<path d="M0 0 L100.12 200.99 Z"/>
숫자값을 소수점 2자리까지만 유지. 이는 일반적으로 렌더링에 충분한 정밀도를 제공합니다.
긴 속성 이름을 짧게 축약
SVG의 속성은 XML 구조로 작성되기 때문에 이름이 길 경우 파일 크기가 커질 수 있습니다. 축약 가능한 속성은 짧은 이름으로 변경할 수 있습니다.
최적화 전:
<path stroke-width="2" fill-opacity="1" stroke-opacity="1"/>
최적화 후:
<path stroke-width="2" fill="1" stroke="1"/>
fill-opacity → fill, stroke-opacity → stroke로 축약
ViewBox와 width/height 설정
SVG에서 width와 height를 설정하지 않고 viewBox만 남겨 두면, SVG를 다양한 크기로 재사용하기 유리합니다.
<svg viewBox="0 0 24 24">
<circle cx="12" cy="12" r="10" />
</svg>
수동으로 최적화하는 것도 가능하지만, SVGO와 같은 도구를 사용하면 반복 작업을 자동화할 수 있습니다.
SVGO 설정 예시
svgo.config.js 파일:
module.exports = {
plugins: [
{ name: 'removeTitle', active: true }, // <title> 태그 제거
{ name: 'removeDesc', active: true }, // <desc> 태그 제거
{ name: 'convertColors', params: { currentColor: true } }, // 색상 축약
{ name: 'cleanupNumericValues', params: { floatPrecision: 2 } }, // 숫자 소수점 자리수 제한
{ name: 'removeUnusedNS', active: true }, // 사용하지 않는 네임스페이스 제거
{ name: 'removeMetadata', active: true }, // 메타데이터 제거
{ name: 'convertPathData', active: true }, // 경로 데이터 최적화
],
};
SVGO(SVG Optimizer)는 SVG 파일의 크기를 줄이고 성능을 개선하기 위해 불필요한 데이터를 제거하고 코드를 최적화하는 오픈 소스 CLI(Command Line Interface) 도구입니다. SVG 파일의 구조를 해석하고, 다양한 플러그인을 활용하여 자동으로 코드를 간결하게 만들어줍니다.
최적화 전:
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200">
<title>Circle Example</title>
<desc>Illustrator Export</desc>
<circle cx="100" cy="100" r="50" fill="#ff0000" stroke="#000000" stroke-width="1.123456"/>
</svg>
최적화 후(SVGO 적용 결과):
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
<circle cx="100" cy="100" r="50" fill="red" stroke="black" stroke-width="1.12"/>
</svg>
최적화 결과:
파일 크기: 270 bytes → 152 bytes (약 44% 감소)
SVGO는 svgo-loader등 로더를 통해 Webpack과 통합이 가능합니다.
module.exports = {
module: {
rules: [
{
test: /\.svg$/,
use: [
{
loader: 'svgo-loader',
options: {
plugins: [
{ name: 'removeTitle', active: true },
{ name: 'convertColors', active: true },
],
},
},
],
},
],
},
};
Figma와 연동
디자이너가 Figma에서 SVG를 내보낼 때, SVGO를 적용하여 최적화된 상태로 개발자에게 전달하도록 자동화 스크립트를 구성할 수 있습니다.
SVG를 React 컴포넌트로 변환하면 스타일, 크기, 클래스 등 다양한 속성을 Props로 전달받아 동적으로 제어할 수 있어 재사용성과 유지보수성이 높아집니다.
SVG를 React 컴포넌트로 변환
SVG 파일을 ReactComponent로 임포트하여 JSX에서 직접 사용합니다.
import { ReactComponent as Icon } from './icon.svg';
const App = () => (
<div>
<Icon width={50} height={50} fill="blue" />
</div>
);
export default App;
Props를 활용한 동적 제어
const Icon = ({ width = 24, height = 24, fill = 'currentColor' }) => (
<svg
width={width}
height={height}
fill={fill}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 24 24"
>
<circle cx="12" cy="12" r="10" />
</svg>
);
const App = () => (
<Icon width={40} height={40} fill="red" />
);
export default App;
장점
단점
지연 로딩은 페이지 로드 시점에 모든 리소스를 한 번에 불러오는 대신, 필요한 시점에 로드하여 초기 로드 속도를 최적화하는 방법입니다. SVG를 지연 로드하면 초기 네트워크 요청을 줄일 수 있습니다.
React dynamic import 사용
import React, { Suspense, lazy } from 'react';
const LazyIcon = lazy(() => import('./icon.svg'));
const App = () => (
<Suspense fallback={<div>Loading...</div>}>
<LazyIcon />
</Suspense>
);
export default App;
Intersection Observer를 활용한 로드
스크롤 영역에 진입할 때 SVG를 로드하도록 설정.
import { useState, useEffect } from 'react';
const LazyIcon = () => {
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) setIsVisible(true);
});
const element = document.getElementById('lazy-icon');
observer.observe(element);
return () => observer.disconnect();
}, []);
return isVisible ? <img src="icon.svg" alt="Lazy Icon" /> : <div id="lazy-icon" />;
};
export default LazyIcon;
React에서 SVG를 자동으로 컴포넌트화하려면 SVGR 같은 도구를 활용할 수 있습니다. 이를 활용하면 SVG를 Props로 제어 가능한 컴포넌트로 변환해 재사용성을 극대화할 수 있습니다.
장점
단점
⚠️ React Suspense와 함께 사용할 떄 주의하세요.
dynamic 방식으로 가져오는 svg 아이콘 컴포넌트는 상위 React.Suspense의 fallback을 의도치 않게 작동시킬 수 있습니다. 로드해야 하는 svg의 용량이 크거나 갯수가 많지 않다면 미리 로드하는 방법이 더 효율적일 수 있습니다.
SVG 스프라이트(Sprite)는 여러 SVG 아이콘을 하나의 파일로 병합하여, 클라이언트에서 단일 네트워크 요청으로 필요한 아이콘을 로드하도록 돕는 방법입니다.
스프라이트 파일 생성
여러 SVG를 하나로 병합합니다.
<!-- sprite.svg -->
<svg xmlns="http://www.w3.org/2000/svg">
<symbol id="icon-home" viewBox="0 0 24 24">
<path d="M12 2L2 12h3v8h6v-6h2v6h6v-8h3L12 2z" />
</symbol>
<symbol id="icon-user" viewBox="0 0 24 24">
<path d="M12 12c2.76 0 5-2.24 5-5s-2.24-5-5-5-5 2.24-5 5 2.24 5 5 5zm0 2c-4.41 0-8 3.59-8 8h2c0-3.31 2.69-6 6-6s6 2.69 6 6h2c0-4.41-3.59-8-8-8z" />
</symbol>
</svg>
스프라이트 사용
태그를 활용하여 원하는 아이콘을 로드합니다.
<svg class="icon">
<use xlink:href="sprite.svg#icon-home"></use>
</svg>
SVG 스프라이트와 Webpack 통합
const SpriteLoaderPlugin = require('svg-sprite-loader/plugin');
module.exports = {
module: {
rules: [
{
test: /\.svg$/,
loader: 'svg-sprite-loader',
},
],
},
plugins: [
new SpriteLoaderPlugin(),
],
};
장점
단점
현대 웹 개발과 디자인 환경에서 SVG는 단순한 아이콘이나 그래픽 요소를 넘어 디자인 시스템과 자산 관리의 핵심 요소로 자리 잡았습니다. SVG는 벡터 기반의 포맷으로, 픽셀 단위의 해상도 제한 없이 선명한 품질을 제공하며, 다양한 디바이스와 화면 크기에 대응할 수 있는 유연성을 갖추고 있습니다. 그러나 SVG를 단순한 파일로 취급하면, 프로젝트 규모가 커질수록 관리의 복잡성이 증가하고, 디자인과 개발 간의 비효율이 발생할 수 있습니다.
이 문제를 해결하기 위해 SVG를 자원으로 보고 체계적으로 관리해야 합니다. 이는 단순히 파일을 저장하는 것을 넘어, 디자인 시스템의 일환으로 SVG를 통합 관리하고, 효율적으로 최적화하며, 개발 프로세스와 빌드 파이프라인에 적합하게 자동화하는 것을 의미합니다. 특히 SVG는 디자인과 개발 간의 연결고리로 작용하기 때문에, 협업의 핵심 도구로 활용될 수 있습니다.
1) 직관적인 네이밍 규칙 정의 (Naming Convention)
SVG 아이콘이나 일러스트의 이름을 직관적으로 정의하면, 팀 내 커뮤니케이션과 리소스 검색이 훨씬 수월해집니다.
규칙 예시:
icon-[category]-[state].svg
icon-user-active.svg
, icon-alert-error.svg
2) 중앙 저장소 활용
SVG 파일을 관리할 중앙 저장소를 설정하여 팀 내 모든 개발자와 디자이너가 동일한 소스에 접근하도록 합니다.
Git 리포지토리: 아이콘 파일을 기능별, 컴포넌트별 디렉토리로 분류:
/icons/
├── user/
│ ├── icon-user-active.svg
│ └── icon-user-inactive.svg
├── alert/
│ ├── icon-alert-error.svg
│ └── icon-alert-success.svg
CDN: 정적 아이콘을 CDN에 배포하여 빠르게 로드.
3) Storybook 도입
Storybook은 UI 컴포넌트 개발 도구로, SVG 아이콘 라이브러리를 문서화하고 시각적으로 확인할 수 있습니다.
4) Design-Dev 협업 강화
SVG를 디자인 단계에서부터 개발까지 원활히 연결하려면 협업 과정을 표준화해야 합니다.
디자인 툴 활용:
개발 단계 검증:
이로써 SVG의 일관성을 보장하고, 불필요한 수정 작업을 줄일 수 있습니다. 또한 디자인 단계에서 최적화된 자원을 제공하여 개발 속도를 높입니다.
5) Build Tool Integration
자동화를 통해 SVG 관리의 생산성을 높이고, 최적화를 일관되게 유지할 수 있습니다.
Wonkook Lee
Software Engineer | Former Industrial Designer
좋은 글 감사합니다. SVG 를 다루는 형식을 전반적으로 이해하기 좋네요. ^^