SvgIcon 컴퍼넌트: svg sprite html에 임베드 방식

쏘쏘임·2022년 1월 12일
2

프론트엔드

목록 보기
1/6

GIFT 프로젝트 진행 중 svg 아이콘들을 어떻게 관리할까 고민을 하게 되었다. 무거운 gif 파일들을 많이 받아오는 사이트 특성 상 성능 최적화를 특히나 더 고려해야하기 때문이다. 성능 최적화와 컴퍼넌트 재사용 문제를 고민하며 개선하고 배워과는 과정을 기록한다.

🚩 svg sprite 기법으로 리액트에서 SvgIcon 컴퍼넌트를 만들어보자.

  • svg sprite를 이용해 서버와의 통신을 최소화한다.
  • svg sprite 이미지를 외부 프로그램 없이 쉽게 추가하도록 한다.
  • html 파일과 관심사를 분리하기 위해 파일로 받고 사용하도록 한다.
  • 의미를 가진 아이콘이라면 보조기기로 대체 텍스트를 인지할 수 있어야 한다.
  • svg 프레임은 기본적으로 부모 요소의 크기에 가득차며, height나 width로 지정해줄 수도 있다.

🌱 SvgIcon 컴퍼넌트 관련 링크
1. SvgIcon 컴퍼넌트: svg sprite html에 임베드 방식
2. SvgIcon 컴퍼넌트 개선: sprite 이미지 라이브러리로 생성 및 이용


1. svg sprite 기법이란?

svg 스프라이트의 기본 원리는 이미지 스프라이트와 같다. 이미지 스프라이트는 서버와의 통신을 최소화하기 위해 하나의 이미지 파일에 모든 이미지들을 촘촘하게 넣은 후 좌표와 width, height로 필요한 이미지만 잘라내 사용하는 이미지 최적화 기법이다. (스프라이트 이미지 사이즈나 위치가 조금이라도 달라진다면 엄청난 후폭풍이 몰아칠 것...)

SVG(Scalable Vector Graphics)는 백터 그래픽 이미지이며, xml 기반으로 화면에 그려지기 때문에 코드로 조정할 수 있다.

svg sprite 기법은 하나의 svg sheet에 모든 svg를 묶어 서버로부터 단 한번만 전송받은 후 필요한 부분만 뽑아 쓰는 것이다. svg 태그 안에 id로 정의한 이미지가 있다면 이것을 <use> 태그와 href 속성으로 호출하면 된다.

<svg viewBox="0 0 30 10">
  <circle id="myCircle" cx="5" cy="5" r="4" stroke="blue"/>
  <use href="#myCircle" x="10" fill="blue"/>
  <use href="#myCircle" x="20" fill="white" stroke="red"/>
</svg>

MDN use 참고하기

2. 리액트로 SvgIcon 컴퍼넌트 만들기

svg sprite 기법을 위한 요구사항

  1. 여러 svg 파일을 묶은 svg를 준비한다.
  2. 서버로부터 단 한번만 이 svg 묶음을 불러와야 한다.
  3. id를 지정하여 필요한 svg 이미지를 불러올 수 있어야 한다.
  4. (선택) width, height, fill 등 컴퍼넌트에서 수정할 수 있는 요소들도 props로 추가한다.

여러 svg 파일을 묶은 svg를 준비한다.

  • Figma에서 필요한 svg 아이콘들의 묶음을 export했다. 이 때 각각의 아이콘들을 클릭하고 내보내야 각각의 파일들을 받을 수 있다.
    Figma svg icons with type

  • 피그마로 받은 svg 파일들을 묶은 Sprite Sheet를 만들었다. svg 파일들을 하나의 파일로 묶어주는 유용한 도구로 Spritebot를 사용하면 편하다.
    spritebot svg 파일 넣기
    설치하고 실행하면 나오는 위 창에 svg들을 넣고 Save Sprite Sheet만 누르면 된다. 각각의 svg 파일들의 이름이 id가 되므로 파일 이름을 잘 지정해줘야 나중에 편하다.

  • 짠 - svg 스프라이트 코드
    svg 스프라이트 코드

서버로부터 단 한번만 svg 묶음을 불러와야 한다.

root 요소에는 동적으로 랜더링될 요소들을 삽입해 왔기 때문에, 이를 구분해주고자 body 하단에 넣어줬다.

  • svg 스프라이트 코드가 삽입된 index.html
    랜더링 후 html

서버로부터 한번만 받아오는 가장 간단한 방법으로는 index.html 파일에 svg 코드를 그냥 정적으로 박아주는 방법이 있다. 하지만 이런 경우 유지 보수에 어려움이 있을 수 있기 때문에 동적으로 삽입했다.

  • GlobalSvgSprite : html 파일에 정적으로 삽입해주는 컴퍼넌트
    - createPortal을 이용해 돔 요소에 접근하여 svg 스프라이트 코드를 넣어준다. 이 컴퍼넌트는 여러번 호출할 일이 없는 App 컴퍼넌트에서 사용했다.
    GlobalSvgSprite 코드

  • SvgIcon : 필요한 id의 svg 요소를 반환하는 컴퍼넌트
    - id는 사용시 정확하게 입력해야하는 부분이기 때문에 입력값들에 대한 타입을 지정해줘야 한다.
    -(문제) fill 속성으로 svg의 색 변경이 안된다. 스프라이트 코드에 이미 지정된 색들이 우선권을 갖고 있기 때문이다. (수정이 필요한 부분이니 색 지정은 참고하지 마십시오...)
    SvgIcon

id를 지정하여 필요한 svg 이미지를 불러올 수 있어야 한다.

현재 프로젝트에서는 타입스크립트를 사용하기 때문에 삽입된 svg id 들을 유니온 타입으로 타입 지정하였다. enum을 사용하지 않고 유니온 타입으로 사용한 이유는 런타임에 영향을 주지 않기 위해, 또 귀찮게 import를 하지 않기 위해서다. 카카오 FE 기술 블로그의 아티클을 보고 꿀팁을 전수받았다. 타입스크립트 꿀팁

완성!

후훗

삽질

  1. 처음엔 GlobalSvg와 SvgIcon을 분리하지 않고 svg 묶음을 index.html에 넣어주는 작업과 필요한 svg 요소를 반환하는 작업을 하나의 컴퍼넌트에서 모두 수행

이를 위해 SvgIcon 컴퍼넌트 내에서 rendered 라는 상태를 두고 useEffect를 통해 처음 마운트될 때 image들이 삽입되었는지 체크를 했고, 아직 rendered가 안 된 경우만 svg 묶음을 넣어주었다. 이 체크가 완료된 다음 props로 들어온 id에 해당하는 svg 요소를 반환하는 컴퍼넌트를 만들었다.

이렇게 제작한다면 'svg icon'에 대한 모든 관심사가 집중된 점은 좋지만 해당 컴퍼넌트를 사용할 때 마다 불필요한 체크를 한다는 단점이 있었다.

결국 기능적으로 관심사를 분리하여 GlobalSvg 컴퍼넌트로 html 파일에 정적으로 삽입해주는 컴퍼넌트와 필요한 id의 svg 요소를 반환만 하는 SvgIcon 컴퍼넌트를 따로 만들었다.

불필요한 체크는 줄였으나 docs에 GlobalSvg 컴퍼넌트가 SvgIcon 컴퍼넌트에 선행되어야 함을 명시하여야 유지보수에서 발생할 수 있는 문제를 방지할 수 있을 것이다.

  1. Icon 컴퍼넌트 이름

Icon은 보통 gitignore 기본 세팅으로 추가되어 있는 디렉토리 이름이었다. 이곳저곳에서 자주 사용되는 명칭이니 커스텀 디렉토리 이름을 Icon으로 지정하지 말자...

💡 배운 점

  • svg sprite 기법
  • 재사용이 가능한 svg 아이콘 컴퍼넌트 만들기
  • 관심사의 분리에 대한 기준 고민
  • Icon은 컴퍼넌트 이름으로 쓰지 말자

📌 참고 링크
Spritebot
MDN use 참고하기

profile
무럭무럭 자라는 주니어 프론트엔드 개발자입니다.

0개의 댓글