CRA 환경에서 설정 필요없이 react에서 제공하는 컴포넌트로 편하게 svg 컴포넌트를 사용했었다.
import { ReactComponent as ArrowUp } from '@assets/arrowUp.svg';
const Component = () => {
return (
<ArrowUp />
)
}
ReactComponent를 까보니 역시 type이 설정되어있었다.
declare module '*.svg' {
import * as React from 'react';
export const ReactComponent: React.FunctionComponent<React.SVGProps<
SVGSVGElement
> & { title?: string }>;
const src: string;
export default src;
}
기존 svg 파일을 그대로 사용할 수 있지만, 어디서나 동일한 색상과 크기의 아이콘을 사용하지 않는 경우가 많다. 따라서 svg 컴포넌트를 통해 원하는 아이콘 색상과 크기를 설정하기로 했다.
컴포넌트 만들기 전에, svg 파일에서 스타일 바꾸고 싶은 값만 current
로 바꿔준다.
<svg width="current" height="current" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="..." fill="current"/>
</svg>
npm i -D file-loader
// webpack.config.js
module.exports = {
//...
module: {
{
test: /\.svg$/,
use: {
loader: 'file-loader',
},
},
// ...
}
}
file-loader를 설치하고 webpack 설정을 마치면, CRA처럼 svg 컴포넌트를 import할 수 있다.
import { ReactComponent as ArrowUp } from '@assets/arrowUp.svg';
const Component = () => {
return (
<ArrowUp width="28" height="28" fill="black" />
)
}
@svgr/webpack
은 svg 파일을 React 컴포넌트로 바꿔주는 라이브러리다.
npm i -D @svgr/webpack
// webpack.config.js
module.exports = {
//...
module: {
{
test: /\.svg$/,
use: {
issuer: /\.[jt]sx?$/,
loader: '@svgr/webpack',
},
},
// ...
}
}
webpack 설정까지 마치면 아래와 같이 사용할 수 있다.
import ArrowUp from '@assets/arrowUp.svg';
const Component = () => {
return (
<ArrowUp width="28" height="28" fill="black" />
)
}
위 방법과 달리 ReactComponent를 import할 필요가 없어서 이 방법을 택했다.
그런데 TypeScript에서 svg 파일을 불러올 수 없다는 에러가 발생했다.
맨 위에서 본 ReactComponent의 type처럼 따로 svg 파일에 관한 type을 설정해야했다.
// custom.d.ts
declare module "*.svg" {
const content: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
export default content;
}
그리고 tsconfig.json
에 custom.d.ts
파일 경로를 넣어준다. (참고로 필자는 tsconfig.json
와 같은 위치에 파일을 만들었다.)
include: ["src/**/*", "custom.d.ts"]
storybook으로 svg 컴포넌트가 잘 만들어졌는지 테스트하려했는데, 아래와 같은 에러가 발생했다.
Failed to execute 'createElement' on 'Document': The tag name provided ('static/media/src/assets/svg파일이름.svg') is not a valid name.
보아하니 storybook에서 svg 파일을 인식하지 못하는 것 같다.
그러기 위해 .storybook
폴더에 있는 main.ts
를 수정해야 한다.
// .storybook/main.ts
const config: StorybookConfig = {
// ...
webpackFinal: async (config) => {
// ...
if (config.module?.rules) {
config.module = config.module || {};
config.module.rules = config.module.rules || [];
const imageRule = config.module.rules.find((rule) => rule?.['test']?.test('.svg'));
if (imageRule) {
imageRule['exclude'] = /\.svg$/;
}
config.module.rules.push({
test: /\.svg$/,
use: ['@svgr/webpack'],
});
}
}
return config;
},
};
svg 컴포넌트 덕분에, 똑같은 모양이지만 다른 스타일을 적용한 SVG 파일을 모두 다운로드 받을 필요가 없어서 편리했다. 그리고 하나의 svg 컴포넌트로 색상, 크기 변경도 할 수 있어서 재사용하기 좋았다.