• [svgr] SVG 아이콘 컴포넌트 생성 :: 마이구미
    알아두면 좋은 라이브러리 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 디자인 시스템의 아이콘 모듈을 참고하였고, 본인의 예제는 간략하게 정리한 것이다.

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

     

     

    반응형

    댓글

Designed by Tistory.