웹 환경에서 SVG 더 잘 이해하고 사용하기

Wonkook Lee·2025년 1월 14일
31

Optimization

목록 보기
1/1
post-thumbnail

이 글에서 다루는 내용

이 글은 SVG(Scalable Vector Graphic)에 대한 전반적인 내용을 다룹니다. SVG의 정의와 특징, 그리고 왜 웹 환경에서 널리 사용되는지에 대한 이유를 설명하며 시작합니다. 이어서 SVG의 장단점을 분석하고, 실제로 SVG를 사용하는 다양한 방법들을 예제로 소개합니다. 또한, SVG를 최적화하는 기술과 자동화 도구를 활용한 효율적인 관리 방안도 다룹니다.

마지막으로, SVG를 디자인 시스템과 개발 프로세스에서 중요한 자원으로써 체계적으로 관리하는 방법까지 탐구해봅니다. 이 글을 통해 SVG를 더 잘 이해하고 실무에서 효과적으로 활용할 수 있는 다양한 인사이트를 얻을 수 있길 바랍니다.




SVG란 무엇이고 왜 많이 사용될까?

SVG: Scalable Vector Graphics | MDN

SVG(Scalable Vector Graphic)는 XML 기반의 벡터 그래픽 파일 형식으로, 선, 도형, 텍스트를 수학적 정의를 통해 표현합니다. 이 특징 덕분에 해상도와 관계없이 선명하게 렌더링되며, 웹 환경에서 특히 많이 사용됩니다.


SVG가 많이 사용되는 이유와 장점

  1. 해상도 독립성

    벡터 기반으로 작동하여 크기를 키우거나 줄여도 품질 저하가 없습니다.

  2. 파일 크기 효율성

    복잡하지 않은 그래픽의 경우 비트맵 이미지(PNG, JPG)보다 용량이 작아 웹 페이지 로딩 속도를 개선할 수 있습니다.

  3. 스타일링과 인터랙션의 유연성

    CSS와 JavaScript를 통해 동적으로 스타일을 변경하거나 애니메이션을 적용할 수 있습니다.

  4. SEO와 접근성 지원

    XML 구조 덕분에 검색 엔진에서 인덱싱할 수 있고, 스크린 리더와 같은 접근성 도구와의 호환성이 좋습니다.

  5. 크로스 플랫폼 호환성

    주요 브라우저와 모든 디바이스에서 별도 플러그인 없이 사용할 수 있습니다.


SVG의 단점

  1. 복잡한 이미지 표현의 비효율성

    사진처럼 픽셀 단위의 세부 묘사가 필요한 경우, SVG는 비효율적이며 용량이 커질 수 있습니다.

  2. 보안 취약성(XML Injection)

    외부 입력을 통해 악성 코드가 삽입될 가능성이 있으므로, 신뢰할 수 없는 SVG는 반드시 sanitize 처리가 필요합니다.

    // **SVG Sanitization**: 신뢰할 수 없는 SVG를 로드할 때 DOMPurify와 같은 도구를 활용해 잠재적 위협 요소를 제거.
    import DOMPurify from 'dompurify';
    
    const sanitizedSVG = DOMPurify.sanitize(rawSVG);
  3. 브라우저 렌더링 성능 이슈

    지나치게 복잡한 SVG는 렌더링 성능 저하를 일으킬 수 있습니다.




SVG 사용하는 일반적인 방법들

1) Inline SVG

HTML에 직접 SVG 코드를 삽입하여 사용하는 방식입니다.

<svg width="100" height="100">
  <circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
</svg>
  • 장점: CSS/JavaScript를 통해 스타일과 인터랙션을 동적으로 제어 가능합니다.
  • 단점: HTML 파일 크기가 커질 수 있습니다.

2) <img> 태그로 사용

SVG 파일을 외부 리소스로 불러오는 방식입니다.

<img src="icon.svg" alt="Icon" />
  • 장점: HTML 코드가 깔끔하고 로드가 간단합니다.
  • 단점: 스타일이나 애니메이션 적용이 어렵습니다.

3) CSS background-image로 사용

CSS를 활용하여 SVG를 배경으로 설정합니다.

.my-icon {
  background-image: url('icon.svg');
}
  • 장점: 디자인 요소로 활용 가능합니다.
  • 단점: 접근성이 떨어집니다.

4) React 컴포넌트로 사용

SVG를 React 컴포넌트로 가져와 JSX에서 직접 사용합니다.

import { ReactComponent as Icon } from './icon.svg';

function App() {
  return <Icon width={100} height={100} />;
}
  • 장점: 재사용성과 동적 속성 적용 가능합니다.
  • 단점: 빌드 크기가 커질 수 있습니다.



알려진 최적화 방법들

SVG는 간단한 최적화 작업만으로도 크기와 성능을 크게 개선할 수 있습니다.

1) 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>

무엇을 최적화 했을까요?

  • 불필요한 속성 제거: xmlns:xlink, width, height 속성을 제거.
  • 불필요한 태그 제거: , <desc>, <rect> 태그 제거.
  • 속성 축약: 색상 표현 방식에서 #FF0000 → red, #000000 → black으로 단축.

위 방법을 통한 최적화 결과

  • 최적화 전 SVG의 용량은 389 bytes.
  • 최적화 후 용량은 152 bytes.
  • 237 bytes(60.93%)의 용량이 감소했습니다.

숫자를 소수점 이하 몇 자리까지만 유지

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>

2) 자동화된 도구 사용

수동으로 최적화하는 것도 가능하지만, SVGO와 같은 도구를 사용하면 반복 작업을 자동화할 수 있습니다.

  • SVGO: SVG 파일을 최적화하는 CLI 도구.
  • SVGOMG: SVGO의 GUI 버전.
  • Figma Export 설정: Figma에서 “Flatten”과 같은 옵션 사용.

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를 적용하여 최적화된 상태로 개발자에게 전달하도록 자동화 스크립트를 구성할 수 있습니다.


3) Reusable Components로 관리

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;

장점

  • 재사용성: 동일한 컴포넌트를 다양한 상황에 맞게 재사용 가능.
  • 스타일링 유연성: fill, stroke, className 등 Props로 스타일을 제어 가능.
  • 모듈화: SVG 리소스를 컴포넌트로 관리하여 코드베이스 정리.

단점

  • 빌드 크기 증가: 많은 SVG를 React 컴포넌트로 변환하면 번들 크기가 커질 수 있음.
  • Dynamic Import 필요: 사용하지 않는 아이콘도 초기 빌드에 포함될 가능성.

4) Lazy Loading

지연 로딩은 페이지 로드 시점에 모든 리소스를 한 번에 불러오는 대신, 필요한 시점에 로드하여 초기 로드 속도를 최적화하는 방법입니다. 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로 제어 가능한 컴포넌트로 변환해 재사용성을 극대화할 수 있습니다.

장점

  • 초기 로드 속도 최적화: 필요한 시점에만 로드하여 초기 네트워크 부하 감소.
  • 사용자 경험 개선: 렌더링 시점에 필요한 리소스만 로드.

단점

  • 레이턴시 문제: 사용자 인터랙션 시 로드 지연으로 인해 딜레이 발생 가능.
  • 복잡성 증가: Intersection Observer와 같은 추가 로직이 필요.

⚠️ React Suspense와 함께 사용할 떄 주의하세요.

dynamic 방식으로 가져오는 svg 아이콘 컴포넌트는 상위 React.Suspense의 fallback을 의도치 않게 작동시킬 수 있습니다. 로드해야 하는 svg의 용량이 크거나 갯수가 많지 않다면 미리 로드하는 방법이 더 효율적일 수 있습니다.



5) Sprite 사용

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를 단순한 파일로 취급하면, 프로젝트 규모가 커질수록 관리의 복잡성이 증가하고, 디자인과 개발 간의 비효율이 발생할 수 있습니다.

이 문제를 해결하기 위해 SVG를 자원으로 보고 체계적으로 관리해야 합니다. 이는 단순히 파일을 저장하는 것을 넘어, 디자인 시스템의 일환으로 SVG를 통합 관리하고, 효율적으로 최적화하며, 개발 프로세스와 빌드 파이프라인에 적합하게 자동화하는 것을 의미합니다. 특히 SVG는 디자인과 개발 간의 연결고리로 작용하기 때문에, 협업의 핵심 도구로 활용될 수 있습니다.


1) 직관적인 네이밍 규칙 정의 (Naming Convention)

SVG 아이콘이나 일러스트의 이름을 직관적으로 정의하면, 팀 내 커뮤니케이션과 리소스 검색이 훨씬 수월해집니다.

규칙 예시:

  • icon-[category]-[state].svg
    • e.g. 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에 배포하여 빠르게 로드.

  • 예: AWS S3 + CloudFront 또는 서비스형 CDN(Fastly, Akamai 등).

3) Storybook 도입

Storybook은 UI 컴포넌트 개발 도구로, SVG 아이콘 라이브러리를 문서화하고 시각적으로 확인할 수 있습니다.

  • Storybook에 SVG를 컴포넌트처럼 등록하여 각 아이콘의 크기, 색상, 변형 상태를 미리 보기.
  • SVG 속성을 조정할 수 있는 컨트롤(Addon Knobs) 추가.

4) Design-Dev 협업 강화

SVG를 디자인 단계에서부터 개발까지 원활히 연결하려면 협업 과정을 표준화해야 합니다.

디자인 툴 활용:

  • Figma, Sketch 등에서 SVG 내보내기 시, 품질과 최적화 설정을 디자이너에게 교육.
    • e.g. 불필요한 그룹, 레이어 제거.
  • 디자인 시스템에 SVG 통합(예: Figma Tokens).

개발 단계 검증:

  • 개발자는 SVG의 코드 구조를 검토하여 최적화 상태를 확인합니다.
    • e.g. 태그가 중복되거나, 불필요한 inline style 제거.

이로써 SVG의 일관성을 보장하고, 불필요한 수정 작업을 줄일 수 있습니다. 또한 디자인 단계에서 최적화된 자원을 제공하여 개발 속도를 높입니다.


5) Build Tool Integration

자동화를 통해 SVG 관리의 생산성을 높이고, 최적화를 일관되게 유지할 수 있습니다.

  • Webpack, Vite 등의 빌드 도구에 SVGO 플러그인을 연동하여 자동 최적화.
  • 필요 시, 스프라이트 생성기를 빌드 파이프라인에 추가.



Wonkook Lee
Software Engineer | Former Industrial Designer

LinkedIn

profile
Software Engineer | Former Industrial Designer

2개의 댓글

comment-user-thumbnail
2025년 1월 15일

좋은 글 감사합니다. SVG 를 다루는 형식을 전반적으로 이해하기 좋네요. ^^

1개의 답글