1. 아이콘 깜빡거리는 문제 개선

웹페이지를 개발하다 보면 아이콘이 렌더링될 때 깜빡이는 현상을 한 번쯤 경험하게 됩니다. 저도 최근에 이 문제로 고민했는데요, 네트워크 탭을 열어보니 아이콘 하나하나가 개별적으로 요청되면서 시간이 꽤 걸리고 있었습니다. 이 문제를 해결하기 위해 여러 방법을 시도해본 과정을 공유해보려고 합니다.

문제 상황 파악하기

먼저 정확히 무엇이 문제인지 파악해봤습니다. 페이지에 아이콘이 10개만 있어도 10번의 HTTP 요청이 발생하고, 각 요청마다 네트워크 레이턴시가 추가됩니다. 특히 모바일 환경이나 네트워크가 느린 상황에서는 이 문제가 더 두드러지게 나타나죠. 아이콘들이 하나씩 로딩되면서 깜빡이는 모습은 사용자 경험 측면에서도 좋지 않았습니다.

  1. SVG 태그 직접 사용

    첫 번째로 SVG 코드를 직접 컴포넌트에 넣는 방법을 생각해봤어요. 이렇게 하면 HTTP 요청이 없어지니까 깜빡임 문제는 확실히 해결됩니다. 하지만 실제로 적용해보니 JSX 코드가 너무 길어져서 가독성이 떨어지더라고요. 코드를 보면서 "이게 무슨 아이콘이지?" 하고 헷갈리는 경우가 많았습니다.

  2. SVG 파일 Import

    두 번째는 SVG 파일을 import해서 사용하는 방법이었습니다. Next.js나 Vite 같은 번들러에서는 SVG를 React 컴포넌트처럼 import할 수 있죠. 이 방법도 나쁘진 않은데, 이 방법은 아이콘이 JavaScript 번들에 포함되기 때문에, 아이콘이 많아질수록 JS 번들 사이즈가 불필요하게 커지는 문제가 있었습니다. 이는 브라우저가 JS를 해석하는 비용을 높여 초기 로딩 속도에 악영향을 줄 수 있죠. 또한 로직 수정으로 JS 파일이 변경될 때마다 아이콘 데이터까지 캐시가 만료되어 다시 다운로드해야 하는 캐싱 비효율도 발생합니다.

  3. Sprite 파일 사용

    마지막으로 SVG Sprite 기법을 떠올렸습니다. 프론트엔드 최적화 강의에서 배웠던 내용인데, 여러 개의 SVG를 하나의 파일로 합쳐서 한 번의 요청으로 모든 아이콘을 사용할 수 있는 방법입니다. 이게 가장 효율적일 것 같아서 직접 적용해보기로 했습니다.

    // package.json
    "make-sprite": "rm public/sprite/sprite.svg && svgstore -o public/sprite/sprite.svg public/sprite/icons/**/*.svg"
    

SVG Sprite 구현하기

Sprite 파일을 만들기 위해 svgstore 라이브러리를 사용했습니다. 먼저 package.json에 스크립트를 추가했어요.

{
  "scripts": {
    "make-sprite": "rm public/sprite/sprite.svg && svgstore -o public/sprite/sprite.svg public/sprite/icons/**/*.svg"
  }
}

이 스크립트는 public/sprite/icons 폴더 안에 있는 모든 SVG 파일을 하나의 sprite.svg 파일로 합쳐줍니다. 기존 파일을 지우고 새로 생성하는 방식이라 항상 최신 상태를 유지할 수 있죠. yarn make-sprite 명령어만 실행하면 됩니다.

다음으로 재사용 가능한 Icon 컴포넌트를 만들었습니다.

// Icon.tsx
import type { SVGProps } from 'react';

interface IconProps extends SVGProps<SVGSVGElement> {
  id: string;
}

export default function Icon({ id, width = 24, height = 24, fill = 'none', ...props }: IconProps) {
  return (
    <svg width={width} height={height} fill={fill} {...props}>
      <use href={`/sprite/sprite.svg#${id}`} />
    </svg>
  );
}

이 컴포넌트의 핵심은 <use> 태그입니다. SVG의 <use> 태그는 다른 SVG 요소를 참조해서 재사용할 수 있게 해주는데요, 여기서는 sprite 파일 안에 있는 특정 아이콘을 id로 참조하고 있습니다. # 뒤에 붙는 id는 원본 SVG 파일명을 그대로 사용하면 됩니다.

IconProps는 기본 SVGProps를 확장해서 만들었기 때문에, className, onClick 같은 일반적인 SVG 속성들도 모두 사용할 수 있습니다. 타입 안정성도 확보되고, 개발 경험도 좋아지는 구조죠.

실제 사용할 때는 이렇게 간단하게 적용할 수 있습니다.

// 적용 전
<IconButton>
  <Image src="/icons/24/navigate_next.svg" width={24} height={24} />
</IconButton>

// 적용 후
<IconButton>
  <Icon id="24-navigate_next" />
</IconButton>