[React] svg 에셋을 컴포넌트로 변환하여 사용하기 - SVGR
Code/React - Node

[React] svg 에셋을 컴포넌트로 변환하여 사용하기 - SVGR

반응형

현재 서비스의 클라이언트 프로젝트는 에셋은 거의 모든 것을 svg 파일을 사용하고 있는데, svg를 프로젝트에 유연하게 적용시키기 위해 여러가지 도전을 해봤던 것 같다.

svg 파일을 Img 태그에 그대로 넣어 사용하기거나 svg를 임포트 해서 컴포넌트로 만들어 보기도 했었다.

컴포넌트화 시킨 svg 를 또 잘 활용할 수는 없을까 해서 current value 를 이용해 svg 프롭에 직접 접근하여 관리하는 방식도 고민했었는데 이건 디자인 쪽에서 넘어오는 에셋들이 current 를 사용해 관리하기 힘든 구조였기 때문에 사용하지는 못했다 (svg 스타일의 통일성 부족)

vscode를 통해 코드 작성을 하는데, 에셋은 자동 임포트가 또 작동되지 않아서 파일 위치 찾아서 하나하나 경로 보고 import 문 작성해야 하는 것도 번거로웠을 뿐더러 꼭 아래와 같이 네임스페이스를 지정해주어야 하는 문제가 있었다.

import { ReactComponent as Icon } from 'assets/icon.svg';

<Icon />

 

 

물론 할 수야 있지만... svg 파일 프로젝트에 집어 넣는 것도 귀찮은데 임포트까지 이렇게 섬세하게 하고 싶진 않다 라는 생각이 들었고, svg를 관리하기 좋은 방법을 찾아 다니다가 svgr (react-svgr) 라이브러리를 발견했다.

https://react-svgr.com/

 

SVGR - Transforms SVG into React Components. - SVGR

Transforms SVG into React Components.

react-svgr.com

 

svg 에셋을 넣으면 react에서 사용 가능한 컴포넌트로 만들어주는 라이브러리라고 이해하면 쉬울 듯 하다.

프론트엔드로서는 혼자 프로젝트를 담당하게 되면서 프로젝트의 설계나 스택 구조 등을 계속 생각하고 만들어내느라 프로젝트 초반에 꽤나 골머리를 앓고 있었는데, 그때 이 라이브러리를 발견하고 꽤나 기분이 좋았던 기억이 난다 (사실 많이 좋았다)

단 한 가지 고민이었던 포인트는 기존 네이밍 컨벤션 때문이었는데,

svg를 컴포넌트로 만드는 경우 그 컴포넌트의 이름 끝에는 Icon이라는 접미사를 붙여 svg 컴포넌트와 다른 컴포넌트를 구별하고 있었으나, svgr 라이브러리의 기본 설정대로 사용 시 원하는 네이밍 방식으로 받을 수 없었다.

이 문제를 해결하기 위해 라이브러리의 configure 를 훑어보니 커스텀 템플릿을 통해 변환이 가능한 듯 하여 컨벤션에 맞는 파일을 만들 수 있도록 템플릿을 작성했다.

svgr.index.template.js

const path = require('path');
function defaultIndexTemplate(filePaths) {
  const exportEntries = filePaths.map(filePath => {
    function camelize(str) {
      return str[0].toUpperCase() + str.slice(1);
    }
    const basename = path.basename(filePath, path.extname(filePath));
    const exportName = /^\d/.test(basename) ? `Svg${basename}` : basename;
    return `export { default as ${camelize(
      exportName,
    )}Icon } from './${basename}'`;
  });
  return exportEntries.join('\n');
}
module.exports = defaultIndexTemplate;

 

카멜케이스와 'Icon' 접미사가 적용된 index 파일을 만드는 템플릿이다.

카멜케이스는 간단하게 적용하고 싶었는데 config에 나와있는 방법으로는 적용이 안돼서 템플릿에 같이 구현했다.

svgr.template.js

const template = (variables, {tpl}) => {
  return tpl`
${variables.imports.slice(1)};
${variables.interfaces};
const ${variables.componentName} = (${variables.props}) => (
  ${variables.jsx}
);
 
${variables.exports};
`;
};
module.exports = template;

svg 에셋을 JSX 파일로 만들어주는 템플렛이다.

그리고 package.json 에 아래 script 를 추가 해 원하는 형태의 파일을 만들어 낼 수 있었다.

{
	"scripts" : {
     	 "icons": "npx @svgr/cli --out-dir src/components/icons -- src/assets",
    }
}

src/assets 경로에 svg 파일을 넣어 놓을테니 src/components/icons 에 만들어 주세요 라는 의미이다.

스크립트 실행 후의 icons 폴더

 

성공적으로 실행이 되면 아래와 같이 svg 파일이 템플렛의 요구사항대로 생성되어있음을 확인할 수 있다.

 

실 사용 시 아래와 같이 간단하게 사용하면 된다.

 

svgr을 쓰지 않았던 때를 상상하기 싫을 정도로.. 이제 만들어진 컴포넌트를 그저 임포트 해서 사용하는 것이 너무 익숙하고 또 편해져 버렸다. 

그러다 svg가 아닌 다른 형식의 소스를 보여줘야 할 때 img 태그에 넣으려고 직접 경로 찾아 임포트 문을 작성하고 있을 때가 돼서야 '아..엄청 귀찮네..' 하는 생각이 들곤 한다.

그래도 제일 많이 쓰는 svg를 편하게 쓸 수 있음에 감사하다..

그러니 뜬금없지만 과거의 나를 칭찬해줘야겠다.

 

게으른 자가 세상을 지배한다!!!

반응형