CRA에서 Vite로 마이그레이션할 때 겪는 SVG 에러를 해결해보자
CRA 환경에서 Vite로 마이그레이션 진행시 트러블슈팅 내용
서론
CRA(Create React App) 프로젝트를 Vite로 마이그레이션하는 과정에서 마주한 SVG 아이콘 import 에러 해결과정을 다뤄보자.
발생한 에러 메시지는 다음과 같았다.
Unexpected Application Error!
Failed to execute 'createElement' on 'Document':
The tag name provided ('data:image/svg+xml,...') is not a valid name.
이 글에서는 이 에러가 발생한 이유, CRA와 Vite의 SVG 처리 방식 차이, 해결 과정, 그리고 vite-plugin-svgr의 svgrOptions를 살펴보자.
문제가 발생한 상황
CRA 환경에서 SVG를 import하는 코드는 다음과 같았다.
import { ReactComponent as Add } from "./Add.svg";
import { ReactComponent as Delete } from "./Delete.svg";
Vite로 마이그레이션한 뒤 동일한 코드를 실행했더니 오류가 발생했다.
- CRA:
Add변수에 React Component가 담겨<Add />로 바로 사용 가능했다. - Vite:
Add변수에 data URL 문자열("data:image/svg+xml;utf8,...)이 담겨<data:image/svg+xml,... />라는 잘못된 태그로 해석되었다.
원인: CRA와 Vite의 SVG 처리 방식 차이
CRA (Webpack 기반)
CRA는 내부적으로 webpack + svgr loader가 설정되어 있었다. 링크.svg 파일을 import하면 Webpack이 SVGR을 사용해 React Component로 변환해준다.
이 덕분에 import { ReactComponent as Icon } from './icon.svg' 로 SVG를 컴포넌트 처럼 쓸 수 있다.
Vite (Rollup 기반)
Vite는 기본적으로 .svg를 정적 파일 URL로 처리했다.
이게 무슨말이냐면 import icon from './icon.svg'라고 하면, icon 변수 안에는 SVG 코드나 React 컴포넌트가 아니라, 빌드된 후의 /assets/icon-xxxx.svg 같은 주소 문자열이 들어간다.
그래서 이걸 <Icon />처럼 JSX에서 태그로 쓰는 건 불가능하고, <img src={icon} />처럼 이미지를 넣는 방식으로만 쓸 수 있다.
CRA처럼 바로 <Icon /> 형태로 쓰려면 SVG를 React 컴포넌트로 변환하는 과정이 필요한데, Vite에는 이 변환 로직이 기본 포함되어 있지 않다. 즉 vite에서는 React Component 변환 과정이 없었기 때문에 CRA 문법이 동작하지 않았다.
핵심 차이
[Unknown block type: table]
[Unknown block type: table_row]
[Unknown block type: table_row]
[Unknown block type: table_row]
[Unknown block type: table_row]
[Unknown block type: table_row]
해결 과정
vite-plugin-svgr 설치
Vite에서 CRA처럼 SVG를 컴포넌트로 사용하려면 vite-plugin-svgr를 설치해야 했다.
pnpm add -D vite-plugin-svgr
Vite 설정 추가
vite.config.ts에 플러그인을 추가했다.
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import svgr from "vite-plugin-svgr";
export default defineConfig({
plugins: [
react(),
svgr({
svgrOptions: {
icon: true,
svgoConfig: {
plugins: [
{
name: "preset-default",
params: { overrides: { removeViewBox: false } },
},
],
},
},
}),
],
});
svgrOptions 상세 분석
icon: true
- 의미: SVG의
width와height속성을 제거하고, 크기 조절을 props로 가능하게 만든다. - 왜 필요한가:
예를 들어 원본 SVG에
width="24" height="24"가 박혀 있으면, CSS나 props로 크기를 바꿔도 적용되지 않는다.icon: true를 설정하면<MyIcon width={32} height={32} />처럼 동적으로 크기를 제어할 수 있다. - CRA와 비교: CRA 환경에서도 svgr loader가 기본적으로 이 설정과 유사하게 동작해 SVG를 아이콘처럼 쓸 수 있었다.
svgoConfig
svgo는 SVG 최적화 도구다. vite-plugin-svgr는 내부적으로 svgo를 이용해 불필요한 속성과 코드를 제거한다.
plugins
"preset-default": svgo의 기본 최적화 플러그인 세트를 사용한다.- 주석 제거
- 불필요한 속성 제거
- path 데이터 단순화 등
params: { overrides: { removeViewBox: false } }
- 의미:
viewBox속성을 삭제하지 않게 설정 - 왜 필요한가:
viewBox는 SVG를 비율에 맞게 확대/축소하는 데 핵심적인 속성이다. 만약removeViewBox가 true면 아이콘 크기 조절이 깨지고, 반응형 크기 조절이 불가능해진다.removeViewBox: false로 설정하면 원본 비율을 유지하면서도 크기 변경이 가능하다.
결과적으로
svgrOptions: {
icon: true, // 아이콘 크기 props로 제어 가능
svgoConfig: {
plugins: [
{
name: "preset-default", // 기본 최적화 적용
params: { overrides: { removeViewBox: false } }, // viewBox 유지
},
],
},
}
이 조합은 다음과 같은 효과를 준다.
- SVG 크기를 props로 자유롭게 조절할 수 있다.
- 불필요한 속성을 제거해 번들 크기를 줄인다.
- viewBox를 유지해 반응형 및 확대/축소가 정상적으로 작동한다.
Import 방식 변경
Vite에서는 ?react 쿼리 파라미터를 붙이는 방식을 사용했다.
이 쿼리를 붙이면 vite-plugin-svgr가 동작해서.svg 파일을 단순한 파일 경로 문자열이 아니라 React 컴포넌트로 변환해준다.
// SVG를 React 컴포넌트로 import
import Add from "./Add.svg?react";
import Add2 from "./Add2.svg?react";
export default function Example() {
return (
<div className="flex items-center gap-2">
{/* 기본 사용 */}
<Add className="w-6 h-6 text-blue-500" />
{/* props로 크기 변경 */}
<Add2 width={32} height={32} />
{/* props로 색상 변경 (currentColor 기반) */}
<Add2 className="text-red-500" />
{/* 인라인 스타일도 가능 */}
<Add2 style={{ fill: "#00ff00" }} />
</div>
);
}
그러면 이런식으로 사용할 수 있다.
느낀 점 및 배운점
- CRA는 숨겨진 편의 기능이 많아 마이그레이션 시 해당 기능을 직접 구현해야 했다.
- Vite는 플러그인 구조 덕분에 가볍지만, 필요한 기능은 직접 추가해야 한다.
icon: true와removeViewBox: false설정은 아이콘 크기 조절과 반응형 지원에 필수적이다.
결론
CRA에서 Vite로 마이그레이션 시 SVG import 에러는 자주 발생한다. 원인을 이해하면 해결은 단순하다.
- CRA: Webpack + SVGR → 자동 React Component 변환
- Vite: Rollup 기반, 기본은 URL 처리 →
vite-plugin-svgr필요 - 해결:
vite-plugin-svgr설치,icon: true,removeViewBox: false설정,?reactimport 방식 적용
마이그레이션은 코드만 옮기는 작업이 아니라, 기존 환경의 빌드·로더 생태계를 재구성하는 과정임을 다시 확인했다.
📚 참고