React에서 SVG 사용하기

컴파운드 패스

이미지를 자바스크립트 객체로 만들기 위해서는 Illustrator의 Compound Path 기능을 이용해 SVG의 그래픽 요소들을 통합해야 한다. 아래 Iamport 로고를 코드 에디터로 열어서 보면 8개의 그래픽 요소(rect, polygon, path)로 이루어진 그룹인 것을 확인할 수 있다.

<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="645.698px" height="136.753px" viewBox="0 0 645.698 136.753" enable-background="new 0 0 645.698 136.753" xml:space="preserve">
  <g>
    <rect x="0" y="0" fill="#2A3D4E" width="27.438" height="106.395"/>
    <polygon fill="#2A3D4E" points="57.224,0 81.454,0 81.454,20.31 65.277,45.395 50.096,45.395 57.224,20.31 	"/>
    <path fill="#2A3D4E" d="M182.575,39.834c3.23-4.941,7.065-8.702,11.508-11.294c4.439-2.589,9.322-3.883,14.642-3.883 c9.171,0,16.155,2.829,20.952,8.481c4.797,5.653,7.198,13.872,7.198,24.657v48.6h-25.653V64.779c0.044-0.62,0.08-1.261,0.107-1.925 c0.022-0.664,0.034-1.615,0.034-2.851c0-5.652-0.831-9.752-2.494-12.293c-1.662-2.54-4.347-3.813-8.052-3.813 c-4.845,0-8.588,1.995-11.225,5.984c-2.636,3.994-4.001,9.764-4.096,17.32v39.193h-25.653V64.779 c0-8.839-0.764-14.528-2.283-17.069c-1.518-2.54-4.229-3.813-8.123-3.813c-4.893,0-8.672,2.006-11.331,6.022 c-2.662,4.015-3.989,9.752-3.989,17.208v39.267H108.46V26.582h25.656v11.685c3.134-4.513,6.733-7.909,10.796-10.189 c4.06-2.282,8.54-3.421,13.433-3.421c5.509,0,10.379,1.331,14.609,3.99C177.18,31.309,180.388,35.037,182.575,39.834"/>
    <path fill="#2A3D4E" d="M286.189,94.85v41.903h-25.513V26.582h25.513v11.685c3.518-4.654,7.412-8.086,11.689-10.295 c4.277-2.212,9.189-3.315,14.75-3.315c9.837,0,17.905,3.909,24.232,11.722c6.313,7.814,9.47,17.877,9.47,30.181 c0,12.305-3.157,22.364-9.47,30.178c-6.327,7.816-14.395,11.722-24.232,11.722c-5.56,0-10.472-1.103-14.75-3.312 C293.601,102.939,289.707,99.507,286.189,94.85 M303.151,43.186c-5.465,0-9.654,2.006-12.581,6.022 c-2.92,4.015-4.381,9.797-4.381,17.353c0,7.552,1.461,13.337,4.381,17.353c2.927,4.016,7.116,6.021,12.581,6.021 c5.465,0,9.631-1.994,12.5-5.988c2.876-3.99,4.314-9.787,4.314-17.386c0-7.603-1.439-13.4-4.314-17.389 C312.782,45.182,308.616,43.186,303.151,43.186"/>
    <path fill="#2A3D4E" d="M403.129,24.657c13.732,0,24.455,3.706,32.176,11.118c7.722,7.411,11.578,17.673,11.578,30.785 s-3.857,23.374-11.578,30.785c-7.721,7.412-18.444,11.115-32.176,11.115c-13.776,0-24.55-3.703-32.316-11.115 c-7.772-7.411-11.651-17.673-11.651-30.785s3.878-23.374,11.651-30.785C378.58,28.363,389.354,24.657,403.129,24.657  M403.129,42.898c-5.649,0-9.971,2.032-12.935,6.095c-2.965,4.06-4.455,9.916-4.455,17.566c0,7.647,1.49,13.503,4.455,17.566 c2.964,4.06,7.286,6.091,12.935,6.091c5.56,0,9.809-2.031,12.758-6.091c2.942-4.063,4.418-9.919,4.418-17.566 c0-7.651-1.475-13.506-4.418-17.566C412.938,44.93,408.689,42.898,403.129,42.898"/>
    <path fill="#2A3D4E" d="M524.701,48.315c-2.233-1.043-4.453-1.818-6.659-2.316c-2.211-0.498-4.433-0.749-6.667-0.749 c-6.55,0-11.608,2.103-15.139,6.309c-3.541,4.204-5.311,10.226-5.311,18.064v36.771h-25.51V26.582h25.51v13.112 c3.282-5.225,7.042-9.038,11.29-11.438c4.255-2.4,9.352-3.599,15.295-3.599c0.856,0,1.777,0.037,2.772,0.107 c1.003,0.07,2.45,0.225,4.351,0.461L524.701,48.315z"/>
    <path fill="#2A3D4E" d="M574.008,3.92v22.662h26.289v18.241h-26.289v33.85c0,3.705,0.736,6.213,2.205,7.519 c1.474,1.309,4.396,1.961,8.769,1.961h13.112v18.241h-21.881c-10.074,0-17.205-2.102-21.41-6.305 c-4.21-4.204-6.304-11.343-6.304-21.416v-33.85h-12.685V26.582H548.5V3.92H574.008z"/>
    <path fill="#2A3D4E" d="M619.975,78.817h25.723v21.733l-17.671,26.582h-15.178l7.126-26.582V78.817z M619.975,26.582h25.723V54.16 h-25.723V26.582z"/>
  </g>
</svg>

Illustrator에서 이 로고파일을 연 다음 cmd+A로 모든 오브젝트를 선택한 뒤, 오브젝트 > 컴파운드패스 > 만들기로 컴파운드패스를 만든다.

svg-compound-path

컴파운드패스를 만들고 저장하면 SVG 코드가 아래와 같이 변경된다. 이전에 8개로 나눠져있던 그래픽요소가 하나로 합쳐져 있는 것을 발견할 수 있다.

<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="645.698px" height="136.753px" viewBox="0 0 645.698 136.753" enable-background="new 0 0 645.698 136.753" xml:space="preserve">
  <path fill="#2A3D4E" d="M0,0h27.438v106.395H0V0z M57.224,0h24.23v20.31L65.277,45.395H50.096l7.128-25.085V0z M182.575,39.834 c3.23-4.941,7.065-8.702,11.508-11.294c4.439-2.589,9.322-3.883,14.642-3.883c9.171,0,16.155,2.829,20.952,8.481 c4.797,5.653,7.198,13.872,7.198,24.657v48.6h-25.653V64.779c0.044-0.62,0.08-1.261,0.107-1.925 c0.022-0.664,0.034-1.615,0.034-2.851c0-5.652-0.831-9.752-2.494-12.293c-1.662-2.54-4.347-3.813-8.052-3.813 c-4.845,0-8.588,1.995-11.225,5.984c-2.636,3.994-4.001,9.764-4.096,17.32v39.193h-25.653V64.779c0-8.839-0.764-14.528-2.283-17.069 c-1.518-2.54-4.229-3.813-8.123-3.813c-4.893,0-8.672,2.006-11.331,6.022c-2.662,4.015-3.989,9.752-3.989,17.208v39.267H108.46 V26.582h25.656v11.685c3.134-4.513,6.733-7.909,10.796-10.189c4.06-2.282,8.54-3.421,13.433-3.421c5.509,0,10.379,1.331,14.609,3.99 C177.18,31.309,180.388,35.037,182.575,39.834 M286.189,94.85v41.903h-25.513V26.582h25.513v11.685 c3.518-4.654,7.412-8.086,11.689-10.295c4.277-2.212,9.189-3.315,14.75-3.315c9.837,0,17.905,3.909,24.232,11.722 c6.313,7.814,9.47,17.877,9.47,30.181c0,12.305-3.157,22.364-9.47,30.178c-6.327,7.816-14.395,11.722-24.232,11.722 c-5.56,0-10.472-1.103-14.75-3.312C293.601,102.939,289.707,99.507,286.189,94.85 M303.151,43.186 c-5.465,0-9.654,2.006-12.581,6.022c-2.92,4.015-4.381,9.797-4.381,17.353c0,7.552,1.461,13.337,4.381,17.353 c2.927,4.016,7.116,6.021,12.581,6.021s9.631-1.994,12.5-5.988c2.876-3.99,4.314-9.787,4.314-17.386 c0-7.603-1.439-13.4-4.314-17.389C312.782,45.182,308.616,43.186,303.151,43.186 M403.129,24.657 c13.732,0,24.455,3.706,32.176,11.118c7.722,7.411,11.578,17.673,11.578,30.785s-3.857,23.374-11.578,30.785 c-7.721,7.412-18.444,11.115-32.176,11.115c-13.776,0-24.55-3.703-32.316-11.115c-7.772-7.411-11.651-17.673-11.651-30.785 s3.878-23.374,11.651-30.785C378.58,28.363,389.354,24.657,403.129,24.657 M403.129,42.898c-5.649,0-9.971,2.032-12.935,6.095 c-2.965,4.06-4.455,9.916-4.455,17.566c0,7.647,1.49,13.503,4.455,17.566c2.964,4.06,7.286,6.091,12.935,6.091 c5.56,0,9.809-2.031,12.758-6.091c2.942-4.063,4.418-9.919,4.418-17.566c0-7.651-1.475-13.506-4.418-17.566 C412.938,44.93,408.689,42.898,403.129,42.898 M524.701,48.315c-2.233-1.043-4.453-1.818-6.659-2.316 c-2.211-0.498-4.433-0.749-6.667-0.749c-6.55,0-11.608,2.103-15.139,6.309c-3.541,4.204-5.311,10.226-5.311,18.064v36.771h-25.51 V26.582h25.51v13.112c3.282-5.225,7.042-9.038,11.29-11.438c4.255-2.4,9.352-3.599,15.295-3.599c0.856,0,1.777,0.037,2.772,0.107 c1.003,0.07,2.45,0.225,4.351,0.461L524.701,48.315z M574.008,3.92v22.662h26.289v18.241h-26.289v33.85 c0,3.705,0.736,6.213,2.205,7.519c1.474,1.309,4.396,1.961,8.769,1.961h13.112v18.241h-21.881c-10.074,0-17.205-2.102-21.41-6.305 c-4.21-4.204-6.304-11.343-6.304-21.416v-33.85h-12.685V26.582H548.5V3.92H574.008z M619.975,78.817h25.723v21.733l-17.671,26.582 h-15.178l7.126-26.582V78.817z M619.975,26.582h25.723V54.16h-25.723V26.582z"/>
</svg>

컴포넌트 만들기

이제 React에서 SVG를 다룰 컴포넌트를 만들어보자. 아래 코드를 SVG라는 컴포넌트로 저장한다.

import React, { Component } from 'react';

const svg = {
  iamport : 'M0,0h27.438v106.395H0V0z M57.224,0h24.23v20.31L65.277,45.395H50.096l7.128-25.085V0z M182.575,39.834 c3.23-4.941,7.065-8.702,11.508-11.294c4.439-2.589,9.322-3.883,14.642-3.883c9.171,0,16.155,2.829,20.952,8.481 c4.797,5.653,7.198,13.872,7.198,24.657v48.6h-25.653V64.779c0.044-0.62,0.08-1.261,0.107-1.925 c0.022-0.664,0.034-1.615,0.034-2.851c0-5.652-0.831-9.752-2.494-12.293c-1.662-2.54-4.347-3.813-8.052-3.813 c-4.845,0-8.588,1.995-11.225,5.984c-2.636,3.994-4.001,9.764-4.096,17.32v39.193h-25.653V64.779c0-8.839-0.764-14.528-2.283-17.069 c-1.518-2.54-4.229-3.813-8.123-3.813c-4.893,0-8.672,2.006-11.331,6.022c-2.662,4.015-3.989,9.752-3.989,17.208v39.267H108.46 V26.582h25.656v11.685c3.134-4.513,6.733-7.909,10.796-10.189c4.06-2.282,8.54-3.421,13.433-3.421c5.509,0,10.379,1.331,14.609,3.99 C177.18,31.309,180.388,35.037,182.575,39.834 M286.189,94.85v41.903h-25.513V26.582h25.513v11.685 c3.518-4.654,7.412-8.086,11.689-10.295c4.277-2.212,9.189-3.315,14.75-3.315c9.837,0,17.905,3.909,24.232,11.722 c6.313,7.814,9.47,17.877,9.47,30.181c0,12.305-3.157,22.364-9.47,30.178c-6.327,7.816-14.395,11.722-24.232,11.722 c-5.56,0-10.472-1.103-14.75-3.312C293.601,102.939,289.707,99.507,286.189,94.85 M303.151,43.186 c-5.465,0-9.654,2.006-12.581,6.022c-2.92,4.015-4.381,9.797-4.381,17.353c0,7.552,1.461,13.337,4.381,17.353 c2.927,4.016,7.116,6.021,12.581,6.021s9.631-1.994,12.5-5.988c2.876-3.99,4.314-9.787,4.314-17.386 c0-7.603-1.439-13.4-4.314-17.389C312.782,45.182,308.616,43.186,303.151,43.186 M403.129,24.657 c13.732,0,24.455,3.706,32.176,11.118c7.722,7.411,11.578,17.673,11.578,30.785s-3.857,23.374-11.578,30.785 c-7.721,7.412-18.444,11.115-32.176,11.115c-13.776,0-24.55-3.703-32.316-11.115c-7.772-7.411-11.651-17.673-11.651-30.785 s3.878-23.374,11.651-30.785C378.58,28.363,389.354,24.657,403.129,24.657 M403.129,42.898c-5.649,0-9.971,2.032-12.935,6.095 c-2.965,4.06-4.455,9.916-4.455,17.566c0,7.647,1.49,13.503,4.455,17.566c2.964,4.06,7.286,6.091,12.935,6.091 c5.56,0,9.809-2.031,12.758-6.091c2.942-4.063,4.418-9.919,4.418-17.566c0-7.651-1.475-13.506-4.418-17.566 C412.938,44.93,408.689,42.898,403.129,42.898 M524.701,48.315c-2.233-1.043-4.453-1.818-6.659-2.316 c-2.211-0.498-4.433-0.749-6.667-0.749c-6.55,0-11.608,2.103-15.139,6.309c-3.541,4.204-5.311,10.226-5.311,18.064v36.771h-25.51 V26.582h25.51v13.112c3.282-5.225,7.042-9.038,11.29-11.438c4.255-2.4,9.352-3.599,15.295-3.599c0.856,0,1.777,0.037,2.772,0.107 c1.003,0.07,2.45,0.225,4.351,0.461L524.701,48.315z M574.008,3.92v22.662h26.289v18.241h-26.289v33.85 c0,3.705,0.736,6.213,2.205,7.519c1.474,1.309,4.396,1.961,8.769,1.961h13.112v18.241h-21.881c-10.074,0-17.205-2.102-21.41-6.305 c-4.21-4.204-6.304-11.343-6.304-21.416v-33.85h-12.685V26.582H548.5V3.92H574.008z M619.975,78.817h25.723v21.733l-17.671,26.582 h-15.178l7.126-26.582V78.817z M619.975,26.582h25.723V54.16h-25.723V26.582z',
}

class SVG extends Component {
  render() {
    return(
      <svg
      	x="0px" y="0px"
      	width={this.props.width ? this.props.width : "auto"}
      	height={this.props.height ? this.props.height : "auto"}
      	viewBox="0 0 645.698 136.753">
        <path
          fill={this.props.color}
          d={svg[this.props.name]}
        />
      />
    );
  }
}

export default SVG;

svg라는 객체를 만들고 그 안에 name : ‘path’와 같이 JSON 타입으로 아이콘의 정보를 추가한다. 항목이 많아질 경우 별도의 파일로 빼내고 import해서 사용할 수도 있다. 여기서는 아이콘 이름을 iamport로 하고 path에 위에서 만든 아이콘 파일의 d 값을 복사해서 붙여넣었다.

SVG 클래스 안의 svg 요소에는 이미지를 정상적으로 그리기 위해 반드시 필요한 시작점 좌표와 viewBox만 둔 채 제외한 나머지 속성들은 삭제한다. 특히 enable-background 속성의 경우 eslint에서 에러를 내므로 반드시 삭제해야 한다.

width, height, color, name은 props로 받는다.

컴포넌트 사용하기

아래와 같은 방식으로 컴포넌트를 사용하여 이미지를 넣는다.

import React, { Component } from 'react';
import SVG from '../../components/SVG';

export class Home extends Component {
  render() {
    return (
      <div>
        <SVG name="iamport" height="4rem" color="#2f3e4d" />
      </div>
    );
  }
}

이미지가 정상적으로 보이는 것을 확인할 수 있다. 이제 이미지가 필요한 곳마다 컴포넌트를 import하여 쉽게 이미지를 추가할 수 있다.

아이콘 라이브러리

이 방식을 이용하면 “자주 쓰는 웹요소 아이콘 세트”를 React 컴포넌트로 만들어서 프로젝트 내 어느 파일에서든 쉽게 아이콘을 불러오고 동일한 이미지를 재사용할 수 있다.

물론 이 방식이 아니더라도 쉽고 간편한 대안은 많이 있다. Font Awesome, ionicons, Semantic-UI 등이 널리 쓰이고 있다. 그럼에도 불구하고 Custom Set을 만드는 이유는 이미지를 원하는대로 수정할 수 있고, 불필요한 아이콘은 프로젝트에 포함시키지 않음으로써 용량을 줄이는 등의 장점이 있기 때문이다.

1) SVG 아이콘 파일 구하기

svg-compound-path

The Noun ProjectFlaticon과 같은 사이트에서 SVG 아이콘을 구할 수 있다. 두 사이트 모두 유사한 느낌의 아이콘들을 패키지로 제공하기 때문에 잘 활용하면 통일감 있는 디자인을 만들 수 있다.

2) SVG 파일로 저장하기

svg-compound-path

다운로드 받은 파일 중 실제 사용할 아이콘 영역을 복사한 뒤 64px * 64px의 새로운 문서에 붙여넣는다. 그리고 오브젝트의 가로세로 중 큰 쪽을 64px로 지정하여 오브젝트가 캔버스에 가득 차게 사이즈를 조정하고 정렬도구로 캔버스의 한가운데에 위치하게 만든다. 이렇게 하면 앞으로 만드는 다른 아이콘들도 동일한 비율로 사용할 수 있다.

또한 SVG파일로 저장하기 전 Compound Path를 만드는 과정도 진행한다.

svg-compound-path

3) React 컴포넌트 만들기

React에 SVG 컴포넌트를 만든다. 이번에는 정사각형 아이콘을 만들기 때문에 width, height 대신 size로 크기값을 받고, viewBox에 새로 만든 캔버스 사이즈인 0 0 64 64로 입력했다.

import React, { Component } from 'react';

const svg = {}

class SVG extends Component {
  render() {
    return(
      <svg
        x="0px" y="0px"
        width={this.props.size}
        height={this.props.size}
        viewBox="0 0 64 64">
        <path
          fill={this.props.color}
          d={svg[this.props.name]}
        />
      />
    );
  }
}

export default SVG;

4) 아이콘 패스 추가하기

SVG 파일을 열고 path 중 d로 시작하는 패스 부분의 값을 복사한다.

<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
  <svg version="1.1" id="레이어_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="64px" height="64px" viewBox="0 0 64 64" enable-background="new 0 0 64 64" xml:space="preserve">
    <path fill="#040000" d="M58.68,8.737c-5.624-5.622-16.473-8.073-24.752-0.905l4.353,4.353c0.521,0.52,0.521,1.365,0,1.885 c-0.517,0.517-1.359,0.526-1.885,0l-5.335-5.333c-7.14-7.14-18.658-7.073-25.731,0c-7.087,7.085-7.127,18.608,0,25.732 l25.731,25.733c0.521,0.52,1.365,0.52,1.885,0L58.68,34.469C65.773,27.374,65.773,15.832,58.68,8.737z"/>
</svg>

그리고 아래와 같이 조금 전에 만든 React 컴포넌트의 SVG 객체 안에 JSON 형태의 배열로 추가한다. name에는 앞으로 사용할 아이콘 이름을 붙여주자.

const svg = {
  heart: "M58.68,8.737c-5.624-5.622-16.473-8.073-24.752-0.905l4.353,4.353c0.521,0.52,0.521,1.365,0,1.885 c-0.517,0.517-1.359,0.526-1.885,0l-5.335-5.333c-7.14-7.14-18.658-7.073-25.731,0c-7.087,7.085-7.127,18.608,0,25.732 l25.731,25.733c0.521,0.52,1.365,0.52,1.885,0L58.68,34.469C65.773,27.374,65.773,15.832,58.68,8.737z"
}

5) 아이콘 사용하기

이제 원하는 곳에 SVG 컴포넌트를 import한 뒤 아래와 같이 사용할 수 있다. 적절한 마크업과 함께 아이콘을 사용하자.

import React, { Component } from 'react';
import SVG from '../../components/SVG';

export class Home extends Component {
  ....
  render() {
    return (
      <div className="like">
        <SVG name="heart" height="30px" color="#ff0000" />
        <span>{this.state.like}</span>
      </div>
    );
  }
}

이상의 내용은 David Gilbertson의 Icons as React Components을 재구성한 것이다.