알아두면 좋은 라이브러리

[svgr] SVG 아이콘 컴포넌트 생성 :: 마이구미

mygumi 2022. 1. 28. 22:56
반응형
이 글은 svg 아이콘과 관련된 라이브러리 SVGR 을 다룬다.
리액트에서 아이콘을 svg 파일로 관리하는 경우 도움을 줄 수 있는 라이브러리이다.
글에서 소개하는 예제들은 각 tag 를 checkout 을 통해 확인할 수 있다.
예제 - https://github.com/hotehrud/svgr-example

조금이나마 도움을 줄 수 있는 라이브러리들을 소개하는 카테고리로 분류된 글이다.
알아두면 좋은 라이브러리

 

 

SVGR 은 svg 파일을 React 컴포넌트로 변환시켜주는 라이브러리이다.

원본 svg 와 svg 를 React 컴포넌트로 변환한 코드를 각각 확인해보자.

 

// icon.svg
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1"  xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
	 viewBox="0 0 24 24" xml:space="preserve">
<path d="M22,11h-4.2L21,7.8l-1.4-1.4L15,11h-2V9l4.7-4.7l-1.4-1.4L13,6.2V2h-2v4.2L7.8,2.9L6.3,4.3L11,9v2H9L4.3,6.3
	L2.9,7.8L6.2,11H2v2h4.2L3,16.2l1.4,1.4L9,13h2v2l-4.7,4.7l1.4,1.4l3.3-3.3V22h2v-4.2l3.2,3.2l1.4-1.4L13,15v-2h2l4.7,4.7l1.4-1.4
	L17.8,13H22V11z"/>
</svg>

 

// Icon.tsx
import * as React from "react";

const SvgAc = (props) => (
  <svg
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 24 24"
    xmlSpace="preserve"
    {...props}
  >
    <path d="M22 11h-4.2L21 7.8l-1.4-1.4L15 11h-2V9l4.7-4.7-1.4-1.4L13 6.2V2h-2v4.2L7.8 2.9 6.3 4.3 11 9v2H9L4.3 6.3 2.9 7.8 6.2 11H2v2h4.2L3 16.2l1.4 1.4L9 13h2v2l-4.7 4.7 1.4 1.4 3.3-3.3V22h2v-4.2l3.2 3.2 1.4-1.4L13 15v-2h2l4.7 4.7 1.4-1.4-3.3-3.3H22v-2z" />
  </svg>
);

export default SvgAc;

 

svg 파일은 svg 태그로 구성되어있고, React 컴포넌트는 랜더링을 svg 태그로 구성하고 있는 모습을 볼 수 있다.

 

그렇다면 왜 svg 를 React 컴포넌트로 변환해야하는가?

 

svg 를 활용해서 아이콘을 관리 및 사용하거나 아이콘 라이브러리를 쓰면서 커스텀 아이콘이 필요한 경우가 있다.

이러한 경우에는 <img src="icon.svg" /> 가 아닌 <Icon /> 형태를 더 선호할 것이다.

 

const Car = () => {
  return (
    <svg ... />
  )
}

const App = () => {
  return (
    <div>
      <Car />
    <div>
  )
}

 

결국 우리는 React 컴포넌트를 작성하고 svg 태그를 입력하는 수동적인 작업이 필요한 것이다.

이러한 수동적인 작업을 svgr 에서 대신 해주는 것이다.

svg 확장자 파일을 jsx 확장자 파일로 생성해주는 것이다.

 

  • svg/car.svg => components/Ac.jsx
  • svg/airplane.svg => components/Airplane.jsx
  • svg/heart.svg => components/Heart.jsx

 

원하는 아이콘 파일(svg)을 svg 디렉토리에 넣은 후, svgr 명령어를 실행해주면 된다.

 

$ svgr svg -d src/components

 

svg 디렉토리에 있는 svg 파일들을 기반으로 components 디렉토리에 React 컴포넌트가 생성된다.

그리고 생성된 컴포넌트들을 export 하는 index 파일까지 생성된다.

 

export { default as Ac } from "./Car";
export { default as Accessible } from "./Airplane";

 

위의 예제는 태그 01-basic 를 통해 확인할 수 있다.

 

그리고 svgr 을 활용해서 더 많은 기능들이 가능해진다.

여기서는 2가지 기능을 추가해본다.

 

  • svg title 요소 넣기
  • svg 를 emotion/styled 로 감싸기

 

우선 svg title 기능을 넣어보자.

svg 에서는 title 요소를 제공해준다. (https://developer.mozilla.org/en-US/docs/Web/SVG/Element/title)

title 의 값이 마우스오버를 하면 툴팁처럼 보여지게 된다.

 

 

사전에 svg 파일에서 title 요소를 넣을 수도 있겠지만, title 의 값을 props 로 전달 받아야한다면 어떻게 할 수 있을까?

 

svgr 을 통해 생성되는 React 컴포넌트를 수정해야한다.

props 에 title 을 추가해야하고 랜더링 코드쪽에서는 <title>{props.title}</title> 같은 코드가 존재해야한다.

 

const SvgAirplane = ({ title, ...props }: SVGProps<SVGSVGElement> & {
  title: string,
}) => {
  return (
    <svg
      xmlns="http://www.w3.org/2000/svg"
      {...props}
    >
      {!!title && <title>{title}</title>}
      <path d="m6.568 4.675 6.302 2.76-2.228 2.326-4.78-4.224a.25.25 0 0 1-.042-.387l.44-.44a.251.251 0 0 1 .308-.035Zm10.403 6.847 2.76 6.315a.252.252 0 0 1-.036.309l-.44.439a.25.25 0 0 1-.387-.042l-4.23-4.8 2.333-2.221ZM6.84 15.22l-2.692-2.262a.25.25 0 0 1-.074-.404l.56-.56a.25.25 0 0 1 .31-.034l3.147.82-1.16 2.249c-.028.056-.064.12-.091.19Zm4.794 1.102.894 3.183c.024.087 0 .18-.064.245l-.598.596a.25.25 0 0 1-.404-.072l-2.275-2.707.17-.085 2.277-1.16Zm8.426-12.025.041.044.017.02c.22.273.25.64.266.864.074 1.216-.075 2.074-.47 2.698l-.036.06a1.713 1.713 0 0 1-.327.407l-7.48 7.127-.054.051-.065.033-2.817 1.437c-.346.188-1.266.442-1.737-.03-.51-.509-.192-1.438-.028-1.748l1.435-2.807.033-.065.05-.052 7.125-7.479c.303-.322.633-.48.951-.606.637-.24 1.447-.307 2.566-.205a.898.898 0 0 1 .53.251Zm-1.172 7.585a.248.248 0 0 1 .354 0l1.06 1.061a.247.247 0 0 1 0 .353l-1.385 1.386-.87-1.959.841-.84ZM11.11 4.104a.248.248 0 0 1 .354 0l1.06 1.061a.247.247 0 0 1 0 .353l-.842.841-1.958-.869 1.386-1.386Z" />
    </svg>
  );
};

 

이것이 의미하는 것은 svgr 에서 자동으로 변환해주는 템플릿을 우리가 직접 커스텀 할 수 있어야하는 것이다.

이것을 위해 svgr 에서는 Custom Template 기능을 제공해주고 있다. 

 

const template = (variables, { tpl }) => {
  return tpl`
${variables.imports};

${variables.interfaces};

const ${variables.componentName} = (${variables.props}) => (
  ${variables.jsx}
);
 
${variables.exports};
`
}

module.exports = template

 

svgr.config.js 파일을 생성해서 기능에 대한 커스텀 및 확장을 할 수 있다.

 

const template = ({ componentName, jsx, props, imports, exports }, { tpl }) =>
  tpl` ... `;

module.exports = {
  template,
  jsx: {
    babelConfig: {
      plugins: [dynamicTitlePlugin],
    },
  }
};

 

템플릿을 우리가 원하는 형태로 작성하고, 랜더링하는 부분에 title 태그를 삽입하는 플러그인을 선언해주었다. (예제 코드 참고)

svg title 기능이 적용된 예제 코드는 태그 02-svg-title 를 통해 확인할 수 있다. 

 

이처럼 커스텀이 자유롭다면 조금 더 나아가서 emotion/styled 로 랩핑하여 style Props 를 활용할 수 있다.

이것을 통해 얻는 이점은 일반적인 svg 태그가 아닌 Styled Component 처럼 사용하는 것이다.

그렇게 되면, svg 태그만이 가질 수 있는 속성 이외의 theme 과 같은 style Props 들을 이용할 수 있게 된다.

 

<Car color="primary" />

 

위처럼 사용하기 위해서는 몇가지 아이디어가 필요하다.

우선 Styled Component 로 제작된 컴포넌트(Svg.tsx)를 하나 더 생성해준다.

 

const color = (props) => {
  if (props.color) {
    return css`
      color: props.theme[props.color].base;
    `
  }
  return null
}

const Svg = styled('svg')`
  flex: none;
  line-height: 1;
  ${color}
`

export default Svg

 

그리고 svgr 을 통해 변환된 React 컴포넌트의 <svg ... /> 코드를 <Svg ... /> 로 변경해주어야한다.

이것을 위한 플러그인을 추가해준다. (예제 코드 참고)

 

// svgr.config.js
module.exports = {
  template,
  jsx: {
    babelConfig: {
      plugins: [dynamicTitlePlugin, replaceSvgPlugin],
    },
  }
};

 

결과적으로 변환된 React 컴포넌트의 랜더링 부분은 svg 태그가 아닌 Styled Component 인 Svg 로 대체되는 것이다.

styled Compoent 가 적용된 예제는 태그 03-svg-emotion 를 통해 확인할 수 있다.

 


 

사실 CRA(Create-react-app) 를 사용해봤다면, 우리는 다음과 같은 코드를 볼 수 있다.

 

import { ReactComponent as Logo } from './logo.svg';

function App() {
  return (
    <div>
      {/* Logo is an actual React component */}
      <Logo />
    </div>
  );
}

 

위 코드는 CRA 설치 시 기본적으로 존재하는 코드이다.

svg 파일을 import 했는데 React 컴포넌트처럼 바로 사용하는 모습을 볼 수 있다.

이것이 가능한 이유는 이미 CRA 내부에 기본적으로 svgr 라이브러리를 지원하고 있기 때문이다.

https://create-react-app.dev/docs/adding-images-fonts-and-files/#adding-svgs

 

 

 

실제로는 priceline 디자인 시스템의 아이콘 모듈을 참고하였고, 본인의 예제는 간략하게 정리한 것이다.

더 세세한 코드를 원하면 참고하면 좋을 것 같다.

 

 

반응형