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, { memo, Suspense, useMemo } from "react";
type LazyIconProps = {
name: string;
} & React.SVGProps<SVGSVGElement>;
const LazyIcon = memo(({ name, ...rest }: LazyIconProps) => {
const IconComponent = useMemo(() => React.lazy(() => import(`./${name}.svg?react`)), [name]);
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 파일이 초기 번들에 포함되지 않아 번들 사이즈를 줄일 수 있습니다.