react

React) SVG 스프라이트와 개별 SVG 아이콘 사용에 관한 개발 이야기

YG - 96년생 , 강아지 있음, 개발자 희망 2025. 5. 15. 21:52

SVG 스프라이트와 개별 SVG 아이콘 사용에 관한 개발 이야기

들어가며

개발 과정에서 우리는 종종 당연하게 여기는 패턴이나 방식들이 있습니다. 그 중 하나가 아이콘을 효율적으로 관리하기 위한 '스프라이트(Sprite)' 기법인데요. 최근 SNS 연동 기능을 구현하면서 이 접근 방식에 대해 다시 한번 생각해보게 되었습니다.

 

문제 상황: 아이콘 관리

SNS 로그인 기능을 개발하면서 다양한 서비스의 로고들을 한 페이지에서 불러와야 했습니다. 네이버, 인스타, 틱톡, 페이스북 등 약 10개의 로고를 효율적으로 처리해야 했죠. 처음 고민했던 것은 무엇보다 '네트워크 요청 수를 줄이는 것'이었습니다.

 

첫 접근: PNG 스프라이트

전통적인 웹 최적화 기법으로, 여러 이미지를 하나의 큰 이미지 파일로 합친 후 CSS의 background-position을 사용해 필요한 부분만 잘라서 보여주는 PNG 스프라이트를 고려했습니다. 이 방식은 HTTP 요청 수를 크게 줄일 수 있다는 장점이 있습니다.

.icon-naver {
  background-image: url('/sprites.png');
  background-position: 0 0;
  width: 24px;
  height: 24px;
}

.icon-kakao {
  background-image: url('/sprites.png');
  background-position: -24px 0;
  width: 24px;
  height: 24px;
}

 

ChatGPT의 조언: SVG 사용

이 접근 방식에 대해 ChatGPT에 물어봤을 때, PNG보다는 SVG가 더 좋을 것이라는 조언을 받았습니다. SVG는 벡터 기반이라 어떤 크기로도 깔끔하게 표시되고, 파일 크기도 작을 수 있기 때문이죠.

 

SVG 스프라이트 시도

SVG도 스프라이트로 만들 수 있습니다. Vite 프로젝트에서는 vite-plugin-svg-icons 같은 플러그인을 사용해 구현할 수 있습니다:

// vite.config.js
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";

export default {
  plugins: [
    react(),
    createSvgIconsPlugin({
      iconDirs: [path.resolve(process.cwd(), "src/assets/icons")],
      symbolId: "icon-[name]",
      svgoOptions: true,
      inject: "body-last",
      customDomId: "svg-sprite",
    }),
  ],
};

그리고 아이콘을 사용하는 컴포넌트를 만들었습니다:

// Icon.jsx
type IconProps = {
  name: string;
  size?: number;
  color?: string;
  className?: string;
};

export const Icon = ({
  name,
  size = 24,
  color = "currentColor",
  className = "",
}: IconProps) => (
  <svg width={size} height={size} fill={color} className={`${className}`}>
    <use xlinkHref={`#icon-${name}`} />
  </svg>
);

export const Naver = () => (
  <Icon name="naver" className="w-6 h-6 text-blue-500" />
);

 

다운로드 수: 41338

마지막 업데이트: 3년 전

 

깨달음: SVG는 스프라이트가 필요 없다

그런데 SVG 스프라이트를 구현하려고 하다가 중요한 사실을 깨달았습니다. SVG는 XML 기반 코드로, 직접 React 컴포넌트로 가져와서 사용할 수 있습니다. 스프라이트를 만들 필요가 없이, 각 SVG 파일을 직접 임포트하는 방식이 오히려 더 간단하고 관리하기 좋을 수 있습니다.

최종 선택: vite-plugin-svgr

결국 vite-plugin-svgr 패키지를 사용해 SVG 파일을 React 컴포넌트로 직접 임포트하는 방식을 선택했습니다:

 

vite-env.d.ts

/// <reference types="vite/client" />
/// <reference types="vite-plugin-svgr/client" /> // << 추가

 

vite.config.ts

import path from "path";
import { defineConfig, loadEnv } from "vite";
import react from "@vitejs/plugin-react-swc";
import svgr from "vite-plugin-svgr"; // << 추가

// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd(), "");

  return {
    base: "/",
   ...
    },
    plugins: [react(), svgr()], // << 추가
    resolve: {
      alias: {
        "@": path.resolve(__dirname, "./src"),
      },
    },
  };
});

 

NaverIcon.tsx

import NaverLogo from "@/assets/icons/naver.svg?react";

type IconProps = {
  size?: number;
  color?: string;
  className?: string;
};

export const NaverIcon = ({
  size = 24,
  color = "currentColor",
  className = "",
}: IconProps) => (
  <NaverLogo width={size} height={size} color={color} className={className} />
);

 

 

이 방식의 장점은:

  1. 코드 가독성: 각 아이콘이 어떤 것인지 임포트 구문만 봐도 명확합니다
  2. 유지보수: 아이콘을 추가하거나 수정할 때 개별 파일만 관리하면 됩니다
  3. 타입 안정성: TypeScript와 함께 사용할 때 속성 타입 체크가 가능합니다
  4. 번들링 최적화: 현대 번들러는 SVG를 효율적으로 처리하며, 트리 쉐이킹으로 사용하지 않는 아이콘은 제외됩니다

 

다운로드 수: 1,742,398

마지막 업데이트: 6개월 전

 

네트워크 요청 수에 대한 오해

처음에는 "여러 아이콘을 개별적으로 불러오면 네트워크 요청 수가 늘어나지 않을까?"라는 걱정이 있었습니다. 하지만 이는 빌드 과정의 이해 부족에서 오는 오해였습니다.

 

현대 빌드 시스템(Webpack, Vite 등)에서는:

  1. SVG 파일을 임포트하면 빌드 시점에 JavaScript 코드로 변환됩니다
  2. 이 코드는 번들에 포함되어 별도의 네트워크 요청 없이 앱과 함께 로드됩니다
  3. 즉, 10개의 SVG를 임포트해도 추가 네트워크 요청은 발생하지 않습니다

개별로 불러오는 것이 사용 방법도 간편하고 네트워크 수 요청도 똑같고, 오히려 스프라이트로 했을 때 필요하지 않은 아이콘들도 초기에 불러와서 로딩 속도만 느려질 뿐이였습니다. 

SVG 최적화: SVGO로 파일 크기 줄이기

개별 SVG 파일을 사용하는 접근 방식을 선택했지만, 한 가지 더 고려할 점이 있었습니다. 디자인 도구(예: Figma)에서 내보낸 SVG 파일은 종종 불필요한 코드를 포함하고 있어 파일 크기가 커지는 문제가 있습니다.

 

이 문제를 해결하기 위해 svgo 도구를 활용했습니다. SVGO(SVG Optimizer)는 SVG 파일에서 불필요한 마크업을 제거하고 다양한 최적화를 수행하는 강력한 도구입니다.

 
bash
# SVGO 설치 및 실행
npx svgo -f src/assets/icons

직접 Figma에서 다운로드한 SVG 파일들에 이 최적화 도구를 적용해본 결과, 놀랍게도 일부 파일은 크기가 최대 20%까지 감소했습니다. 불필요한 메타데이터, 주석, 빈 그룹 등이 모두 제거되어 파일 크기가 크게 줄었습니다.

 

효율적인 워크플로우를 위해 package.json에 스크립트도 추가했습니다:

 
json
{
  "scripts": {
    "optimize-icons": "npx svgo -f src/assets/icons"
  }
}

이제 npm run optimize-icons 명령어만으로 모든 아이콘을 한 번에 최적화할 수 있습니다. 새로운 아이콘이 추가될 때마다 이 스크립트를 실행하면 항상 최적화된 SVG 파일을 유지할 수 있습니다.

 

 

다운로드 수: 19,667,708

마지막 업데이트: 9일 전

 

결론

개발에서는 종종 "이렇게 하는 게 좋다"라는 관념에 사로잡히곤 합니다. 스프라이트가 항상 최적의 해결책이라고 생각했지만, 실제로는 기술과 도구에 맞는 더 간단하고 효율적인 접근 방식이 있었습니다.

 

SVG 아이콘을 관리할 때는 각 파일을 개별적으로 관리하고 컴포넌트로 임포트하는 방식이 현대 프론트엔드 개발 흐름에 더 적합할 수 있습니다. 여기에 SVGO 같은 최적화 도구를 함께 사용하면 성능까지 향상시킬 수 있습니다.

 

때로는 "당연히 이렇게 해야 한다"는 생각을 잠시 내려놓고, 현재의 도구와 환경에서 정말 최적의 방법이 무엇인지 다시 생각해보는 것이 중요하다고 생각이 들었습니다.