webpack 환경에서 svg 컴포넌트 만들기 (feat: Storybook)

aken·2023년 12월 11일
0

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 컴포넌트를 통해 원하는 아이콘 색상과 크기를 설정하기로 했다.

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>

svg 컴포넌트 만드는 방법

1. file-loader

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" />
	)
}

2. @svgr/webpack

@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할 필요가 없어서 이 방법을 택했다.

svg 파일을 불러올 수 없다고?!

그런데 TypeScript에서 svg 파일을 불러올 수 없다는 에러가 발생했다.
맨 위에서 본 ReactComponent의 type처럼 따로 svg 파일에 관한 type을 설정해야했다.

// custom.d.ts
declare module "*.svg" {
  const content: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
  export default content;
}

그리고 tsconfig.jsoncustom.d.ts 파일 경로를 넣어준다. (참고로 필자는 tsconfig.json와 같은 위치에 파일을 만들었다.)

include: ["src/**/*", "custom.d.ts"]

storybook에서 마주친 에러

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 컴포넌트로 색상, 크기 변경도 할 수 있어서 재사용하기 좋았다.

0개의 댓글