사용하지 않는 코드 삭제하기

이 Codelab에서는 사용되지 않거나 불필요한 종속 항목을 삭제하여 다음 애플리케이션의 성능을 개선합니다.

앱 스크린샷

측정

최적화를 추가하기 전에 항상 웹사이트 실적을 먼저 측정하는 것이 좋습니다.

  • 사이트를 미리 보려면 View App을 누른 다음 Fullscreen 전체 화면을 누릅니다.

좋아하는 고양이를 클릭하세요. 이 애플리케이션에는 Firebase의 실시간 데이터베이스가 사용되므로 점수가 실시간으로 업데이트되고 애플리케이션을 사용하는 다른 모든 사용자와 동기화됩니다. 🐈

  1. `Control+Shift+J` (Mac의 경우 `Command+Option+J`)를 눌러 DevTools를 엽니다.
  2. 네트워크 탭을 클릭합니다.
  3. 캐시 사용 중지 체크박스를 선택합니다.
  4. 앱을 새로고침합니다.

원래 번들 크기: 992KB

이 간단한 애플리케이션을 로드하기 위해 약 1MB 상당의 JavaScript가 제공되고 있습니다.

DevTools에서 프로젝트 경고를 살펴보세요.

  • 콘솔 탭을 클릭합니다.
  • Filter 입력 옆의 수준 드롭다운에서 Warnings가 사용 설정되어 있는지 확인합니다.

경고 필터

  • 표시된 경고를 확인합니다.

콘솔 경고

이 애플리케이션에서 사용되는 라이브러리 중 하나인 Firebase는 개발자에게 전체 패키지가 아닌 사용되는 구성요소만 가져오라는 경고를 표시하여 모범적인 사례입니다. 즉, 더 빠른 로드를 위해 이 애플리케이션에서 삭제할 수 있는 미사용 라이브러리가 있습니다.

특정 라이브러리가 사용되는 경우도 있지만 더 간단한 대안이 있을 수 있습니다. 불필요한 라이브러리를 삭제하는 개념은 이 가이드의 뒷부분에서 설명합니다.

번들 분석

애플리케이션에는 두 가지 주요 종속 항목이 있습니다.

  • Firebase: iOS, Android 또는 웹 애플리케이션을 위한 여러 유용한 서비스를 제공하는 플랫폼입니다. 여기서 실시간 데이터베이스는 각 고양이에 대한 정보를 실시간으로 저장하고 동기화하는 데 사용됩니다.
  • Moment.js: JavaScript에서 날짜를 더 쉽게 처리할 수 있는 유틸리티 라이브러리입니다. 새끼 고양이의 생년월일은 Firebase 데이터베이스에 저장되며 moment를 사용하여 새끼 고양이의 나이를 주 단위로 계산합니다.

종속 항목 두 개만이 번들 크기가 거의 1MB가 되는 이유는 무엇인가요? 그 이유 중 하나는 모든 종속 항목이 차례로 자체 종속 항목을 가질 수 있기 때문입니다. 따라서 종속 항목 '트리'의 모든 깊이/브랜치를 고려하면 2개 이상의 항목이 생깁니다. 많은 종속 항목이 포함되어 있으면 애플리케이션이 비교적 빠르게 커지기 쉽습니다.

번들러를 분석하여 상황을 더 정확하게 파악합니다. 이 작업을 수행하는 데 도움이 되는 여러 가지 커뮤니티 빌드 도구(예: webpack-bundle-analyzer)가 있습니다.

이 도구의 패키지는 이미 앱에 devDependency로 포함되어 있습니다.

"devDependencies": {
  //...
  "webpack-bundle-analyzer": "^2.13.1"
},

즉, webpack 구성 파일에서 직접 사용할 수 있습니다. webpack.config.js의 맨 처음에 가져옵니다.

const path = require("path");

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

이제 plugins 배열 내 파일 맨 끝에 이를 플러그인으로 추가합니다.

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

애플리케이션이 새로고침되면 앱 자체가 아닌 전체 번들이 시각화됩니다.

Webpack 번들 분석 도구

새끼 고양이를 보는 것만큼 귀엽진 않지만 그럼에도 엄청난 도움이 됩니다. 패키지 중 하나에 마우스를 가져가면 세 가지 방식으로 크기가 표시됩니다.

통계 크기 압축 또는 압축 전의 크기입니다.
파싱된 크기 컴파일된 후 번들 내 실제 패키지의 크기입니다. 이 애플리케이션에서 사용되는 webpack 버전 4는 컴파일된 파일을 자동으로 축소하므로 통계 크기보다 작습니다.
Gzip으로 압축된 크기 gzip 인코딩으로 압축한 후 패키지의 크기입니다. 이 주제는 별도의 가이드에서 다룹니다.

webpack-bundle-analyzer 도구를 사용하면 번들의 상당 부분을 차지하는 사용되지 않거나 불필요한 패키지를 쉽게 식별할 수 있습니다.

사용하지 않는 패키지 삭제

시각화를 보면 firebase 패키지가 단순한 데이터베이스보다 많은 것으로 구성되어 있음을 알 수 있습니다. 다음과 같은 추가 패키지가 포함됩니다.

  • firestore
  • auth
  • storage
  • messaging
  • functions

이들은 모두 Firebase에서 제공하는 훌륭한 서비스이지만 (자세한 내용은 문서 참조) 애플리케이션에서 사용되는 서비스가 아니므로 모두 가져올 필요는 없습니다.

애플리케이션을 다시 보려면 webpack.config.js의 변경사항을 되돌립니다.

  • 플러그인 목록에서 BundleAnalyzerPlugin를 삭제합니다.
plugins: [
  //...
  new BundleAnalyzerPlugin()
];
  • 이제 파일 상단에서 사용되지 않는 가져오기를 삭제합니다.
const path = require("path");

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

이제 애플리케이션이 정상적으로 로드됩니다. src/index.js를 수정하여 Firebase 가져오기를 업데이트합니다.

import firebase from 'firebase';
import firebase from 'firebase/app';
import 'firebase/database';

이제 앱이 새로고침되면 DevTools 경고가 표시되지 않습니다. DevTools 네트워크 패널을 열면 번들 크기도 적당 감소한 것을 확인할 수 있습니다.

번들 크기를 480KB로 줄임

번들 크기의 절반 이상이 삭제되었습니다. Firebase는 다양한 서비스를 제공하며 개발자에게 실제로 필요한 서비스만 포함할 수 있는 옵션을 제공합니다. 이 애플리케이션에서는 모든 데이터를 저장하고 동기화하는 데 firebase/database만 사용되었습니다. 각 서비스의 API 노출 영역을 설정하는 firebase/app 가져오기는 항상 필요합니다.

lodash와 같이 널리 사용되는 다른 많은 라이브러리도 개발자가 패키지의 다양한 부분을 선택적으로 가져올 수 있게 해줍니다. 많은 작업을 실행하지 않고도 사용 중인 항목만 포함하도록 애플리케이션의 라이브러리 가져오기를 업데이트하면 성능이 크게 향상될 수 있습니다.

번들 크기가 상당히 줄어들었지만 아직 할 일이 더 있습니다. 😈

불필요한 패키지 삭제

Firebase와 달리 moment 라이브러리의 일부를 가져오기는 쉽게 할 수 없지만 완전히 삭제할 수는 있습니다.

각 귀여운 새끼 고양이의 생일은 Firebase 데이터베이스에 Unix 형식 (밀리초)으로 저장됩니다.

Unix 형식으로 저장된 생년월일

1970년 1월 1일 00:00 UTC 이후 경과된 밀리초 수로 표시되는 특정 날짜와 시간의 타임스탬프입니다. 현재 날짜와 시간을 같은 형식으로 계산할 수 있다면 각 새끼 고양이의 나이를 몇 주 단위로 찾는 작은 함수를 만들 수 있습니다.

항상 그렇듯이 아래 설명을 따르면서 복사하여 붙여넣지 마세요. 먼저 src/index.js의 가져오기에서 moment를 삭제합니다.

import firebase from 'firebase/app';
import 'firebase/database';
import * as moment from 'moment';

데이터베이스의 값 변경을 처리하는 Firebase 이벤트 리스너가 있습니다.

favoritesRef.on("value", (snapshot) => { ... })

이 위에 지정된 날짜로부터 주 수를 계산하는 작은 함수를 추가합니다.

const ageInWeeks = birthDate => {
  const WEEK_IN_MILLISECONDS = 1000 * 60 * 60 * 24 * 7;
  const diff = Math.abs((new Date).getTime() - birthDate);
  return Math.floor(diff / WEEK_IN_MILLISECONDS);
}

이 함수에서 현재 날짜 및 시간 (new Date).getTime()과 생년월일 (이미 밀리초 단위의 birthDate 인수) 사이의 밀리초 단위 차이를 계산하여 일주일의 밀리초 수로 나눕니다.

마지막으로, 이 함수를 대신 활용하여 이벤트 리스너에서 moment의 모든 인스턴스를 삭제할 수 있습니다.

favoritesRef.on("value", (snapshot) => {
  const { kitties, favorites, names, birthDates } = snapshot.val();
  favoritesScores = favorites;

  kittiesList.innerHTML = kitties.map((kittiePic, index) => {
    const birthday = moment(birthDates[index]);

    return `
      <li>
        <img src=${kittiePic} onclick="favKittie(${index})">
        <div class="extra">
          <div class="details">
            <p class="name">${names[index]}</p>
            <p class="age">${moment().diff(birthday, 'weeks')} weeks old</p>
            <p class="age">${ageInWeeks(birthDates[index])} weeks old</p>
          </div>
          <p class="score">${favorites[index]} ❤</p>
        </div>
      </li>
    `})
});

이제 애플리케이션을 새로고침하고 네트워크 패널을 한 번 더 확인합니다.

번들 크기를 225KB로 줄임

번들의 크기가 다시 절반 이상 줄었습니다.

결론

이 Codelab을 통해 특정 번들을 분석하는 방법과 사용되지 않거나 불필요한 패키지를 삭제하는 것이 왜 유용한지 충분히 이해하게 될 것입니다. 이 기법으로 애플리케이션을 최적화하기 전에 대규모 애플리케이션에서는 이 방법이 훨씬 더 복잡할 수 있음을 아는 것이 중요합니다.

사용하지 않는 라이브러리 삭제와 관련하여 번들에서 사용되는 부분과 사용되지 않는 부분을 확인하세요. 아무 곳에서도 사용되지 않는 것처럼 보이는 이상해 보이는 패키지에는 한 걸음 물러서서 어떤 최상위 종속 항목에 이 파일이 필요한지 확인하세요. 가능한 한 서로 분리할 방법을 찾으세요.

불필요한 라이브러리 삭제에 관한 작업은 조금 더 복잡할 수 있습니다. 팀과 긴밀하게 협력하고 코드베이스의 일부를 단순화할 가능성이 있는지 확인하는 것이 중요합니다. 이 애플리케이션에서 moment를 삭제하는 것이 매번 옳은 것처럼 보일 수 있지만 처리해야 하는 시간대와 다른 언어가 있다면 어떻게 해야 할까요? 또는 더 복잡한 날짜 조작이 있다면 어떻게 될까요? 날짜/시간을 조작하고 파싱할 때 작업이 매우 까다로워질 수 있으며 momentdate-fns와 같은 라이브러리는 이를 크게 단순화합니다.

모든 것은 절충해야 하며, 서드 파티 라이브러리에 의존하는 대신 커스텀 솔루션을 출시하기 위해 복잡성과 노력이 필요한지를 판단하는 것이 중요합니다.