04-third-party 라이브러리 최적화

업데이트: Link

third-party 라이브러리 최적화

Part 4 of 6 in our Vue.js Performance series.
Written by Filip Rakowski

오랜만에 Vue Performance 시리즈 편을 공개합니다. 이미 배운 모든 것을 프로젝트에 적용하고 더 빠르게 만들 수 있는 기회가 되었기를 바랍니다!

이전 기사를 읽었다면 지연 로딩이 어떻게 작동하는지, 경로 및 구성 요소를 코드 분할하는 방법비동기 로딩을 우아하게 관리하는 방법 을 이미 완벽하게 알고 있지만 번들 크기에 가장 큰 영향을 미치는 측면(타사 라이브러리)은 아직 다루지 않았습니다.

이 기사는 그것에 관한 것입니다!

예산 정의

프론트엔드 애플리케이션을 최적화하기 전에 스스로에게 물어야 하는 첫 번째 질문은 “정말 이 작업을 수행해야 합니까?” 입니다.

성능에 집착하기 쉽지만 최적화에 지나치게 공격적인 것은 결코 좋은 생각이 아닙니다. 성능 최적화는 절충점에 관한 것임을 이해하는 것이 매우 중요합니다.

더 빠른 웹사이트가 항상 더 나은 웹사이트는 아닙니다. 더 나은 성능 결과를 얻으려면 항상 무언가를 희생해야 합니다. 그것은 때로는 기능이고 때로는 우리가 가장 좋아하는 라이브러리를 변경해야 합니다.

성능 최적화는 그 자체로 목표가 아닙니다. 속도와 상식 간의 건전한 관계를 유지하려면 달성하고자 하는 측정 가능한 목표를 정의해야 합니다.
그들에게 도달한다는 것은 우리의 애플리케이션이 충분히 빠르며 더 이상 최적화할 필요가 없다는 것을 의미해야 합니다. 이러한 목표를 일반적으로 성과 예산(performance budget)이라고 합니다.

그렇다면 완벽한 성능 예산이란 무엇입니까? 이 질문에 대한 대답은 우리의 목표 청중과 밀접한 관련이 있습니다. 시골 거주자나 인터넷 연결이 느린 국가를 대상으로 하는 애플리케이션의 성능 예산은 주요 유럽 또는 미국 도시를 대상으로 하는 것보다 훨씬 낮습니다. 중요한 자산의 경우 절대 170kB를 초과하지 않는 것이 일반적으로 인정되는 경험 법칙입니다. 중요한 자산은 사용자가 처음 방문할 때 다운로드하고 느리게 로드되지 않는 자산입니다. 일반적으로 공통 종속성(스타일시트 포함)과 현재 경로가 있는 공유 번들입니다.

타사 라이브러리가 성능에 미치는 영향

직접 작성하는 코드는 프로덕션 번들의 작은 부분일 뿐입니다. 콘텐츠의 대부분은 타사 종속성(dependency)으로 채워져 있습니다. 단순한 라이브러리는 사실 전체 애플리케이션보다 더 많은 코드를 포함할 수 있습니다!

우리는 일반적으로 앱을 느리게 만드는 것은 우리의 코드가 아니라 다른 사람들의 코드일 때 앱을 더 빠르게 만들 수 있기를 바라면서 자체 코드에서 다양한 최적화를 수행합니다! 이것이 제3자 라이브러리를 현명하게 선택하는 것이 성능이 좋은 웹 애플리케이션을 구축하는 열쇠인 이유입니다.

특정 타사 라이브러리의 성능 영향에 대한 학습은 bundlephobia 덕분에 매우 쉽습니다! NPM 레지스트리에서 사용 가능한 모든 라이브러리에 대한 성능 관련 데이터를 많이 찾을 수 있는 절대적으로 놀라운 웹 사이트입니다. 사용하려는 라이브러리의 이름을 입력하기만 하면 번들 공포증(bundlephobia)이 다양한 네트워크 조건에서 애플리케이션의 로딩 시간에 어떤 영향을 미치는지 정확히 알려줍니다.

라이브러리를 하나씩 확인하는 대신 package.json 전체 스캔을 한 번에 할 수도 있습니다. 앱 성능에 잠재적으로 가장 큰 영향을 미치는 라이브러리를 식별해야 할 때 매우 유용합니다. 너무 높으면 더 작은 대안을 찾아야 한다는 신호입니다.

번들 분석

Bundlephobia는 프로젝트에 추가하기 전에 특정 라이브러리 영향을 확인하고 싶지만 package.json 분석은 우리가 얻는 데이터가 컨텍스트가 없기 때문에 오해의 소지가 있을 수 있을 때 정말 유용합니다.
종속성을 스캔하고 일부 라이브러리가 번들에 700kB를 추가하고 있음을 발견하는 것이 전체 성능에 나쁘게 기여한다는 의미는 아닙니다. 이 라이브러리는 느리게 로드되거나 관리자만 다운로드하는 특정 번들에 포함될 수 있습니다. 원시 데이터가 성능 문제를 식별하는 데 도움이 될 수 있지만 이 지식만을 기반으로 결정을 내려서는 안 됩니다.

고맙게도 이 격차를 메울 수 있고 번들에 대한 컨텍스트 정보를 제공할 수 있는 훌륭한 웹팩 플러그인이 있습니다. 바로 webpack-bundle-analyzer입니다.

프로젝트에 추가하려면 npm 또는 yarn으로 설치하기만 하면 됩니다.

npm install --save-dev webpack-bundle-analyzer

웹팩 설정에 플러그인으로 포함하세요:

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
}

이제 앱을 빌드할 때마다 새 탭에서 생성된 코드의 그래픽 표현을 볼 수 있습니다. 다음과 같이 보일 것입니다.

webpack bundle analyzer zoomable treemap

모든 루트 수준 사각형은 단일 JS 번들을 나타냅니다. 우리는 각각을 검사하고 어떤 모듈과 타사 라이브러리가 내부에 있는지 확인할 수 있습니다. 또한 직사각형의 시각적 크기는 무게와 직접적인 관련이 있기 때문에 번들 크기에 가장 큰 영향을 미치는 라이브러리와 모듈을 쉽게 찾을 수 있습니다.

약점 찾기

더 진행하기 전에 성능 최적화에 너무 집착하는 것은 일반적으로 노력을 낭비한다는 것을 상기시키고 싶습니다. 라이브러리를 최적화하는 데 필요한 시간은 일반적으로 크고 작은 라이브러리에 대해 동일합니다. 우리는 가장 영향력 있는 이슈를 찾는 것을 목표로 삼고 그 이슈에만 집중해야 합니다. 마이크로 최적화는 눈에 띄지 않을 가능성이 높습니다.

이전 기사(지연 로딩, 라우팅, 프리페칭)에서 이러한 문제에 초점을 맞춘 좋은 사례를 많이 찾을 수 있으므로 오늘은 타사 라이브러리에만 초점을 맞출 것입니다.

기본 번들의 특정 경로에서만 사용되는 라이브러리를 포함하지 마십시오.

우선 특정 경로에서만 사용되는 라이브러리가 기본 번들에 포함되지 않도록 합니다. 많은 라이브러리가 작동하려면 일종의 구성 단계가 필요합니다.

예를 들어 Firebase SDK의 경우 ‘initializeApp’ 기능을 실행하여 다른 기능을 사용하기 전에 API 키를 구성해야 합니다. 일반적으로 이러한 항목을 앱 항목에 넣습니다. 다른 코드보다 먼저 실행되었는지 확인하기 위해 main.js와 같은 포인트를 지정합니다.
거기에는 아무런 문제가 없습니다. 좋은 습관이라고 생각할 수도 있지만 이렇게 하면 전체 라이브러리를 기본 번들에 넣는 것임을 알아야 합니다. 이 번들은 사용자가 입력하는 경로에 관계없이 사용자가 다운로드합니다.

기본 번들은 소위 중요한 종속성만 포함해야 합니다. - 응용 프로그램을 시작하는 데 필요한 최소한의 타사 라이브러리 집합입니다. 이러한 종속성의 좋은 예는 vue , vue-router 또는 vuex입니다.

번들 분석기의 아래 스크린샷을 살펴보십시오.

Znalezione obrazy dla zapytania bundle analyzer

여기서 볼 수 있는 것은 중요한 종속성만 포함해야 하는 shared vendor 번들입니다. 대화형 지도를 표시하는 자바스크립트 라이브러리인 leaflet 모듈을 즉시 발견할 수 있습니다. 이것은 아마도 연락처 페이지에 필요한 기능이지만 사전에 구성해야 하기 때문에 실수로 main.js에서 가져올 수 있고 그 때문에 기본 번들에 넣을 수 있습니다.

작은 것이 항상 좋은 것은 아니다

더 작은 라이브러리가 더 큰 라이브러리보다 항상 더 성능 친화적이라는 것이 직관적이고 논리적으로 보일 수 있지만 항상 그런 것은 아닙니다! 사실 라이브러리를 묶는 방식이 전체 크기보다 훨씬 더 중요합니다!

누군가 JavaScript 라이브러리를 구축할 때 일반적으로 우리가 애플리케이션에서 하는 것처럼 하나의 파일로 묶습니다. 분석기에 하나의 큰 직사각형이 표시되면 전체 라이브러리가 단일 파일로 연결되었음을 의미합니다. 유감스럽게도 ES6 모듈 번들러가 이 큰 파일의 어느 부분이 사용되었고 어떤 부분이 사용되지 않았는지 감지할 수 없는 매우 드문 상황을 제외하고는 종속성이 ES6으로 내보내집니다. 그 때문에 단일 기능만 사용하더라도 모든 기능이 포함된 전체 파일이 프로덕션 번들에 포함됩니다.

Lodash는 이 동작이 페이지 성능에 얼마나 심각한 영향을 미칠 수 있는지에 대한 가장 일반적인 예 중 하나입니다. 말 그대로 코드 세 줄lodash에서 단일 함수 isNull을 가져 오더라도 전체 라이브러리가 번들에 포함됩니다.

import { isNull } from 'lodash'

세 줄의 코드가 성능에 미치는 영향은 상당히 큽니다. 그렇지 않나요?

다행히도 많은 라이브러리가 기능을 가져오는 더 성능 친화적인 대안을 제공합니다. Lodash는 모든 기능이 포함된 연결된 자바스크립트 번들과 함께 파일당 하나씩 개별적으로 노출합니다. 다음과 같은 별도의 파일에서 이 기능을 가져오면:

import isNull from 'lodash/isNull`

이 파일의 내용(69킬로바이트 대신 약 500바이트)만 번들에 포함됩니다.

따라서 하나의 라이브러리를 다른 방식으로 가져옴으로써 24kB의 축소된 코드(전체 애플리케이션에 대한 성능 예산의 15%)를 절약한 것으로 나타났습니다! 매우 단순한 것에 대한 번들 분석기 출력에서 의심스러울 정도로 큰 직사각형을 볼 때마다 아마도 그 주요 부분이 필요하지 않을 것입니다. 이 기능을 가져오는 다른 방법이 있는지 확인하는 것이 좋습니다.

이를 수행하는 가장 좋은 방법은 항상 공식 문서이지만 때때로 이 정보가 부족합니다. 이러한 상황에서는 약간의 인터넷 검색을 할 가치가 있습니다. 라이브러리가 인기 있는 경우(lodash 처럼) 누군가가 이미 크기를 줄이는 방법을 찾았을 가능성이 매우 높습니다.

구성 요소 라이브러리

Vue 구성 요소 라이브러리는 성능에 정말 나쁜 영향을 줄 수 있는 특수한 유형의 종속성입니다. 기본적으로 대부분은 구성 요소를 개별적으로 가져오는 방법을 제공하지 않으므로 필요하지 않은 구성 요소를 제거할 수 없습니다. 이것이 최악인 부분이 아닙니다.

거의 모든 공통 UI 라이브러리는 설치할 때 모든 구성 요소를 전역적으로 등록하는 Vue 플러그인에서 제공됩니다. 그 때문에 루트 Vue 인스턴스를 초기화하기 전에 이 플러그인을 등록해야 합니다. 그러면 모든 라이브러리 코드를 사용자가 항상 다운로드하는 기본 번들에 넣고 다운로드 및 구문 분석할 때까지 아무 것도 볼 수 없습니다.

예를 들어 Vue에서 가장 인기 있는 Ui 라이브러리 중 하나인 Vuetify는 성능을 가장 많이 저하시킬 수 있는 라이브러리이기도 합니다. Bundlephobia의 통계를 살펴보십시오!

124kB는 거의 전체 성능 예산입니다! Vuetify에서 얼마나 많은 구성 요소를 사용하든(그리고 어떤 경로에서) 전체 라이브러리를 기본 번들로 끌어올 것입니다. 기본적으로 애플리케이션이 제대로 작동하지 못하게 합니다.

다행히 Vuetify 작성자는 라이브러리가 얼마나 큰지 알고 있으며 이 문제를 처리하는 매우 친숙한 방법을 제공했습니다. Vuetify 문서를 조금 더 파고들면 à la carte라는 문서를 찾을 수 있습니다. lodash에서 개별 기능을 가져오는 것과 거의 동일한 방식으로 Vuetify(및 기타 많은 인기 라이브러리)에서 개별 구성 요소를 가져올 수도 있습니다.

이를 수행하는 몇 가지 방법이 있으며 여기에서 모든 방법에 대해 읽을 수 있습니다. 아래에서 가장 쉬운 방법을 설명했습니다.

전체 라이브러리를 단일 JS 플러그인으로 가져오는 대신 모든 구성 요소를 전역적으로 등록하게 됩니다.

// main.js
import Vuetify from 'vuetify'

Vue.use(Vuetify) 

// rest of code

특정 구성 요소를 필요한 위치에 직접 가져올 수 있습니다.

<!-- About.vue -->
<template>
  <v-card>
    <v-card-title>...</v-card-title>
    <v-card-text>...</v-card-text>
  </v-card>
</template>

<script>
import { VCard, VCardText, VCardTitle } from 'vuetify/lib'

export default {
  components: {
    VCard,
    VCardText,
    VCardTitle,
  }
}
</script>

이렇게 하면 사용하지 않는 모든 구성 요소를 제거할 뿐만 아니라 다른 경로 번들에 사용하기로 결정한 구성 요소를 배포하여 중복 코드가 번들되지 않도록 합니다.

이 간단한 트릭으로 많은 문제를 해결할 수 있으므로 기억할 가치가 있습니다!

팁: Vuetify Nuxt 모듈Vue CLI 3 플러그인 모두 전용 Vuetify 웹팩 로더를 통해 기본적으로 동일한 동작을 제공합니다.

요약

타사 라이브러리는 애플리케이션의 번들 크기에 영향을 미치는 주요 요소입니다. 코드를 가져오는 방법은 전체 크기보다 훨씬 더 중요합니다. 모든 기능이 포함된 단일 개체를 가져오는 대신 필요한 항목만 가져올 수 있는 대체 방법이 있는지 확인하는 것이 좋습니다.

때로는 올바른 방법으로 라이브러리를 가져오는 경우 명확하지 않습니다. 고맙게도 webpack 번들 분석기와 bundlephobie는 이것을 알아내는 데 도움이 될 것입니다.

댓글남기기