모듈식 Firebase JS SDK로 마이그레이션하여 웹 앱 강화

1. 시작하기 전에

모듈식 Firebase JS SDK는 기존 JS SDK를 재작성한 것으로, 다음 주 버전으로 출시될 예정입니다. 이를 통해 개발자는 Firebase JS SDK에서 사용되지 않는 코드를 제외하여 더 작은 번들을 만들고 더 나은 성능을 달성할 수 있습니다.

모듈식 JS SDK에서 가장 눈에 띄는 차이점은 모든 항목을 포함하는 단일 firebase 네임스페이스가 아닌 이제 기능이 가져올 자유로운 플로팅 함수로 구성된다는 점입니다. 이 새로운 코드 구성 방식은 트리 쉐이킹을 가능하게 하며, 현재 v8 Firebase JS SDK를 사용 중인 앱을 새로운 모듈식 앱으로 업그레이드하는 방법을 배웁니다.

원활한 업그레이드 프로세스를 제공하기 위해 호환성 패키지 세트가 제공됩니다. 이 Codelab에서는 호환성 패키지를 사용하여 앱을 부분별로 포팅하는 방법을 알아봅니다.

빌드할 항목

이 Codelab에서는 v8 JS SDK를 사용하는 기존 스톡 관심 목록 웹 앱을 다음 세 단계를 통해 새로운 모듈식 JS SDK로 점진적으로 이전합니다.

  • 앱을 업그레이드하여 호환성 패키지 사용
  • 호환성 패키지에서 모듈식 API로 앱을 부분적으로 업그레이드
  • Firestore SDK의 가벼운 구현인 Firestore Lite를 사용하여 앱 성능을 더욱 향상하세요.

2d351cb47b604ad7.png

이 Codelab에서는 Firebase SDK를 업그레이드하는 데 중점을 둡니다. 다른 개념과 코드 블록은 자세히 다루지 않으며 간단히 복사하여 붙여넣을 수 있도록 제공됩니다.

필요한 사항

  • 원하는 브라우저(예: Chrome)
  • 원하는 IDE/텍스트 편집기(예: WebStorm, Atom, Sublime, VS Code)
  • 패키지 관리자 npm(일반적으로 Node.js와 함께 제공)
  • Codelab의 샘플 코드 (코드를 가져오는 방법은 Codelab의 다음 단계 참고)

2. 설정

코드 가져오기

이 프로젝트에 필요한 모든 항목은 Git 저장소에 있습니다. 시작하려면 코드를 가져와 원하는 개발 환경에서 열어야 합니다.

명령줄에서 Codelab의 GitHub 저장소를 클론합니다.

git clone https://github.com/FirebaseExtended/codelab-modular-sdk.git

또는 git이 설치되어 있지 않은 경우 저장소를 ZIP 파일로 다운로드하고 다운로드한 ZIP 파일의 압축을 풉니다.

앱 가져오기

  1. IDE를 사용하여 codelab-modular-sdk 디렉터리를 열거나 가져옵니다.
  2. npm install를 실행하여 앱을 로컬에서 빌드하고 실행하는 데 필요한 종속 항목을 설치합니다.
  3. npm run build를 실행하여 앱을 빌드합니다.
  4. npm run serve를 실행하여 웹 서버를 시작합니다.
  5. 브라우저 탭을 열어 http://localhost:8080으로 이동합니다.

71a8a7d47392e8f4.png

3. 기준 설정

시작하기

시작점은 이 Codelab을 위해 설계된 스톡 관심 목록 앱입니다. 이 Codelab의 개념을 설명하기 위해 코드를 단순화했으며 오류 처리도 거의 없습니다. 프로덕션 앱에서 이 코드를 재사용하려면 오류를 처리하고 모든 코드를 완전히 테스트해야 합니다.

앱에서 모든 것이 작동하는지 확인합니다.

  1. 오른쪽 상단의 로그인 버튼을 사용해 익명으로 로그인합니다.
  2. 로그인한 후 "NFLX", "SBUX"를 검색하여 추가하세요. 및 'T' 추가 버튼을 클릭하고 문자를 입력한 후 아래에 팝업으로 나타나는 검색결과 행을 클릭하면 관심 목록에 추가할 수 있습니다.
  3. 행 끝에 있는 x를 클릭하여 관심 목록에서 주식을 삭제합니다.
  4. 실시간 주가 업데이트를 확인하세요.
  5. Chrome DevTools를 열고 네트워크 탭으로 이동하여 캐시 사용 중지큰 요청 행 사용을 선택합니다. 캐시 사용 중지를 선택하면 새로고침 후 항상 최신 변경사항을 가져올 수 있으며, 넓은 요청 행 사용을 선택하면 행에 리소스의 전송된 크기와 리소스 크기가 모두 표시됩니다. 이 Codelab에서는 주로 main.js의 크기를 살펴봅니다.

48a096debb2aa940.png

  1. 시뮬레이션된 제한을 사용하여 다양한 네트워크 조건에서 앱을 로드합니다. 이 Codelab에서는 느린 3G를 사용하여 로드 시간을 측정합니다. 작은 번들 크기가 가장 유용하기 때문입니다.

4397cb2c1327089.png

이제 바로 앱을 새로운 모듈식 API로 마이그레이션해 보세요.

4. 호환성 패키지 사용

호환성 패키지를 사용하면 모든 Firebase 코드를 한 번에 변경하지 않고도 새 SDK 버전으로 업그레이드할 수 있습니다. 점진적으로 모듈식 API로 업그레이드할 수 있습니다.

이 단계에서는 Firebase 라이브러리를 v8에서 새 버전으로 업그레이드하고 호환성 패키지를 사용하도록 코드를 변경합니다. 다음 단계에서는 먼저 모듈식 API를 사용하도록 Firebase 인증 코드만 업그레이드한 다음 Firestore 코드를 업그레이드하는 방법을 알아봅니다.

각 단계가 끝날 때마다 중단 없이 앱을 컴파일하고 실행할 수 있어야 하며 각 제품을 이전함에 따라 번들 크기가 감소하는 것을 확인할 수 있습니다.

새 SDK 다운로드

package.json에서 종속 항목 섹션을 찾아 다음으로 바꿉니다.

package.json

"dependencies": {
    "firebase": "^9.0.0" 
}

종속 항목 재설치

종속 항목의 버전을 변경했으므로 npm install를 다시 실행하여 종속 항목의 새 버전을 가져와야 합니다.

가져오기 경로 변경

호환성 패키지는 하위 모듈 firebase/compat에 노출되므로 적절하게 가져오기 경로를 업데이트합니다.

  1. src/firebase.ts 파일로 이동
  2. 기존 가져오기를 다음 가져오기로 바꿉니다.

src/firebase.ts

import firebase from 'firebase/compat/app'; 
import 'firebase/compat/auth'; 
import 'firebase/compat/firestore';

앱 작동 확인

  1. npm run build를 실행하여 앱을 다시 빌드합니다.
  2. 브라우저 탭에서 http://localhost:8080을 열거나 기존 탭을 새로고침합니다.
  3. 앱을 플레이합니다. 모든 것이 정상적으로 작동할 것입니다.

5. 모듈식 API를 사용하도록 인증 업그레이드

Firebase 제품은 순서에 관계없이 업그레이드할 수 있습니다. 이 Codelab에서는 먼저 Auth를 업그레이드하여 기본 개념을 알아봅니다. Auth API는 비교적 간단하기 때문입니다. Firestore 업그레이드는 좀 더 복잡하며, 이에 대한 방법은 다음에 알아봅니다.

인증 초기화 업데이트

  1. src/firebase.ts 파일로 이동
  2. 다음 가져오기를 추가합니다.

src/firebase.ts

import { initializeAuth, indexedDBLocalPersistence } from 'firebase/auth';
  1. import ‘firebase/compat/auth'. 삭제
  2. export const firebaseAuth = app.auth();를 다음으로 바꿉니다.

src/firebase.ts

export const firebaseAuth = initializeAuth(app, { persistence: [indexedDBLocalPersistence] });
  1. 파일 끝에서 export type User = firebase.User;를 삭제합니다. User을(를) src/auth.ts(으)로 직접 내보내고 이 항목을 다음에 변경합니다.

인증 코드 업데이트

  1. src/auth.ts 파일로 이동
  2. 파일 상단에 다음 가져오기를 추가합니다.

src/auth.ts

import { 
    signInAnonymously, 
    signOut,
    onAuthStateChanged,
    User
} from 'firebase/auth';
  1. 이미 ‘firebase/auth'.에서 User을(를) 가져왔으므로 import { firebaseAuth, User } from './firebase';에서 User을(를) 삭제합니다.
  2. 모듈식 API를 사용하도록 함수를 업데이트합니다.

앞서 import 문을 업데이트할 때 확인했듯이, 버전 9의 패키지는 점으로 체이닝된 네임스페이스 및 서비스 패턴을 기반으로 하는 버전 8 API와 달리 가져올 수 있는 함수를 중심으로 구성됩니다. 이 새로운 코드 구성으로 미사용 코드를 트리 쉐이킹할 수 있습니다. 빌드 도구를 통해 사용되는 코드와 사용되지 않는 코드를 분석할 수 있기 때문입니다.

버전 9에서 서비스는 첫 번째 인수로 함수에 전달됩니다. 서비스는 Firebase 서비스를 초기화할 때 가져오는 객체입니다. 예: getAuth() 또는 initializeAuth()에서 반환된 객체입니다. 서비스 객체는 특정 Firebase 서비스의 상태를 보유하고 이 상태를 사용하여 작업을 수행합니다. 이 패턴을 적용하여 다음 함수를 구현해 보겠습니다.

src/auth.ts

export function firebaseSignInAnonymously() { 
    return signInAnonymously(firebaseAuth); 
} 

export function firebaseSignOut() { 
    return signOut(firebaseAuth); 
} 

export function onUserChange(callback: (user: User | null) => void) { 
    return onAuthStateChanged(firebaseAuth, callback); 
} 

export { User } from 'firebase/auth';

앱 작동 확인

  1. npm run build를 실행하여 앱을 다시 빌드합니다.
  2. 브라우저 탭에서 http://localhost:8080을 열거나 기존 탭을 새로고침합니다.
  3. 앱을 플레이합니다. 모든 것이 정상적으로 작동할 것입니다.

번들 크기 확인하기

  1. Chrome DevTools를 엽니다.
  2. 네트워크 탭으로 전환합니다.
  3. 페이지를 새로고침하여 네트워크 요청을 캡처합니다.
  4. main.js를 찾아 크기를 확인합니다. 번들 크기가 100KB (gzip으로 36KB) 줄었거나 단 몇 줄만 변경하여 약 22% 줄였습니다. 또한 이 사이트는 느린 3G 연결에서 0.75초 더 빠르게 로드됩니다.

2e4eafaf66cd829b.png

6. 모듈식 API를 사용하도록 Firebase 앱 및 Firestore 업그레이드

Firebase 초기화 업데이트

  1. src/firebase.ts. 파일로 이동
  2. import firebase from ‘firebase/compat/app';를 다음으로 바꿉니다.

src/firebase.ts

import { initializeApp } from 'firebase/app';
  1. const app = firebase.initializeApp({...});를 다음으로 바꿉니다.

src/firebase.ts

const app = initializeApp({
    apiKey: "AIzaSyBnRKitQGBX0u8k4COtDTILYxCJuMf7xzE", 
    authDomain: "exchange-rates-adcf6.firebaseapp.com", 
    databaseURL: "https://exchange-rates-adcf6.firebaseio.com", 
    projectId: "exchange-rates-adcf6", 
    storageBucket: "exchange-rates-adcf6.appspot.com", 
    messagingSenderId: "875614679042", 
    appId: "1:875614679042:web:5813c3e70a33e91ba0371b"
});

Firestore 초기화 업데이트

  1. 동일한 파일에서 src/firebase.ts,import 'firebase/compat/firestore';로 바꿉니다.

src/firebase.ts

import { getFirestore } from 'firebase/firestore';
  1. export const firestore = app.firestore();를 다음으로 바꿉니다.

src/firebase.ts

export const firestore = getFirestore();
  1. 'export const firestore = ...' 뒤의 모든 행 삭제

가져오기 업데이트

  1. src/services.ts. 파일 열기
  2. 가져오기에서 FirestoreFieldPath, FirestoreFieldValue, QuerySnapshot를 삭제합니다. 이제 './firebase'에서 가져오기가 다음과 같이 표시됩니다.

src/services.ts

import { firestore } from './firebase';
  1. 파일 상단에서 사용할 함수와 유형을 가져옵니다.
    **src/services.ts**
import { 
    collection, 
    getDocs, 
    doc, 
    setDoc, 
    arrayUnion, 
    arrayRemove, 
    onSnapshot, 
    query, 
    where, 
    documentId, 
    QuerySnapshot
} from 'firebase/firestore';
  1. 모든 티커가 포함된 컬렉션에 대한 참조를 만듭니다.

src/services.ts

const tickersCollRef = collection(firestore, 'current');
  1. getDocs()를 사용하여 컬렉션에서 모든 문서를 가져옵니다.

src/services.ts

const tickers = await getDocs(tickersCollRef);

완성된 코드는 search()를 참고하세요.

addToWatchList() 업데이트

doc()를 사용하여 사용자의 관심 목록에 대한 문서 참조를 만든 다음 arrayUnion()와 함께 setDoc()를 사용하여 티커를 추가합니다.

src/services.ts

export function addToWatchList(ticker: string, user: User) {
      const watchlistRef = doc(firestore, `watchlist/${user.uid}`);
      return setDoc(watchlistRef, {
       tickers: arrayUnion(ticker)
   }, { merge: true });
}

deleteFromWatchList() 업데이트

마찬가지로 arrayRemove()와 함께 setDoc()를 사용하여 사용자의 관심 목록에서 티커를 삭제합니다.

src/services.ts

export function deleteFromWatchList(ticker: string, user: User) {
   const watchlistRef = doc(firestore, `watchlist/${user.uid}`);
   return setDoc(watchlistRef, {
       tickers: arrayRemove(ticker)
   }, { merge: true });
}

subscribeToTickerChanges() 업데이트

  1. doc()를 사용하여 먼저 사용자의 관심 목록에 대한 문서 참조를 만든 다음 onSnapshot()를 사용하여 관심 목록 변경사항을 수신 대기합니다.

src/services.ts

const watchlistRef = doc(firestore, `watchlist/${user.uid}`);
const unsubscribe = onSnapshot(watchlistRef, snapshot => {
   /* subscribe to ticker price changes */
});
  1. 관심 목록에 티커가 있으면 query()를 사용하여 가격을 가져오는 쿼리를 만들고 onSnapshot()를 사용하여 가격 변경을 리슨합니다.

src/services.ts

const priceQuery = query(
    collection(firestore, 'current'),
    where(documentId(), 'in', tickers)
);
unsubscribePrevTickerChanges = onSnapshot(priceQuery, snapshot => {
               if (firstload) {
                   performance && performance.measure("initial-data-load");
                   firstload = false;
                   logPerformance();
               }
               const stocks = formatSDKStocks(snapshot);
               callback(stocks);
  });

전체 구현은 subscribeToTickerChanges()를 참고하세요.

subscribeToAllTickerChanges() 업데이트

먼저 collection()를 사용하여 먼저 모든 티커의 가격이 포함된 컬렉션에 대한 참조를 만든 다음 onSnapshot()를 사용하여 가격 변경을 수신 대기합니다.

src/services.ts

export function subscribeToAllTickerChanges(callback: TickerChangesCallBack) {
   const tickersCollRef = collection(firestore, 'current');
   return onSnapshot(tickersCollRef, snapshot => {
       if (firstload) {
           performance && performance.measure("initial-data-load");
           firstload = false;
           logPerformance();
       }
       const stocks = formatSDKStocks(snapshot);
       callback(stocks);
   });
}

앱 작동 확인

  1. npm run build를 실행하여 앱을 다시 빌드합니다.
  2. 브라우저 탭에서 http://localhost:8080을 열거나 기존 탭을 새로고침합니다.
  3. 앱을 플레이합니다. 모든 것이 정상적으로 작동할 것입니다.

번들 크기 확인하기

  1. Chrome DevTools를 엽니다.
  2. 네트워크 탭으로 전환합니다.
  3. 페이지를 새로고침하여 네트워크 요청을 캡처합니다.
  4. main.js를 찾아 크기를 확인합니다. 원래 번들 크기와 다시 비교해 보세요. 번들 크기가 200KB 이상 (63.8KB gzip됨) 또는 50% 작아져 로드 시간이 1.3초 단축되었습니다.

7660cdc574ee8571.png

7. Firestore Lite를 사용하여 초기 페이지 렌더링 속도 향상

Firestore Lite란 무엇인가요?

Firestore SDK는 복잡한 캐싱, 실시간 스트리밍, 영구 스토리지, 멀티탭 오프라인 동기화, 재시도, 낙관적 동시 실행 등을 제공하므로 크기가 상당히 큽니다. 그러나 고급 기능이 없어도 단순히 데이터를 한 번만 가져오고자 할 수도 있습니다. 이러한 경우를 위해 Firestore는 새로운 패키지인 Firestore Lite라는 간단하고 가벼운 솔루션을 만들었습니다.

Firestore Lite의 한 가지 좋은 사용 사례는 초기 페이지 렌더링의 성능을 최적화하는 것입니다. 이 경우에는 사용자가 로그인했는지 여부를 확인한 후 Firetore에서 일부 데이터를 읽어 표시하면 됩니다.

이 단계에서는 Firestore lite로 번들 크기를 줄여 초기 페이지 렌더링 속도를 높인 다음 기본 Firestore SDK를 동적으로 로드하여 실시간 업데이트를 구독하는 방법을 알아봅니다.

코드를 다음과 같이 리팩터링합니다.

  1. 동적 가져오기를 사용하여 동적으로 로드할 수 있도록 실시간 서비스를 별도의 파일로 이동합니다.
  2. Firestore Lite를 사용하여 관심 목록과 주가를 검색하는 새 함수를 만듭니다.
  3. 새로운 Firestore Lite 함수를 사용하여 데이터를 검색하여 초기 페이지를 렌더링한 다음 실시간 서비스를 동적으로 로드하여 실시간 업데이트를 리슨합니다.

실시간 서비스를 새 파일로 이동

  1. src/services.realtime.ts.라는 새 파일을 만듭니다.
  2. subscribeToTickerChanges()subscribeToAllTickerChanges() 함수를 src/services.ts에서 새 파일로 이동합니다.
  3. 필요한 가져오기를 새 파일 상단에 추가합니다.

여기서 몇 가지 사항을 변경해야 합니다.

  1. 먼저, 파일 상단의 기본 Firestore SDK에서 함수에 사용할 Firestore 인스턴스를 만듭니다. 여기서는 firebase.ts에서 Firestore 인스턴스를 가져올 수 없습니다. 몇 단계를 거쳐 Firestore Lite 인스턴스로 변경할 것이므로 초기 페이지 렌더링에만 사용됩니다.
  2. 둘째, firstload 변수와 이 변수로 보호되는 if 블록을 삭제합니다. 해당 기능은 다음 단계에서 만들 새 함수로 이동됩니다.

src/services.realtime.ts

import { User } from './auth'
import { TickerChange } from './models';
import { collection, doc, onSnapshot, query, where, documentId, getFirestore } from 'firebase/firestore';
import { formatSDKStocks } from './services';

const firestore = getFirestore();
type TickerChangesCallBack = (changes: TickerChange[]) => void

export function subscribeToTickerChanges(user: User, callback: TickerChangesCallBack) {

   let unsubscribePrevTickerChanges: () => void;

   // Subscribe to watchlist changes. We will get an update whenever a ticker is added/deleted to the watchlist
   const watchlistRef = doc(firestore, `watchlist/${user.uid}`);
   const unsubscribe = onSnapshot(watchlistRef, snapshot => {
       const doc = snapshot.data();
       const tickers = doc ? doc.tickers : [];

       if (unsubscribePrevTickerChanges) {
           unsubscribePrevTickerChanges();
       }

       if (tickers.length === 0) {
           callback([]);
       } else {
           // Query to get current price for tickers in the watchlist
           const priceQuery = query(
               collection(firestore, 'current'),
               where(documentId(), 'in', tickers)
           );

           // Subscribe to price changes for tickers in the watchlist
           unsubscribePrevTickerChanges = onSnapshot(priceQuery, snapshot => {
               const stocks = formatSDKStocks(snapshot);
               callback(stocks);
           });
       }
   });
   return () => {
       if (unsubscribePrevTickerChanges) {
           unsubscribePrevTickerChanges();
       }
       unsubscribe();
   };
}

export function subscribeToAllTickerChanges(callback: TickerChangesCallBack) {
   const tickersCollRef = collection(firestore, 'current');
   return onSnapshot(tickersCollRef, snapshot => {
       const stocks = formatSDKStocks(snapshot);
       callback(stocks);
   });
}

Firestore Lite를 사용하여 데이터 가져오기

  1. src/services.ts. 열기
  2. 가져오기 경로를 ‘firebase/firestore'에서 ‘firebase/firestore/lite',로 변경하고 getDoc를 추가하고 가져오기 목록에서 onSnapshot을 삭제합니다.:

src/services.ts

import { 
    collection, 
    getDocs, 
    doc, 
    setDoc, 
    arrayUnion, 
    arrayRemove,
//  onSnapshot, // firestore lite doesn't support realtime updates
    query, 
    where, 
    documentId, 
    QuerySnapshot, 
    getDoc // add this import
} from 'firebase/firestore/lite';
  1. Firestore Lite를 사용하여 초기 페이지 렌더링에 필요한 데이터를 가져오는 함수를 추가합니다.

src/services.ts

export async function getTickerChanges(tickers: string[]): Promise<TickerChange[]> {

   if (tickers.length === 0) {
       return [];
   }

   const priceQuery = query(
       collection(firestore, 'current'),
       where(documentId(), 'in', tickers)
   );
   const snapshot = await getDocs(priceQuery);
   performance && performance.measure("initial-data-load");
   logPerformance();
   return formatSDKStocks(snapshot);
}

export async function getTickers(user: User): Promise<string[]> {
   const watchlistRef = doc(firestore, `watchlist/${user.uid}`);
   const data =  (await getDoc(watchlistRef)).data();

   return data ? data.tickers : [];
}

export async function getAllTickerChanges(): Promise<TickerChange[]> {
   const tickersCollRef = collection(firestore, 'current');
   const snapshot = await getDocs(tickersCollRef);
   performance && performance.measure("initial-data-load");
   logPerformance();
   return formatSDKStocks(snapshot);
}
  1. src/firebase.ts를 열고 가져오기 경로를 ‘firebase/firestore'에서 ‘firebase/firestore/lite':로 변경합니다.

src/firebase.ts

import { getFirestore } from 'firebase/firestore/lite';

종합적으로 활용

  1. src/main.ts. 열기
  2. 초기 페이지 렌더링을 위한 데이터를 가져오려면 새로 만든 함수와 앱 상태를 관리하기 위한 몇 가지 도우미 함수가 필요합니다. 이제 import를 업데이트합니다.

src/main.ts

import { renderLoginPage, renderUserPage } from './renderer';
import { getAllTickerChanges, getTickerChanges, getTickers } from './services';
import { onUserChange } from './auth';
import { getState, setRealtimeServicesLoaded, setUser } from './state';
import './styles.scss';
  1. 파일 상단에서 동적 가져오기를 사용하여 src/services.realtime를 로드합니다. loadRealtimeService 변수는 코드가 로드되면 실시간 서비스로 확인되는 프로미스입니다. 나중에 실시간 업데이트를 구독하는 데 사용됩니다.

src/main.ts

const loadRealtimeService = import('./services.realtime');
loadRealtimeService.then(() => {
   setRealtimeServicesLoaded(true);
});
  1. 함수 본문에서 await를 사용할 수 있도록 onUserChange()의 콜백을 async 함수로 변경합니다.

src/main.ts

onUserChange(async user => {
 // callback body
});
  1. 이제 데이터를 가져와 이전 단계에서 만든 새 함수를 사용하여 초기 페이지를 렌더링합니다.

onUserChange() 콜백에서 사용자가 로그인한 if 조건을 찾고 if 문 내에 코드를 붙여넣습니다.

src/main.ts

onUserChange(async user => {
      // LEAVE THE EXISTING CODE UNCHANGED HERE
      ...

      if (user) {
       // REPLACE THESE LINES

       // user page
       setUser(user);

       // show loading screen in 500ms
       const timeoutId = setTimeout(() => {
           renderUserPage(user, {
               loading: true,
               tableData: []
           });
       }, 500);

       // get data once if realtime services haven't been loaded
       if (!getState().realtimeServicesLoaded) {
           const tickers = await getTickers(user);
           const tickerData = await getTickerChanges(tickers);
           clearTimeout(timeoutId);
           renderUserPage(user, { tableData: tickerData });
       }

       // subscribe to realtime updates once realtime services are loaded
       loadRealtimeService.then(({ subscribeToTickerChanges }) => {
           unsubscribeTickerChanges = subscribeToTickerChanges(user, stockData => {
               clearTimeout(timeoutId);
               renderUserPage(user, { tableData: stockData })
           });
       });
   } else {
     // DON'T EDIT THIS PART, YET   
   }
}
  1. 로그인한 사용자가 없는 else 블록에서 Firestore Lite를 사용하여 모든 주식의 가격 정보를 가져오고 페이지를 렌더링한 다음 실시간 서비스가 로드되면 가격 변경사항을 수신합니다.

src/main.ts

if (user) {
   // DON'T EDIT THIS PART, WHICH WE JUST CHANGED ABOVE
   ...
} else {
   // REPLACE THESE LINES

   // login page
   setUser(null);

   // show loading screen in 500ms
   const timeoutId = setTimeout(() => {
       renderLoginPage('Landing page', {
           loading: true,
           tableData: []
       });
   }, 500);

   // get data once if realtime services haven't been loaded
   if (!getState().realtimeServicesLoaded) {
       const tickerData = await getAllTickerChanges();
       clearTimeout(timeoutId);
       renderLoginPage('Landing page', { tableData: tickerData });
   }

   // subscribe to realtime updates once realtime services are loaded
   loadRealtimeService.then(({ subscribeToAllTickerChanges }) => {
       unsubscribeAllTickerChanges = subscribeToAllTickerChanges(stockData => {
           clearTimeout(timeoutId);
           renderLoginPage('Landing page', { tableData: stockData })
       });
   });
}

완성된 코드는 src/main.ts를 참조하세요.

앱 작동 확인

  1. npm run build를 실행하여 앱을 다시 빌드합니다.
  2. 브라우저 탭에서 http://localhost:8080을 열거나 기존 탭을 새로고침합니다.

번들 크기 확인하기

  1. Chrome Devtools를 엽니다.
  2. 네트워크 탭으로 전환합니다.
  3. 페이지를 새로고침하여 네트워크 요청 캡처
  4. main.js를 찾아 크기를 확인합니다.
  5. 이제 115KB (34.5KB gzip으로 압축됨)에 불과합니다. 이는 원래 번들 크기인 446KB(138KB gzip으로 압축됨)보다 75% 더 작습니다. 그 결과, 3G 연결에서 사이트 로드 속도가 2초 이상 빨라졌습니다. 덕분에 성능과 사용자 환경이 크게 개선되었습니다.

9ea7398a8c8ef81b.png

8. 축하합니다

축하합니다. 앱을 업그레이드하여 더 작고 빠르게 만들었습니다.

compat 패키지를 사용하여 앱을 하나씩 업그레이드했고 Firestore Lite를 사용하여 초기 페이지 렌더링 속도를 높인 다음 기본 Firestore를 동적으로 로드하여 가격 변경을 스트리밍했습니다.

또한 이 Codelab을 진행하면서 번들 크기를 줄이고 로드 시간을 개선했습니다.

기본.js

리소스 크기 (kb)

gzip으로 압축한 크기 (kb)

로드 시간 (초) (느린 3g 초과)

v8

446

138

4.92

v9 호환성

429

124

4.65

v9 전용 모듈식 인증

348

102

4.2

v9 완전 모듈식

244

74.6

3.66

v9 완전 모듈식 + Firestore Lite

117

34.9

2.88

32a71bd5a774e035.png

지금까지 v8 Firebase JS SDK를 사용하여 새로운 모듈식 JS SDK를 사용하는 웹 앱을 업그레이드하는 데 필요한 주요 단계를 알아봤습니다.

추가 자료

참조 문서