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

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

앱 스크린샷

측정

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

  • 사이트를 미리 보려면 앱 보기를 누릅니다. 그런 다음 전체 화면 전체 화면을 누릅니다.

좋아하는 새끼 고양이를 클릭하세요. 이 애플리케이션에서는 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는 주 단위로 나이를 계산하는 데 사용됩니다.

종속 항목 2개만으로 번들 크기가 거의 1MB에 달할 수 있나요? 그 이유는 종속 항목이 자체 종속 항목을 가질 수 있기 때문입니다. 종속 항목 '트리'의 모든 깊이/분기를 고려하면 두 개보다 훨씬 많습니다. 종속 항목이 많이 포함된 경우 애플리케이션이 비교적 빠르게 커질 수 있습니다.

번들러를 분석하여 진행 상황을 더 잘 파악하세요. 이 작업을 지원하는 다양한 커뮤니티 제작 도구가 있습니다(예: 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는 컴파일된 파일을 자동으로 최소화하므로 통계 크기보다 작습니다.
Gzipped size(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;

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

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 인수, 이미 밀리초 단위) 간의 밀리초 차이를 계산하고 이를 1주일의 밀리초 수로 나눕니다.

마지막으로 이 함수를 대신 활용하여 이벤트 리스너에서 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와 같은 라이브러리를 사용하면 이 작업을 크게 간소화할 수 있습니다.

모든 것은 트레이드오프이며, 서드 파티 라이브러리를 사용하는 대신 맞춤 솔루션을 출시하는 것이 복잡성과 노력에 비해 가치가 있는지 측정하는 것이 중요합니다.