Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

CDN에서 번들로 제공되는 Firestore 콘텐츠 제공

많은 애플리케이션이 첫 페이지 로드 시 모든 사용자에게 동일한 콘텐츠를 제공합니다. 예를 들어 뉴스 사이트에 최신 기사가 표시되거나 전자 상거래 사이트에 베스트셀러 항목이 표시될 수 있습니다.

이 콘텐츠가 Cloud Firestore에서 제공되는 경우 각 사용자는 애플리케이션을 로드할 때 동일한 결과에 대해 새 쿼리를 실행합니다. 이러한 결과는 사용자 간에 캐시되지 않기 때문에 응용 프로그램은 필요한 것보다 느리고 비용이 많이 듭니다.

솔루션: 번들

Cloud Firestore 번들은 Firebase Admin SDK를 사용하여 백엔드의 공통 쿼리 결과에서 데이터 번들을 조합하고 CDN에 캐시된 이러한 미리 계산된 blob을 제공할 수 있습니다. 이렇게 하면 사용자에게 훨씬 더 빠른 첫 번째 로드 환경을 제공하고 Cloud Firestore 쿼리 비용을 줄일 수 있습니다.

이 가이드에서는 Cloud Functions를 사용하여 번들을 생성하고 Firebase 호스팅을 사용하여 번들 콘텐츠를 동적으로 캐시하고 제공합니다. 번들에 대한 자세한 정보는에서 사용할 수 있습니다 가이드 .

먼저 50개의 최신 "이야기"를 쿼리하고 결과를 번들로 제공하는 간단한 공개 HTTP 함수를 만듭니다.

노드.js
exports.createBundle = functions.https.onRequest(async (request, response) => {
  // Query the 50 latest stories
  const latestStories = await db.collection('stories')
    .orderBy('timestamp', 'desc')
    .limit(50)
    .get();

  // Build the bundle from the query results
  const bundleBuffer = db.bundle('latest-stories')
    .add('latest-stories-query', latestStories)
    .build();

  // Cache the response for up to 5 minutes;
  // see https://firebase.google.com/docs/hosting/manage-cache
  response.set('Cache-Control', 'public, max-age=300, s-maxage=600');

  response.end(bundleBuffer);
});
      
자바

package com.example;

import com.google.auth.oauth2.GoogleCredentials;
import com.google.cloud.firestore.Firestore;
import com.google.cloud.firestore.FirestoreBundle;
import com.google.cloud.firestore.Query.Direction;
import com.google.cloud.firestore.QuerySnapshot;
import com.google.cloud.functions.HttpFunction;
import com.google.cloud.functions.HttpRequest;
import com.google.cloud.functions.HttpResponse;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.cloud.FirestoreClient;
import java.io.BufferedWriter;
import java.io.IOException;

public class ExampleFunction implements HttpFunction {

  public static FirebaseApp initializeFirebase() throws IOException {
    if (FirebaseApp.getApps().isEmpty()) {
      FirebaseOptions options = FirebaseOptions.builder()
          .setCredentials(GoogleCredentials.getApplicationDefault())
          .setProjectId("YOUR-PROJECT-ID")
          .build();

      FirebaseApp.initializeApp(options);
    }

    return FirebaseApp.getInstance();
  }

  @Override
  public void service(HttpRequest request, HttpResponse response) throws Exception {
    // Get a Firestore instance
    FirebaseApp app = initializeFirebase();
    Firestore db = FirestoreClient.getFirestore(app);

    // Query the 50 latest stories
    QuerySnapshot latestStories = db.collection("stories")
        .orderBy("timestamp", Direction.DESCENDING)
        .limit(50)
        .get()
        .get();

    // Build the bundle from the query results
    FirestoreBundle bundle = db.bundleBuilder("latest-stores")
        .add("latest-stories-query", latestStories)
        .build();

    // Cache the response for up to 5 minutes
    // see https://firebase.google.com/docs/hosting/manage-cache
    response.appendHeader("Cache-Control", "public, max-age=300, s-maxage=600");

    // Write the bundle to the HTTP response
    BufferedWriter writer = response.getWriter();
    writer.write(new String(bundle.toByteBuffer().array()));
  }
}
      

다음 구성 중포 기지는 봉사하고 수정하여이 클라우드 기능을 캐시 호스팅 firebase.json . 이 구성을 사용하면 Firebase 호스팅 CDN이 Cloud 함수에서 설정한 캐시 설정에 따라 번들 콘텐츠를 제공합니다. 캐시가 만료되면 기능을 다시 트리거하여 콘텐츠를 새로 고칩니다.

firebase.json
{
  "hosting": {
    // ...
    "rewrites": [{
      "source": "/createBundle",
      "function": "createBundle"
    }]
  },
  // ...
}

마지막으로 웹 애플리케이션에서 CDN에서 번들 콘텐츠를 가져와 Firestore SDK에 로드합니다.

// If you are using module bundlers.
import firebase from "firebase/app";
import "firebase/firestore";
import "firebase/firestore/bundle" // This line enables bundle loading as a side effect.

async function fetchFromBundle() {
  // Fetch the bundle from Firebase Hosting, if the CDN cache is hit the 'X-Cache'
  // response header will be set to 'HIT'
  const resp = await fetch('/createBundle');

  // Load the bundle contents into the Firestore SDK
  await db.loadBundle(resp.body);

  // Query the results from the cache
  // Note: omitting "source: cache" will query the Firestore backend.
  
  const query = await db.namedQuery('latest-stories-query');
  const storiesSnap = await query.get({ source: 'cache' });

  // Use the results
  // ...
}

예상 절감액

하루에 100,000명의 사용자를 확보하고 각 사용자가 초기 로드 시 동일한 50개의 주요 뉴스를 로드하는 뉴스 웹사이트를 생각해 보십시오. 캐싱이 없으면 Cloud Firestore에서 하루에 50 x 100,000 = 5,000,000 문서 읽기가 발생합니다.

이제 사이트가 위의 기술을 채택하고 최대 5분 동안 해당 50개의 결과를 캐시한다고 가정합니다. 따라서 모든 사용자에 대한 쿼리 결과를 로드하는 대신 결과가 시간당 정확히 12번 로드됩니다. 얼마나 많은 사용자가 사이트에 도착하든 Cloud Firestore에 대한 쿼리 수는 동일하게 유지됩니다. 이 페이지는 5,000,000 문서 읽기 대신 12 x 24 x 50 = 14,400 문서 읽기를 사용합니다. Firebase 호스팅 및 Cloud Functions에 대한 약간의 추가 비용은 Cloud Firestore 비용 절감으로 쉽게 상쇄됩니다.

개발자는 비용 절감의 혜택을 받지만 가장 큰 수혜자는 사용자입니다. Cloud Firestore가 아닌 Firebase 호스팅 CDN에서 이러한 50개의 문서를 로드하면 페이지의 콘텐츠 로드 시간을 100-200ms 이상 단축할 수 있습니다. 연구에 따르면 빠른 페이지는 더 행복한 사용자를 의미합니다.