SVG "내보내기"에 대한 정리 - (1)

조경석·2022년 12월 11일
1
post-thumbnail

서론

디자이너와 개발자의 통역사 역할을 도맡았던 실무 경험에서 내린 결론은
디자이너가 개발 공부하고 개발자가 디자이너 공부한다고 당장 말이 통하는건 아니라는 것이다.
외국어에서 당장 문법 달달 외고 단어 다 안다고 일상 대화가 되지는 않는것과 같다.

디자이너 경력을 잠시 접어두고 웹 프론트엔드 개발 공부를 시작한 김에,
디자이너는 이해할 수 없고, 개발자는 무심하게 생각하는 부분에 대해 블로그로 남겨보고자 한다.

그 시작으로, 실무에서 가장 유용하게 써먹었던 웹 환경의 그래픽 에셋을 위한 SVG 형식에 대한 정리를 선택했다.
앞으로 스스로 개발하면서 내가 만들어서 내가 쓸 SVG에 대해 확실히 공부해볼 겸사겸사,
각 툴에서 내보낼 때 나타나는 특징에 대해 정리해보면서 SVG의 특징과 효율적인 압축 방법을 고민해볼 생각이다.

SVG

과거에는 웹 브라우저 렌더링 엔진의 한계와 저밀도 디스플레이로 인해 래스터 이미지를 크롭하여 사용했지만,
다원화된 디스플레이와 그만큼 다양해진 웹 환경에서 동일한 경험을 주기 위해 벡터 그래픽을 사용하는 추세다.

caniuse.com의 SVG 검색결과, IE를 제외하고는 당연히 초기 버전부터 지원했지만, 웹 표준 상으로는 1.1 스펙이 권장된다.

본문에서는 SVG의 요소와 특성에 대해서는 자세히 다루지 않을 예정이다.
해당 내용이 필요하다면 MDN web docsW3Schools를 참조하길 바란다.

내보내기

SVG 형식의 벡터 그래핏 에셋은 그 특성상 래스터 이미지 에셋보다 용량이 작고, HTML의 인라인에서 바로 처리할 수 있다는 장점이 있다.

과거에 비해 전송 속도가 빠르고 통신 용량에 부담이 많이 사라진 현재에는 크게 느껴지지 않을 수 있으나,
임베딩된 웹 애플리케이션이나 폐쇄망 환경 등 생각보다 다양한 경우에 불필요한 용량을 최대한 줄이는 작업이 필요하다.

그래서 웹 개발에서 사용하는 대부분의 디자인 툴에는 SVG 형식으로 그래픽 에셋을 내보내는 기능이 있으며,
아예 개발자가 알아서 추출할 수 있도록 준비되어 있는 디자인 가이드 협업 툴도 많이 사용되고 있다.
하지만 사실, 이렇게 추출된 대부분의 SVG 파일은 끔찍할 정도로 최적화되어있지 않다.
추출된 SVG 파일을 열어본 개발자는 마크업 꼬라지를 보고 한숨을 쉬고 SVGOMG와 같은 압축 도구를 사용할 것이다.
하지만 사실(2), 이렇게 압축된 대부분의 SVG는 불필요한 마크업이 여전히 남아있거나, 디자이너의 의도를 해치고 형태가 깨지게 된다.

결국 디자인 의도를 가진 디자이너가 SVG에 대해 이해하고 벡터 그래픽 작업 단계에서 최적화하거나,
XML 형식에 익숙한 프론트엔드 담당자가 직접 마크업을 편집해 압축하는 작업이 필요하다.
당연히 둘 다 한명이 하면 더할나위 없고, 그걸 하는 개자이너가 되고자 툴 단위로 SVG Export의 특징과, 압축을 위한 방법을 정리해보려 한다.

예제 벡터 이미지를 작업하기 위한 스타일가이드.
경험상 디자인 툴마다 차이가 있었던 부분을 의도적으로 모아놓았고,
dashed line, inner shadow나 blur처럼 SVG로 내보낼때 포함되지 않는 경우가 많은 효과는 제외했다.

Adobe XD의 경우

Adobe XD에 대한 짧은 설명 Figma가 아직 대세를 차지하기 이전 Sketch가 맥 환경에서 득세하는 동안, Adobe의 웹 디자인을 위한 툴이라고는 구 웹 환경에 맞춘 슬라이스 기능 정도만 제공하는 포토샵과, 억지로 기능만 추가해 둔 편집 디자인용 벡터 기반인 인디자인을 쓸 수밖에 없었다.
이에 Adobe가 비교적(?) 야심차게 내놓은 웹 디자인 도구로, 신생 프로그램답게 UWP 기반이라 고질적인 32비트 레거시 문제에서 자유롭고 다른 Adobe Creative Cloud 앱과 연계가 편리하다는 점이 장점이다.
다른 Adobe Creative Cloud 앱을 많이 사용하거나 윈도우 어플리케이션 환경이 익숙하다면 추천되는 GUI 디자인 도구.

서론이 너무 길었다. 재직중에 제일 많이 사용했던 Adobe XD부터 정리해보자.
아래 아이콘이 XD에서 위 스타일가이드를 따라 작업한 뒤 기본 설정으로 내보내기한 SVG 파일이다.

SVG 코드
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="36" height="36" viewBox="0 0 36 36">
<defs>
  <filter id="사각형_2" x="9.5" y="6.5" width="19" height="25" filterUnits="userSpaceOnUse">
    <feOffset dy="1" input="SourceAlpha"/>
    <feGaussianBlur stdDeviation="0.5" result="blur"/>
    <feFlood flood-opacity="0.251"/>
    <feComposite operator="in" in2="blur"/>
    <feComposite in="SourceGraphic"/>
  </filter>
  <linearGradient id="linear-gradient" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
    <stop offset="0" stop-color="#35f39d"/>
    <stop offset="1" stop-color="#169df6"/>
  </linearGradient>
</defs>
<g id="XD 기본 설정" transform="translate(-930 -528)">
  <rect id="사각형_1" data-name="사각형 1" width="36" height="36" transform="translate(930 528)" fill="none"/>
  <g id="그룹_1" data-name="그룹 1">
    <g transform="matrix(1, 0, 0, 1, 930, 528)" filter="url(#사각형_2)">
      <rect id="사각형_2-2" data-name="사각형 2" width="16" height="22" rx="2" transform="translate(11 7)" fill="#e8eded"/>
    </g>
    <path id="패스_1" data-name="패스 1" d="M13,6a3,3,0,0,0-3,3v5a1,1,0,0,0,2,0V9a1,1,0,0,1,1-1H25a1,1,0,0,1,1,1V27a1,1,0,0,1-1,1H19a1,1,0,0,0,0,2h6a3,3,0,0,0,3-3V9a3,3,0,0,0-3-3Z" transform="translate(930 528)" fill="url(#linear-gradient)"/>
    <rect id="사각형_3" data-name="사각형 3" width="5" height="2" rx="1" transform="translate(945 538)" fill="#35f39d"/>
    <rect id="사각형_4" data-name="사각형 4" width="2" height="2" rx="1" transform="translate(951 538)" fill="#35f39d"/>
  </g>
  <rect id="사각형_5" data-name="사각형 5" width="8" height="11" rx="2" transform="translate(937 546)" fill="#fff" stroke="#169df6" stroke-width="2"/>
  <circle id="타원_2" data-name="타원 2" cx="1" cy="1" r="1" transform="translate(940 553)" fill="#169df6"/>
  <g id="타원_1" data-name="타원 1" transform="translate(948 553)" fill="none" stroke="#169df6" stroke-width="1">
    <circle cx="1" cy="1" r="1" stroke="none"/>
    <circle cx="1" cy="1" r="0.5" fill="none"/>
  </g>
</g>
</svg>

특징적으로는 호환을 위한 xmlns:xlink가 한번 더 선언되어 있고, 필터 상관없이 모든 도형에 id와 data-name이 부여되어 있다는 점이다.
drop shadow는 도형 이름을 id로 하는 filter로, gradient는 형식에 따라 id를 부여하여 적용되어 있다.
효과를 적용한 상세한 방식은 이후 다른 툴과 비교하면서 정리하도록 하겠다.

transform : Adobe XD로 내보낸 SVG 에셋의 고질적인 문제점

XD는 도형의 위치를 기억하기 위해 transfrom="translate(x y)"형식의 인라인 특성을 사용한다.
문제는 이를 내부적으로만 사용하는 것이 아닌 내보내기로 생성된 SVG에도 보정 없이 그대로 포함되어 있다는 점이다.
그로 인해 단일 SVG 파일로 내보내기 위해 그룹으로 묶어둔 경우 경우, 전체가 그룹 태그로 묶여 translate 값이 지정되어 남는다.
Adobe 포럼에서 XD SVG translate 로 검색해보면 수많은 수정 요구가 있지만 아직도 고쳐지지 않은 문제점이다.

도형 영역에서 이렇게나 많은 불필요한 코드가 포함된다.
"XD 기본 설정" 그룹에 적용된 translate가 최상위 절대좌표이며,
모든 하위 도형 및 그룹이 모두 상대 좌표로 설정되어 있다.

캔버스 절대 좌표를 그대로 사용할 일도 없는 그래픽 에셋에는 불필요한 코드인데다,
하위 그룹이 있는 경우 translate값이 중첩되어 있어 단순히 속성을 지우는 것으로는 해결할 수 없어 코드 편집을 통한 최적화를 어렵게 만든다.
가장 편리한 해결방법은 Inkscape를 통해 최상위부터 그룹을 해제해 transform 값을 보정하여 삭제하는 것 뿐이다.

추가 : 그림자가 적용된 경우 transfrom="matrix(1, 0, 0, 1, x, y)"가 적용된다.
최근 버전에 추가된 기능인지 이 글을 작성하는 도중에 발견했는데, 기존에 이슈가 있었던 XD에서 내보낸 SVG의 filter 효과 레이어 순서 문제를 해결하기 위한 궁여지책으로 보인다.
다행히 Inkscape는 아예 matrix를 translate로 해석한다.

에셋 내보내기 세부 설정

다른 디자인 툴과 비교되는 Adobe XD의 가장 큰 특징은 내보내기 창에서 SVG 포맷의 세부 옵션을 수정할 수 있다는 것이다.
각 세부 옵션 적용시 달라지는 부분을 분석해보자.

코드 블록마다 기존 SVG 마크업과 달라진 부분, 장/단점을 주석으로 정리해두었다.

스타일링

fill, stroke, filter 등 인라인 특성으로 적용되는 스타일에 관련된 옵션을 지정하는 방식을 선택한다.

프레젠테이션 속성
기본값, 각 도형의 태그마다 인라인 특성으로 추가된다.

내부 CSS
각 도형의 태그마다 cls-#형식의 클래스가 추가되고, defs 태그 내에 style 태그를 통해 각 클래스의 스타일을 지정한다.
내부 CSS의 경우 스타일시트의 선언 블록 내의 값이 동일한 경우 자동으로 동일한 클래스가 지정된다.

상태별로 다른 색상이 지정되는 아이콘 에셋의 경우 유용할 수 있지만,
선언 블록 내의 모든 값이 동일해야 하는 조건상 디자이너의 의도와 다르게 동작하는 경우가 있으므로 확인이 필요하다.

내부 CSS 옵션을 적용한 SVG 코드
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="36" height="36" viewBox="0 0 36 36">
<defs>
  <style>
    .cls-1, .cls-7 { /* 다중 선언된 fill*/
      fill: none;
    }

    .cls-2 {
      fill: #e8eded;
    }

    .cls-3 {
      fill: url(#linear-gradient);
    }

    .cls-4 {
      fill: #35f39d;
    }

    .cls-5 {
      fill: #fff;
      stroke-width: 2px;
    }

    .cls-5, .cls-7 { /* 다중 선언된 stroke*/
      stroke: #169df6;
    }

    .cls-6 {
      fill: #169df6;
    }

    .cls-8 {
      stroke: none;
    }

    .cls-9 {
      filter: url(#사각형_2);
    }
  </style>
  <filter id="사각형_2" x="9.5" y="6.5" width="19" height="25" filterUnits="userSpaceOnUse">
    <feOffset dy="1" input="SourceAlpha"/>
    <feGaussianBlur stdDeviation="0.5" result="blur"/>
    <feFlood flood-opacity="0.251"/>
    <feComposite operator="in" in2="blur"/>
    <feComposite in="SourceGraphic"/>
  </filter>
  <linearGradient id="linear-gradient" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
    <stop offset="0" stop-color="#35f39d"/>
    <stop offset="1" stop-color="#169df6"/>
  </linearGradient>
</defs>
<g id="내부_CSS" data-name="내부 CSS" transform="translate(-930 -528)">
  <rect id="사각형_1" data-name="사각형 1" class="cls-1" width="36" height="36" transform="translate(930 528)"/>
  <g id="그룹_1" data-name="그룹 1">
    <g class="cls-9" transform="matrix(1, 0, 0, 1, 930, 528)">
      <rect id="사각형_2-2" data-name="사각형 2" class="cls-2" width="16" height="22" rx="2" transform="translate(11 7)"/>
    </g>
    <path id="패스_1" data-name="패스 1" class="cls-3" d="M13,6a3,3,0,0,0-3,3v5a1,1,0,0,0,2,0V9a1,1,0,0,1,1-1H25a1,1,0,0,1,1,1V27a1,1,0,0,1-1,1H19a1,1,0,0,0,0,2h6a3,3,0,0,0,3-3V9a3,3,0,0,0-3-3Z" transform="translate(930 528)"/>
    <rect id="사각형_3" data-name="사각형 3" class="cls-4" width="5" height="2" rx="1" transform="translate(945 538)"/>
    <rect id="사각형_4" data-name="사각형 4" class="cls-4" width="2" height="2" rx="1" transform="translate(951 538)"/>
    <!-- 동일한 클래스가 적용된 사각형 3,4 -->
  </g>
  <rect id="사각형_5" data-name="사각형 5" class="cls-5" width="8" height="11" rx="2" transform="translate(937 546)"/>
  <circle id="타원_2" data-name="타원 2" class="cls-6" cx="1" cy="1" r="1" transform="translate(940 553)"/>
  <g id="타원_1" data-name="타원 1" class="cls-7" transform="translate(948 553)">
    <circle class="cls-8" cx="1" cy="1" r="1"/>
    <circle class="cls-1" cx="1" cy="1" r="0.5"/>
    <!-- 디자이너의 의도와 다르게 사각형_1과 동일한 클래스가 적용됨  -->
  </g>
</g>
</svg>

이미지 저장

래스터 이미지를 이미지 태그의 href 속성을 통해 포함시키는 방법을 선택한다.

임베드
이미지를 base64로 인코딩해 포함시킨다. 당연히 이미지 크기에 비례하여 base64 코드가 상당히 커질 수 있다.

링크
이미지의 로컬 경로를 포함시킨다. 가져온 이미지인 경우 원본 이미지 파일의 절대 경로가 표기되고, 원본 파일을 찾을 수 없거나 XD 내에서 생성한 이미지인 경우(ex:포토샵 임시파일을 통해 편집한 경우) 내보낸 위치에 이미지 파일을 같이 내보내고 상대 경로를 표기한다.

단순히 image 태그를 통해 추가되는 형식이므로, 코드 블럭은 생략한다.

파일 크기 - 최적화(축소)

기본적으로 prettify되어있던 형식의 들여쓰기, 줄바꿈이 모두 삭제되고,
각 도형의 태그마다 지정되어 있던 id와 data-name이 삭제된다.
필터를 위한 id나 내부 CSS를 위한 클래스가 지정되는 경우 소문자 알파벳으로 단순화된다.

알아보기 쉽도록 코드 블럭에는 줄바꿈과 들여쓰기를 적용했다.

파일 크기 - 최적화(축소) 옵션을 적용한 SVG 코드
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="36" height="36" viewBox="0 0 36 36">
  <defs>
      <filter id="a" x="9.5" y="6.5" width="19" height="25" filterUnits="userSpaceOnUse">
          <feOffset dy="1" input="SourceAlpha" />
          <feGaussianBlur stdDeviation="0.5" result="b" />
          <feFlood flood-opacity="0.251" />
          <feComposite operator="in" in2="b" />
          <feComposite in="SourceGraphic" />
      </filter>
      <!-- filter #a의 result에 b가 사용되어, gradient fill의 id가 c로 지정된다. -->
      <linearGradient id="c" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
          <stop offset="0" stop-color="#35f39d" />
          <stop offset="1" stop-color="#169df6" />
      </linearGradient>
  </defs>
	<!-- 여전히 남아있는 translate -->
  <g transform="translate(-930 -528)">
      <rect width="36" height="36" transform="translate(930 528)" fill="none" />
      <g transform="matrix(1, 0, 0, 1, 930, 528)" filter="url(#a)">
          <rect width="16" height="22" rx="2" transform="translate(11 7)" fill="#e8eded" />
      </g>
      <path d="M13,6a3,3,0,0,0-3,3v5a1,1,0,0,0,2,0V9a1,1,0,0,1,1-1H25a1,1,0,0,1,1,1V27a1,1,0,0,1-1,1H19a1,1,0,0,0,0,2h6a3,3,0,0,0,3-3V9a3,3,0,0,0-3-3Z" transform="translate(930 528)" fill="url(#c)" />
      <rect width="5" height="2" rx="1" transform="translate(945 538)" fill="#35f39d" />
      <rect width="2" height="2" rx="1" transform="translate(951 538)" fill="#35f39d" />
      <rect width="8" height="11" rx="2" transform="translate(937 546)" fill="#fff" stroke="#169df6" stroke-width="2" />
      <circle cx="1" cy="1" r="1" transform="translate(940 553)" fill="#169df6" />
      <g transform="translate(948 553)" fill="none" stroke="#169df6" stroke-width="1">
      	<!-- 여전히 남아있는 fill도 stroke도 none인 도형 -->
          <circle cx="1" cy="1" r="1" stroke="none" />
          <circle cx="1" cy="1" r="0.5" fill="none" />
      </g>
  </g>
</svg>

패스 옵션 - 윤곽선

XD는 기본적으로 도형에서 center가 아닌(패스의 안or바깥쪽) stroke를 사용한 경우
그 도형의 이름을 id로 하는 그룹을 생성한 뒤 fill, stroke, stroke-width 속성을 추가하고,
fill과 stroke를 담당하는 2개의 도형을 각각 stroke="none"과 fill="none"을 붙여 그룹에 포함시킨다.
여기서 stroke를 담당하는 도형의 위치, 크기를 stroke의 안or바깥쪽 설정에 맞춰 조정한다.
이는 SVG의 stroke-alignment 속성이 SVG 2.0에서 추가되어 1.1 버전을 표준으로 하는 웹에서 일반적으로 적용되지 않기 때문으로 보인다.

기본 옵션 SVG 코드에서 stroke가 다르게 적용된 부분
  ...
  <rect id="사각형_5" data-name="사각형 5" width="8" height="11" rx="2" transform="translate(937 546)" fill="#fff" stroke="#169df6" stroke-width="2"/>
  <!-- stroke가 center이므로 fill과 stroke가 사각형_5에 적용되어 있다. -->
  ...
  <g id="타원_1" data-name="타원 1" transform="translate(948 553)" fill="none" stroke="#169df6" stroke-width="1">
  <!-- 그룹에 지정되어 있는 fill, stroke, stroke-width -->
    <circle cx="1" cy="1" r="1" stroke="none"/>
    <!-- fill과 stroke가 모두 없는 원래 위치를 표기하기 위한 도형 -->
    <circle cx="1" cy="1" r="0.5" fill="none"/>
    <!-- 그룹에서 stroke를 상속받는 도형, r이 stroke-width의 반절만큼 조정되어 있다. -->
  </g>
  ...

특이한 점은 fill을 사용하지 않은 경우에도 원래 도형의 형태를 fill="none" 속성을 붙여 포함해두는데,
보통 디자이너가 표시되지 않는 fill 영역을 사용하려는 경우 tranparent 등의 속성을 적용하기 때문에 불필요한 코드인 경우가 많다.

패스 옵션 - 윤곽선 옵션은 모든 stroke를 윤곽선 path로 변환한다.
stroke만 적용된 도형은 stroke가 칠해지는 영역의 path로 변경되고, 기존 stroke의 색상을 fill로 적용한다.
fill과 stroke가 적용된 경우 기존 도형에 fill만 적용하고 해당 도형 id에 "기존 도형 ID_-_윤곽선"의 id를 적용한 새로운 path를 추가한다.
주의 : path보다는 도형 태그가 훨씬 압축된 형태이므로, 이 옵션은 압축을 위한 옵션이 아니다.

패스 옵션 - 윤곽선 옵션을 적용한 SVG 코드
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="36" height="36" viewBox="0 0 36 36">
<defs>
  <filter id="사각형_2" x="9.5" y="6.5" width="19" height="25" filterUnits="userSpaceOnUse">
    <feOffset dy="1" input="SourceAlpha"/>
    <feGaussianBlur stdDeviation="0.5" result="blur"/>
    <feFlood flood-opacity="0.251"/>
    <feComposite operator="in" in2="blur"/>
    <feComposite in="SourceGraphic"/>
  </filter>
  <linearGradient id="linear-gradient" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
    <stop offset="0" stop-color="#35f39d"/>
    <stop offset="1" stop-color="#169df6"/>
  </linearGradient>
</defs>
<g id="윤곽선" transform="translate(-930 -528)">
  <rect id="사각형_1" data-name="사각형 1" width="36" height="36" transform="translate(930 528)" fill="none"/>
  <g id="그룹_1" data-name="그룹 1">
    <g transform="matrix(1, 0, 0, 1, 930, 528)" filter="url(#사각형_2)">
      <rect id="사각형_2-2" data-name="사각형 2" width="16" height="22" rx="2" transform="translate(11 7)" fill="#e8eded"/>
    </g>
    <path id="패스_1" data-name="패스 1" d="M13,6a3,3,0,0,0-3,3v5a1,1,0,0,0,2,0V9a1,1,0,0,1,1-1H25a1,1,0,0,1,1,1V27a1,1,0,0,1-1,1H19a1,1,0,0,0,0,2h6a3,3,0,0,0,3-3V9a3,3,0,0,0-3-3Z" transform="translate(930 528)" fill="url(#linear-gradient)"/>
    <rect id="사각형_3" data-name="사각형 3" width="5" height="2" rx="1" transform="translate(945 538)" fill="#35f39d"/>
    <rect id="사각형_4" data-name="사각형 4" width="2" height="2" rx="1" transform="translate(951 538)" fill="#35f39d"/>
  </g>
  <rect id="사각형_5" data-name="사각형 5" width="8" height="11" rx="2" transform="translate(937 546)" fill="#fff"/>
  <!-- 사각형_5의 fill과 윤곽선이 rect와 stroke로 별도의 도형으로 분리되어 있다. -->
  <path id="사각형_5_-_윤곽선" data-name="사각형 5 - 윤곽선" d="M2-1H6A3,3,0,0,1,9,2V9a3,3,0,0,1-3,3H2A3,3,0,0,1-1,9V2A3,3,0,0,1,2-1ZM6,10A1,1,0,0,0,7,9V2A1,1,0,0,0,6,1H2A1,1,0,0,0,1,2V9a1,1,0,0,0,1,1Z" transform="translate(937 546)" fill="#169df6"/>
  <circle id="타원_2" data-name="타원 2" cx="1" cy="1" r="1" transform="translate(940 553)" fill="#169df6"/>
  <!-- 타원_1이 path로 변환되고, stroke가 아닌 fill로 색상을 적용한다. -->
  <path id="타원_1" data-name="타원 1" d="M1,0A1,1,0,1,1,0,1,1,1,0,0,1,1,0Z" transform="translate(948 553)" fill="#169df6"/>
</g>
</svg>

마무리

가장 많이 사용했던 디자인 도구였고, 이후 다른 도구와 비교하기 위해 상세한 부분까지 분석하느라 내용이 많이 길어졌다.
이유를 알 수 없는 부분과 호환성을 위해 고려된 부분까지 종합적으로 분포되어 있어 생각보다 SVG에 대해 다양하게 분석해볼 수 있었다.
다음 글에서는 XD보다 대중적인 Figma, Sketch 등에서 나타나는 차이를 중점적으로 정리해보면서, SVG에서 보통 깊게 파고들지 않는 부분인 filter를 분석해보려고 한다.

0개의 댓글