개발 기술/개발 이야기

아이콘 컴포넌트 렌더링 방식, 정말 좋을까? (with. 빌드 시간, FID, TBT 등 비교)

by GicoMomg 2024. 8. 11.

1. 들어가며…

프론트엔드에서 Vue나 React와 같은 Virtual DOM 프레임워크의 사용이 활발하다.
그리고 이러한 프레임워크들은 페이지마다 반복 작성했던 코드를 재사용 컴포넌트로 분리할 수 있다.
그래서, 아토믹 단위로 요소를 관리하기 쉬워졌다.

재사용 가능한 요소인 헤더, 네비게이션, 아코디언, 그리고 비즈니스 로직에서 반복되는 UI 등을 컴포넌트로 분리할 수 있다.
물론, SVG 아이콘도 별도의 컴포넌트로 관리할 수 있다.

 

❓ 그런데, 과연 SVG 아이콘 파일을 컴포넌트로 만들어야 할 필요가 있을까?

소규모 프로젝트에서는 그 필요성을 느끼지 못할 수도 있다. 하지만 실제 서비스되는 대규모 프로젝트에서는 아이콘을 컴포넌트화할 필요가 있다.
그 이유는 크게 두 가지인데…
첫째, 색상만 다른 아이콘 파일이 여러 개 생성되는 문제를 해결해 중복 아이콘을 제거하기 위해서,
둘째, 중복된 아이콘을 제거함으로써 외부에서 아이콘의 stroke, fill 색상, 너비, 높이 등을 쉽게 조정할 수 있게 하기 위함이다.

 

🤔 그런데 svg 아이콘을 vue 컴포넌트로 관리하면, 성능에 영향을 주지 않을까?

그래서… 이번 시간에는 svg 파일을 이미지로 로드하는 방법, svg 아이콘 컴포넌트 방법,
특정 경로의 svg를 컴포넌트로 로드하는 방법, 총 3가지 방식의 성능을 비교해보았다!




2. 3가지 아이콘 관리 방식과 성능 측정

📍 아이콘 선언 방식 별로 성능을 비교하고자, 총 300개의 아이콘을 로드해서 테스트했다.
빌드 시간과 페이지 로드 성능을 테스트하고 싶다면, 이 레포의 코드 기반으로 테스트 가능하다 :)

1) svg 파일을 이미지로 로드하는 경우

(1) 설명

  • 첫 번째는 제일 기본적인 방식으로, img 태그에 svg 파일을 로드하는 형태이다.
  • 이 방법의 장점은 svg 아이콘을 추가할 때 svg을 수정할 필요가 없다는 점인데,
  • 단점으로는 같은 아이콘 형태이지만 색상만 다른 경우에도 svg 아이콘 파일을 추가해줘야 한다.
<template>
  <img src="/svg/icon1.svg" alt="icon" />
</template>

 

  • 그럼 이 방식대로 아이콘을 로드할 경우, 빌드 & 브라우저 성능은 어떨까?
  • 빌드 시간 & 빌드파일 크기, FCP(First Content Paint), TBT(Total Block Time), 요청 응답 시간 등을 확인해보자!

 

(2) 빌드 시간 & 파일 크기 체크

  • Vite & Vue 환경에서 300개의 아이콘을 로드해봤는데, 서버 실행 시간은 132ms이었다.

 

  • 빌드했을 때 시간은 725ms가 소요되었고, dist 파일 크기는 552kb였다.

 

 

(3) FCP, TBT 체크

  • 그 다음으로 Chorme의 [lighthouse]탭에서 FCP, TBT을 확인해보았다.
  • FCP(First Contentful Paint)는 유저가 페이지를 처음 진입했을 때, 페이지 콘텐츠가 렌더링되는 데 걸리는 시간이다.
  • 이 시간은 1.8초 이하일 때 사용자에게 좋은 경험을 준다.

 

  • TBT(Time Blocking Time)은 FCP 이후, 사용자의 클릭이나 입력과 같은 상호작용에 즉시 응답하지 않은 시간이다.
  • 쉽게 말해, 페이지 로드 중에 자바스크립트 실행이나 다른 작업으로 인해 메인 스레드가 차단되는 시간이다.
  • TBT가 길어질수록 사용자는 동작에 대한 상호작용을 즉시 받을수 없는데, TBT가 길어질수록 사용자 경험은 나빠진다.
  • TBT의 경우, 모바일 환경 기준으로 200밀리초 미만이어야 한다.

 

그럼 svg 파일을 로드하는 경우, 두 측정 항목은 얼마가 나올까?

  • 데스크탑 기준 FCP는 0.6초로 양호한 편이었는데, 다만 모바일 기준상 2.8초로 개선이 필요했다.
  • TBT는 모바일 기준으로 20밀리초로 성능상 양호했다.
측정 기기 FCP TBT
데스크탑 0.6초 0초
모바일 2.8초 20밀리초

 

(4) 서버 응답 시간 체크

  • 마지막으로 크롬의 [네트워크] 탭에서 서버 응답 대기 시간, 콘텐츠 다운로드 시간을 확인했다.

 

  • 서버 응답 대기 시간 (Server Response Time, TTFB)는 서버가 요청을 받아 첫 번째 바이트를 클라이언트에 전송하기까지 걸리는 시간이다. TTFB가 길어지면 사용자가 페이지를 처음으로 볼 수 있는 시간이 지연돼 사용자 경험에 영향을 준다.
  • 콘텐츠 다운로드 시간 (Content Download Time)는 서버에서 클라이언트로 데이터를 전송하는 데 걸리는 시간이다. 이 값이 크면 리소스(이미지, 스크립트, 스타일시트) 로드 시간이 늘어나, 전체 페이지 렌더링 속도가 느려진다.
  • 하나의 svg 아이콘을 로드할 때, 2가지 측정값의 최소 최대 값은 아래와 같다.
  서버 응답 대기 시간 콘텐츠 다운로드 시간
최소 0.25밀리초 0.076밀리초
최대 5.03밀리초 2.14밀리초

 

  • 300개의 아이콘을 로드했을 때, DOMContentLoaded는 35밀리초, 로드 시간은 471밀리초가 소요됐다.



2) svg 아이콘 컴포넌트를 사용하는 경우

(1) 설명

  • 두 번째는, svg 파일을 vue 컴포넌트로 변환하는 방법이다.
  • svg 파일을 vue 컴포넌트로 만드는 방식은 간단한데, vue파일의 template에 svg 코드를 넣고, width, height, color를 외부에서 컨트롤하도록 선언하면 된다.
  • 이 방법은 매번 아이콘 컴포넌트를 만들어야 하는 번거로움이 있지만,
  • 같은 형태이지만 색상이 다른 아이콘을 하나의 컴포넌트로 컨트롤할 수 있다.
// ArrowDownIcon.vue

<template>
  <svg
      :width="props.width"
      :height="props.height"
      style="display: block"
      viewBox="0 0 24 24"
    >
    ...
  </svg>
</template>

<script setup>
const props = defineProps({
  width: {
    type: String,
    default: "800px",
  },
  height: {
    type: String,
    default: "800px",
  },
  color: {
    type: String,
    default: "currentColor"
  }
});
</script>
<template>
  <ArrowDownIcon width="200px" height="150px" color="red" />
  <ArrowDownIcon width="300px" height="350px" color="blue" />
</template>

 

(2) 빌드 시간 & 파일 크기

  • 첫 번째 방법과 마찬가지로 Vite & Vue 환경에서 300개의 아이콘을 로드해봤는데, 서버 실행 시간은 164ms로, 앞선 방법보다 약 30ms 더 소요됐다.

 

  • Vite로 빌드했을 때 시간은 9.51s가 소요되었고, dist 파일 크기는 무려… 15.8mb였다;;

 

(3) FCP, TBT 체크

  • FCP, TBT도 성능이 좋지 않았는데… 데스크탑 기준 FCP는 2.9초로, 모바일 기준 11.9초 였다.
  • 그리고 TBT는 모바일 기준으로 230밀리초로 권장하는 200밀리초보다 30밀리초를 초과했다.
측정 기기 FCP TBT
데스크탑 2.9초 40초
모바일 11.9초 230밀리초

 

(4) 서버 응답 시간

  • 평균적으로 하나의 아이콘을 로드할 때, 응답 대기, 다운로드 시간의 최소 최대 값은 아래와 같다.
  서버 응답 대기 시간 콘텐츠 다운로드 시간
최소 85마이크로초 91마이크로초
최대 0.87밀리초 1.17밀리초

 

  • 300개의 아이콘을 로드했을 때, DOMContentLoaded는 38밀리초, 로드 시간은 135밀리초가 소요됐다.



3) 특정 경로의 svg를 컴포넌트에서 로드하는 경우

(1) 설명

  • 마지막 방법은, 특정 위치에 있는 svg 파일을 컴포넌트에서 로드하는 방식이다. (예시 보기)
  • 사용처에서 아이콘 컴포넌트에 아이콘 이름과 너비, 높이, 색상을 넘기면, fetch API로 svg 아이콘을 불러와서 svg를 재구성한다.
  • 두 번째 방법처럼, 아이콘 별로 컴포넌트를 만들지 않지만 너비, 높이, 색상은 외부에서 props로 쉽게 컨트롤 할 수 있다.
  • 단, 이 방식을 쓸 때는 이미 로드된 아이콘을 재로드하지 않기 위해 캐시 스토어가 필요하다. (ex. useSvgCacheStore)
<template>
  <div>
    <Icon name="sample" width="50" height="50" color="blue" hover-color="gray" />
    <Icon name="sample" width="50" height="50" color="red" hover-color="gray" />
  </div>
</template>

<script setup lang="ts">
import { provide } from 'vue';
import Icon from "./components/icons";

import { useSvgCacheStore } from "./composables/useSvgCacheStore";

const svgCacheStore = useSvgCacheStore();
provide("svgCacheStore", svgCacheStore);
</script>

 



(2) 빌드 시간 & 파일 크기

📍 svg 파일을 커스텀하지 않아도 사용처에서 너비, 높이, 색상을 바꿀 수 있다니…!
너무 유용한데? 그럼, 성능도 좋을까? 빌드 시간과 빌드 파일 크기를 측정해보았다.

  • 우선 서버 실행 시간은 168ms로, 두 번째 방법보다 약 3ms 더 소요됐다.

 

  • 빌드 시간은 889ms가 소요되었고,
  • dist 파일 크기는 555kb로 svg 파일을 이미지로 로드하는 방식과 유사했다.

 

(3) FCP, TBT 체크

  • FCP는 svg 파일을 이미지로 로드하는 방식과 유사했는데, 데스크탑 기준 0.6초로, 모바일 기준 2.9초 였다.
  • 그리고 TBT는 모바일 기준으로 130밀리초로 양호했다.
측정 기기 FCP TBT
데스크탑 0.6초 10밀리초
모바일 2.9초 130밀리초

 

(4) 서버 응답 시간

  • 하나의 svg 아이콘을 로드할 때, 응답 대기, 다운로드 시간의 최소 최대 값은 아래와 같다.
  서버 응답 대기 시간 콘텐츠 다운로드 시간
최소 0.20밀리초 95마이크로초
최대 2밀리초 1.57밀리초

 

  • 300개의 아이콘을 로드했을 때, DOMContentLoaded는 36밀리초, 로드 시간은 46밀리초가 소요됐다.



3. 아이콘 관리 방식 간의 성능 비교

📍 앞서 아이콘을 선언하는 3가지 방식을 알아보았다. 그럼 이 3가지 방식 중에 어떤 방식이 성능이 우수할까?
측정 결과를 표로 비교해보았다.

1) 비교해보기

(1) 빌드 시간 & 파일 크기

  (A) svg 파일을 이미지로
로드하는 경우
(B) svg 아이콘 컴포넌트를
사용하는 경우
(C) 특정 경로의 svg를
컴포넌트에서 로드하는 경우
빌드 시간 725ms 9.51s 889ms
빌드 파일 크기 552kb 15.8mb 555kb
  • 빌드 시간:
    • (A) 방식은 725ms로 빌드 시간이 제일 빨랐다.
    • (C) 방식은 889ms로 두 번째로 빠릅니다.
    • (B) 방식은 9.51s로 상대적으로 매우 느렸다.
  • 빌드 파일 크기:
    • (A) 방식은 552kb로 가장 작은 파일 크기를 가진다.
    • (C) 방식은 555kb로 (A)와 거의 동일한 크기였다.
    • (B) 방식은 15.8mb로 매우 큰 파일 크기를 가졌다!

 

(2) FCP, TBT

  (A) svg 파일을 이미지로
로드하는 경우
(B) svg 아이콘 컴포넌트를
사용하는 경우
(C) 특정 경로의 svg를
컴포넌트에서 로드하는 경우
FCP 데스크탑(0.6초), 모바일(2.8초) 데스크탑(2.9초), 모바일(11.9초) 데스크탑(0.6초), 모바일(2.9초)
TBT 모바일(20밀리초) 모바일(230밀리초) 모바일(130밀리초)
  • FCP (First Contentful Paint):
    • 데스크탑:
      • (A)와 (C) 방식은 각각 0.6초로 가장 빨랐다.
      • (B) 방식은 2.9초로 가장 느렸다.
    • 모바일:
      • (A) 방식은 2.8초로 가장 빨랐다.
      • (C) 방식은 2.9초로, (A)와 비슷한 성능을 보였다.
      • (B) 방식은 11.9초로 매우 느렸다.
  • TBT (Total Blocking Time):
    • (A) 방식은 20밀리초로 가장 적은 TBT를 보였다.
    • (C) 방식은 130밀리초로 그 다음을 차지했다.
    • (B) 방식은 230밀리초로 가장 많은 TBT를 기록했다.

 

(3) 서버 응답 시간

  (A) svg 파일을 이미지로
로드하는 경우
(B) svg 아이콘 컴포넌트를
사용하는 경우
(C) 특정 경로의 svg를
컴포넌트에서로드하는 경우
아이콘별
서버 응답 대기 시간
5.03밀리초 ~ 0.25밀리초 0.87밀리초 ~ 85마이크로초 2밀리초 ~ 0.20밀리초
아이콘별
콘텐츠 다운로드 시간
2.14밀리초 ~ 0.076밀리초 1.17밀리초 ~ 91마이크로초 1.57밀리초 ~ 95마이크로초
총 DOMContentLoaded 시간 35밀리초 38밀리초 36밀리초
총 로드시간 471밀리초 135밀리초 46밀리초
  • 아이콘별 서버 응답 대기 시간:
    • (B) 방식이 0.87밀리초 ~ 85마이크로초로 가장 빨랐다.
    • (C) 방식은 2밀리초 ~ 0.20밀리초로 두 번째로 빨랐다.
    • (A) 방식은 5.03밀리초 ~ 0.25밀리초로 가장 느렸다.
  • 아이콘별 콘텐츠 다운로드 시간:
    • (B) 방식이 1.17밀리초 ~ 91마이크로초로 가장 빨랐다.
    • (C) 방식은 1.57밀리초 ~ 95마이크로초로 두 번째로 빨랐다.
    • (A) 방식은 2.14밀리초 ~ 0.076밀리초로 가장 느렸다.
  • 총 DOMContentLoaded 시간:
    • 세 가지 방식 모두 큰 차이는 없지만, (A) 방식이 35밀리초로 가장 빨랐다.
    • (C) 방식은 36밀리초, (B) 방식은 38밀리초로 비교적 유사했다.
  • 총 로드시간:
    • (C) 방식이 46밀리초로 가장 짧았다.
    • (B) 방식은 135밀리초로 중간 수준이었다.
    • (A) 방식은 471밀리초로 가장 길었다.



2) 결론

  • 빌드 시간, 서버 응답 시간은 (B) 아이콘 컴포넌트 방식이 제일 성능이 좋지 않았다.
  • 그에 비해 (C) 특정 경로의 svg를 컴포넌트에서 로드하는 방식은 전반적인 성능이 좋았다.
  (A) svg 파일을 이미지로
로드하는 경우
(B) svg 아이콘 컴포넌트를
사용하는 경우
(C) 특정 경로의 svg를
컴포넌트에서 로드하는 경우
빌드 시간
& 빌드 파일 크기
빌드 시간과 파일 크기가 가장 유리하다. 빌드 시간이 길고 파일 크기가 컸다. 전반적으로 균형 잡힌 성능을 보인다.
FCP
& TBT
FCP와 TBT도 우수한 성능을 보인다. FCP와 TBT 성능이 가장 저조했다. FCP, TBT 성능이 양호했다.
서버 응답 시간
& 로드 시간
서버 응답 및 로드 시간 측면에서는 다소 불리했다. 서버 응답 시간과 콘텐츠 다운로드 시간이 가장 빨랐다. 서버 응답 시간 및 로드 시간이 좋았다.

 

  • 만약 서비스에서 형태는 같지만 색상만 다른 아이콘이 많은 경우,
  • 각 svg 아이콘을 컴포넌트화 하기 보다는 특정 경로의 svg를 컴포넌트에서 로드하는 방식을 쓰면 어떨까?




반응형

댓글