SVG 파일, React Component로 가져오기 (Vite + TypeScript) 해당 내용의 확장 글입니다.
프로젝트를 진행하면서 아이콘과 같은 SVG 파일을 효율적으로 관리하기 위해 아래와 같은 폴더 구조를 사용했습니다.
📦assets ┣ 📂icon ┃ ┣ 📜icon-comments.svg ┃ ┣ 📜icon-heart.svg ┃ ┣ ... ┃ ┗ 📜index.ts
index.ts
에서 모든 SVG 파일을 React 컴포넌트로 변환 후 한 번에 export를 진행했습니다.import IconHeart from "./icon-heart.svg?react"; import IconComments from "./icon-comments.svg?react"; ... export { IconHeart, IconComments, ... }
이렇게 하면서 아이콘 파일들을 일괄적으로 관리하고 각 컴포넌트를 쉽게 가져와 사용할 수 있었습니다.
<IconHeart className="stroke-gray-500" />
<IconComments />
프로젝트가 커질수록 SVG 파일이 많아지고 아래와 같은 문제들이 발생했습니다.
index.ts
파일에서 모든 SVG를 한 번에 import 하여 사용하지 않는 SVG 파일들까지 초기 번들에 포함
특정 페이지에서 사용하지 않는 아이콘들이 불필요하게 로드 되는 상황
아이콘이 많아질수록 index.ts
에 모든 아이콘을 관리하기가 힘들어지고 파일을 추가, 삭제할 때마다 빼먹는 경우가 생김
기존
index.ts
파일을 삭제하고LazyIcon.tsx
파일을 추가하였습니다.📦assets ┣ 📂icon ┃ ┣ 📜icon-comments.svg ┃ ┣ 📜icon-heart.svg ┃ ┣ ... ┃ ┗ 📜LazyIcon.tsx
import React, { Suspense } from "react";
type LazyIconProps = {
name: string;
} & React.SVGProps<SVGSVGElement>;
const LazyIcon = ({ name, ...rest }: LazyIconProps) => {
const IconComponent = React.lazy(() => import(`./${name}.svg?react`));
return (
<Suspense fallback={<div className="h-full w-full animate-pulse bg-gray-100" />}>
<IconComponent {...rest} />
</Suspense>
);
};
export default LazyIcon;
React.lazy
를 사용해서 SVG 컴포넌트를 동적으로 로드Suspense
를 사용<LazyIcon name="icon-comments" className="~" />
수정 전 (25개 파일 로드) / 용량 : 3.6kB 수정 후 (12개 파일 로드) / 용량 : 1.7kB
- 빨간 박스 : 모든 페이지에서 공통으로 사용하는 SVG
- 노란 박스 : 현재 페이지에서 사용하는 SVG
- 그 외 : 다른 곳에서 사용하는 SVG
25개
의 SVG 파일에서 현재 페이지에서 사용하고 있는 SVG 파일인 12개
의 파일로 줄였습니다.3.6kB
에서 1.7kB
로 약 52.8%
감소했습니다. 기존 index.ts
파일로 관리하던 방식은 SVG 파일이 추가, 삭제될 때마다 수정해야 하는 번거로움이 있어 유지보수가 좋지 않았습니다.
모든 곳에서 공통으로 사용하거나 로딩 없이 바로 보여줘야 하는 SVG들만 index.ts
에서 관리하고, 그 외에는 각 컴포넌트나 페이지에서 필요한 SVG를 동적 로드하는 방식으로 변경했습니다.
이렇게 하면 SVG 파일의 추가, 삭제가 필요할 때마다 index.ts
를 수정할 필요 없이 개별적으로 관리할 수 있어 유지보수성이 향상되고 불필요한 SVG 파일이 초기 번들에 포함되지 않아 번들 사이즈를 줄일 수 있습니다.