Cloud Firestore 웹 Codelab

1. 개요

목표

이 Codelab에서는 Cloud Firestore 에서 제공하는 레스토랑 추천 웹 앱을 빌드합니다.

img5.png

무엇을 배울 것인가

  • 웹 앱에서 Cloud Firestore에 데이터 읽기 및 쓰기
  • Cloud Firestore 데이터의 변경사항을 실시간으로 수신합니다.
  • Firebase 인증 및 보안 규칙을 사용하여 Cloud Firestore 데이터 보호
  • 복잡한 Cloud Firestore 쿼리 작성

필요한 것

이 Codelab을 시작하기 전에 다음을 설치했는지 확인하세요.

2. Firebase 프로젝트 생성 및 설정

Firebase 프로젝트 만들기

  1. Firebase 콘솔 에서 프로젝트 추가를 클릭한 다음 Firebase 프로젝트 이름을 FriendlyEats 로 지정합니다.

Firebase 프로젝트의 프로젝트 ID를 기억하세요.

  1. 프로젝트 생성 을 클릭합니다.

우리가 빌드할 애플리케이션은 웹에서 사용할 수 있는 몇 가지 Firebase 서비스를 사용합니다.

  • 사용자를 쉽게 식별할 수 있는 Firebase 인증
  • 구조화된 데이터를 클라우드에 저장하고 데이터가 업데이트되면 즉시 알림을 받을 수 있는 Cloud Firestore
  • 정적 자산을 호스팅하고 제공하는 Firebase 호스팅

이 특정 Codelab에서는 이미 Firebase 호스팅을 구성했습니다. 하지만 Firebase 인증 및 Cloud Firestore의 경우 Firebase 콘솔을 사용하여 서비스를 구성하고 활성화하는 과정을 안내해 드립니다.

익명 인증 활성화

인증이 이 Codelab의 초점은 아니지만 앱에 일종의 인증을 포함하는 것이 중요합니다. 익명 로그인을 사용하겠습니다. 즉, 사용자는 메시지를 표시하지 않고 자동으로 로그인됩니다.

익명 로그인을 활성화해야 합니다.

  1. Firebase 콘솔의 왼쪽 탐색 메뉴에서 빌드 섹션을 찾습니다.
  2. 인증 을 클릭한 다음 로그인 방법 탭을 클릭합니다(또는 여기를 클릭하여 바로 이동합니다).
  3. 익명 로그인 공급자를 활성화한 다음 저장을 클릭합니다.

img7.png

이렇게 하면 사용자가 웹 앱에 액세스할 때 애플리케이션이 자동으로 사용자를 로그인할 수 있습니다. 자세한 내용은 익명 인증 설명서를 읽어보세요.

Cloud Firestore 활성화

앱은 Cloud Firestore를 사용하여 레스토랑 정보와 평점을 저장하고 수신합니다.

Cloud Firestore를 활성화해야 합니다. Firebase 콘솔의 빌드 섹션에서 Firestore Database를 클릭합니다. Cloud Firestore 창에서 데이터베이스 생성을 클릭합니다.

Cloud Firestore의 데이터에 대한 액세스는 보안 규칙에 따라 제어됩니다. 이 Codelab의 뒷부분에서 규칙에 대해 자세히 설명하겠지만 시작하려면 먼저 데이터에 대한 몇 가지 기본 규칙을 설정해야 합니다. Firebase 콘솔의 규칙 탭 에서 다음 규칙을 추가한 후 게시를 클릭하세요.

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      //
      // WARNING: These rules are insecure! We will replace them with
      // more secure rules later in the codelab
      //
      allow read, write: if request.auth != null;
    }
  }
}

위의 규칙은 로그인한 사용자로 데이터 액세스를 제한하여 인증되지 않은 사용자가 읽거나 쓰는 것을 방지합니다. 이는 공개 액세스를 허용하는 것보다 낫지만 여전히 안전하지는 않습니다. 나중에 Codelab에서 이러한 규칙을 개선할 예정입니다.

3. 샘플 코드 받기

명령줄에서 GitHub 저장소를 복제합니다.

git clone https://github.com/firebase/friendlyeats-web

샘플 코드는 📁 friendlyeats-web 디렉터리에 복제되어 있어야 합니다. 이제부터 이 디렉터리에서 모든 명령을 실행해야 합니다.

cd friendlyeats-web/vanilla-js

시작 앱 가져오기

IDE(WebStorm, Atom, Sublime, Visual Studio Code...)를 사용하여 📁 friendlyeats-web 디렉터리를 열거나 가져옵니다. 이 디렉터리에는 아직 작동하지 않는 레스토랑 추천 앱으로 구성된 Codelab의 시작 코드가 포함되어 있습니다. 이 Codelab 전체에서 기능이 작동하도록 만들 것이므로 곧 해당 디렉터리의 코드를 수정해야 합니다.

4. Firebase 명령줄 인터페이스 설치

Firebase 명령줄 인터페이스(CLI)를 사용하면 웹 앱을 로컬로 제공하고 웹 앱을 Firebase 호스팅에 배포할 수 있습니다.

  1. 다음 npm 명령을 실행하여 CLI를 설치합니다.
npm -g install firebase-tools
  1. 다음 명령을 실행하여 CLI가 올바르게 설치되었는지 확인하십시오.
firebase --version

Firebase CLI 버전이 v7.4.0 이상인지 확인하세요.

  1. 다음 명령어를 실행하여 Firebase CLI를 승인합니다.
firebase login

앱의 로컬 디렉터리 및 파일에서 Firebase 호스팅에 대한 앱 구성을 가져오도록 웹 앱 템플릿을 설정했습니다. 하지만 이렇게 하려면 앱을 Firebase 프로젝트와 연결해야 합니다.

  1. 명령줄이 앱의 로컬 디렉터리에 액세스하고 있는지 확인하세요.
  2. 다음 명령어를 실행하여 앱을 Firebase 프로젝트와 연결합니다.
firebase use --add
  1. 메시지가 표시되면 프로젝트 ID를 선택한 다음 Firebase 프로젝트에 별칭을 지정합니다.

여러 환경(프로덕션, 스테이징 등)이 있는 경우 별칭이 유용합니다. 하지만 이 Codelab에서는 default 의 별칭을 사용하겠습니다.

  1. 명령줄의 나머지 지침을 따르세요.

5. 로컬 서버 실행

실제로 앱 작업을 시작할 준비가 되었습니다! 앱을 로컬에서 실행해 보겠습니다!

  1. 다음 Firebase CLI 명령어를 실행하세요.
firebase emulators:start --only hosting
  1. 명령줄에 다음 응답이 표시되어야 합니다.
hosting: Local server: http://localhost:5000

우리는 Firebase 호스팅 에뮬레이터를 사용하여 앱을 로컬에서 제공하고 있습니다. 이제 http://localhost:5000 에서 웹 앱을 사용할 수 있습니다.

  1. http://localhost:5000 에서 앱을 엽니다.

Firebase 프로젝트에 연결된 FriendlyEats 사본이 표시됩니다.

앱이 자동으로 Firebase 프로젝트에 연결되었으며 익명 사용자로 자동 로그인되었습니다.

img2.png

6. Cloud Firestore에 데이터 쓰기

이 섹션에서는 앱의 UI를 채울 수 있도록 Cloud Firestore에 일부 데이터를 작성하겠습니다. 이 작업은 Firebase 콘솔을 통해 수동으로 수행할 수 있지만 기본적인 Cloud Firestore 쓰기를 보여주기 위해 앱 자체에서 수행하겠습니다.

데이터 모델

Firestore 데이터는 컬렉션, 문서, 필드, 하위 컬렉션으로 분할됩니다. 우리는 restaurants 이라는 최상위 컬렉션에 각 레스토랑을 문서로 저장할 것입니다.

img3.png

나중에 각 레스토랑 아래의 ratings 라는 하위 컬렉션에 각 리뷰를 저장합니다.

img4.png

Firestore에 레스토랑 추가

우리 앱의 주요 모델 객체는 레스토랑입니다. restaurants 컬렉션에 레스토랑 문서를 추가하는 코드를 작성해 보겠습니다.

  1. 다운로드한 파일에서 scripts/FriendlyEats.Data.js 엽니다.
  2. FriendlyEats.prototype.addRestaurant 함수를 찾으세요.
  3. 전체 함수를 다음 코드로 바꿉니다.

FriendlyEats.Data.js

FriendlyEats.prototype.addRestaurant = function(data) {
  var collection = firebase.firestore().collection('restaurants');
  return collection.add(data);
};

위의 코드는 restaurants 컬렉션에 새 문서를 추가합니다. 문서 데이터는 일반 JavaScript 개체에서 가져옵니다. 먼저 Cloud Firestore 컬렉션 restaurants 에 대한 참조를 가져온 다음 데이터를 add 이를 수행합니다.

레스토랑을 추가하자!

  1. 브라우저에서 FriendlyEats 앱으로 돌아가서 새로 고치세요.
  2. 모의 데이터 추가 를 클릭합니다.

앱은 자동으로 임의의 레스토랑 객체 세트를 생성한 다음 addRestaurant 함수를 호출합니다. 그러나 데이터 검색을 구현해야 하기 때문에 실제 웹 앱에서는 아직 데이터가 표시되지 않습니다 (Codelab의 다음 섹션).

하지만 Firebase 콘솔에서 Cloud Firestore 탭 으로 이동하면 이제 restaurants 컬렉션에 새 문서가 표시됩니다.

img6.png

축하합니다. 웹 앱에서 Cloud Firestore에 데이터를 썼습니다!

다음 섹션에서는 Cloud Firestore에서 데이터를 검색하여 앱에 표시하는 방법을 알아봅니다.

7. Cloud Firestore의 데이터 표시

이 섹션에서는 Cloud Firestore에서 데이터를 검색하여 앱에 표시하는 방법을 알아봅니다. 두 가지 주요 단계는 쿼리를 생성하고 스냅샷 리스너를 추가하는 것입니다. 이 리스너는 쿼리와 일치하는 모든 기존 데이터에 대한 알림을 받고 실시간으로 업데이트를 받습니다.

먼저 필터링되지 않은 기본 레스토랑 목록을 제공하는 쿼리를 작성해 보겠습니다.

  1. scripts/FriendlyEats.Data.js 파일로 돌아갑니다.
  2. FriendlyEats.prototype.getAllRestaurants 함수를 찾으세요.
  3. 전체 함수를 다음 코드로 바꿉니다.

FriendlyEats.Data.js

FriendlyEats.prototype.getAllRestaurants = function(renderer) {
  var query = firebase.firestore()
      .collection('restaurants')
      .orderBy('avgRating', 'desc')
      .limit(50);

  this.getDocumentsInQuery(query, renderer);
};

위 코드에서는 restaurants 이라는 최상위 컬렉션에서 최대 50개의 레스토랑을 검색하는 쿼리를 구성합니다. 레스토랑은 평균 등급(현재 모두 0)을 기준으로 정렬됩니다. 이 쿼리를 선언한 후 데이터 로드 및 렌더링을 담당하는 getDocumentsInQuery() 메서드에 전달합니다.

스냅샷 리스너를 추가하여 이를 수행하겠습니다.

  1. scripts/FriendlyEats.Data.js 파일로 돌아갑니다.
  2. FriendlyEats.prototype.getDocumentsInQuery 함수를 찾으세요.
  3. 전체 함수를 다음 코드로 바꿉니다.

FriendlyEats.Data.js

FriendlyEats.prototype.getDocumentsInQuery = function(query, renderer) {
  query.onSnapshot(function(snapshot) {
    if (!snapshot.size) return renderer.empty(); // Display "There are no restaurants".

    snapshot.docChanges().forEach(function(change) {
      if (change.type === 'removed') {
        renderer.remove(change.doc);
      } else {
        renderer.display(change.doc);
      }
    });
  });
};

위 코드에서 query.onSnapshot 쿼리 결과가 변경될 때마다 콜백을 트리거합니다.

  • 처음에는 쿼리의 전체 결과 집합, 즉 Cloud Firestore의 전체 restaurants 컬렉션을 사용하여 콜백이 트리거됩니다. 그런 다음 모든 개별 문서를 renderer.display 함수에 전달합니다.
  • 문서가 삭제되면 change.type removed 과 같습니다. 따라서 이 경우 UI에서 레스토랑을 제거하는 함수를 호출하겠습니다.

이제 두 가지 방법을 모두 구현했으므로 앱을 새로 고치고 앞서 Firebase 콘솔에서 본 레스토랑이 이제 앱에 표시되는지 확인합니다. 이 섹션을 성공적으로 완료했다면 이제 앱이 Cloud Firestore를 사용하여 데이터를 읽고 쓰는 중입니다!

레스토랑 목록이 변경되면 이 리스너가 자동으로 계속 업데이트됩니다. Firebase 콘솔로 이동하여 수동으로 레스토랑을 삭제하거나 이름을 변경해 보세요. 변경사항이 사이트에 즉시 표시되는 것을 확인할 수 있습니다!

img5.png

8. 데이터 가져오기()

지금까지 onSnapshot 사용하여 실시간으로 업데이트를 검색하는 방법을 살펴보았습니다. 그러나 그것이 항상 우리가 원하는 것은 아닙니다. 때로는 데이터를 한 번만 가져오는 것이 더 합리적일 때도 있습니다.

우리는 사용자가 앱에서 특정 레스토랑을 클릭할 때 트리거되는 메서드를 구현하려고 합니다.

  1. scripts/FriendlyEats.Data.js 파일로 돌아갑니다.
  2. FriendlyEats.prototype.getRestaurant 함수를 찾으세요.
  3. 전체 함수를 다음 코드로 바꿉니다.

FriendlyEats.Data.js

FriendlyEats.prototype.getRestaurant = function(id) {
  return firebase.firestore().collection('restaurants').doc(id).get();
};

이 방법을 구현한 후에는 각 레스토랑의 페이지를 볼 수 있습니다. 목록에서 레스토랑을 클릭하면 레스토랑의 세부정보 페이지가 표시됩니다.

img1.png

지금은 Codelab의 후반부에 등급 추가를 구현해야 하므로 등급을 추가할 수 없습니다.

9. 데이터 정렬 및 필터링

현재 우리 앱에는 레스토랑 목록이 표시되지만 사용자가 필요에 따라 필터링할 수 있는 방법은 없습니다. 이 섹션에서는 Cloud Firestore의 고급 쿼리를 사용하여 필터링을 활성화합니다.

다음은 모든 Dim Sum 레스토랑을 가져오는 간단한 쿼리의 예입니다.

var filteredQuery = query.where('category', '==', 'Dim Sum')

이름에서 알 수 있듯이 where() 메소드는 필드가 우리가 설정한 제한 사항을 충족하는 컬렉션의 멤버만 다운로드하도록 쿼리를 수행합니다. 이 경우 category Dim Sum 인 레스토랑만 다운로드됩니다.

우리 앱에서 사용자는 여러 필터를 연결하여 "샌프란시스코 피자" 또는 "인기 순으로 주문한 로스앤젤레스 해산물"과 같은 특정 쿼리를 생성할 수 있습니다.

사용자가 선택한 여러 기준에 따라 레스토랑을 필터링하는 쿼리를 작성하는 방법을 만들어보겠습니다.

  1. scripts/FriendlyEats.Data.js 파일로 돌아갑니다.
  2. FriendlyEats.prototype.getFilteredRestaurants 함수를 찾으세요.
  3. 전체 함수를 다음 코드로 바꿉니다.

FriendlyEats.Data.js

FriendlyEats.prototype.getFilteredRestaurants = function(filters, renderer) {
  var query = firebase.firestore().collection('restaurants');

  if (filters.category !== 'Any') {
    query = query.where('category', '==', filters.category);
  }

  if (filters.city !== 'Any') {
    query = query.where('city', '==', filters.city);
  }

  if (filters.price !== 'Any') {
    query = query.where('price', '==', filters.price.length);
  }

  if (filters.sort === 'Rating') {
    query = query.orderBy('avgRating', 'desc');
  } else if (filters.sort === 'Reviews') {
    query = query.orderBy('numRatings', 'desc');
  }

  this.getDocumentsInQuery(query, renderer);
};

위의 코드는 여러 개의 where 필터와 단일 orderBy 절을 추가하여 사용자 입력을 기반으로 복합 쿼리를 작성합니다. 이제 쿼리는 사용자의 요구 사항과 일치하는 레스토랑만 반환합니다.

브라우저에서 FriendlyEats 앱을 새로 고친 다음 가격, 도시, 카테고리별로 필터링할 수 있는지 확인하세요. 테스트하는 동안 브라우저의 JavaScript 콘솔에 다음과 같은 오류가 표시됩니다.

The query requires an index. You can create it here: https://console.firebase.google.com/project/project-id/database/firestore/indexes?create_composite=...

이러한 오류는 Cloud Firestore가 대부분의 복합 쿼리에 색인을 요구하기 때문에 발생합니다. 쿼리에 색인을 요구하면 Cloud Firestore가 대규모로 빠르게 유지됩니다.

오류 메시지에서 링크를 열면 올바른 매개변수가 채워진 Firebase 콘솔의 색인 생성 UI가 자동으로 열립니다. 다음 섹션에서는 이 애플리케이션에 필요한 색인을 작성하고 배포하겠습니다.

10. 인덱스 배포

앱의 모든 경로를 탐색하고 각 색인 생성 링크를 따르지 않으려면 Firebase CLI를 사용하여 한 번에 여러 색인을 쉽게 배포할 수 있습니다.

  1. 앱의 다운로드된 로컬 디렉터리에서 firestore.indexes.json 파일을 찾을 수 있습니다.

이 파일은 가능한 모든 필터 조합에 필요한 모든 색인을 설명합니다.

Firestore.indexes.json

{
 "indexes": [
   {
     "collectionGroup": "restaurants",
     "queryScope": "COLLECTION",
     "fields": [
       { "fieldPath": "city", "order": "ASCENDING" },
       { "fieldPath": "avgRating", "order": "DESCENDING" }
     ]
   },

   ...

 ]
}
  1. 다음 명령을 사용하여 이러한 인덱스를 배포합니다.
firebase deploy --only firestore:indexes

몇 분 후에 인덱스가 활성화되고 오류 메시지가 사라집니다.

11. 트랜잭션에 데이터 쓰기

이 섹션에서는 사용자가 레스토랑에 리뷰를 제출하는 기능을 추가하겠습니다. 지금까지 우리의 모든 쓰기 작업은 원자적이고 상대적으로 단순했습니다. 그 중 오류가 발생한 경우 사용자에게 다시 시도하라는 메시지를 표시하거나 앱이 자동으로 쓰기를 다시 시도할 것입니다.

우리 앱에는 레스토랑에 대한 평가를 추가하려는 사용자가 많으므로 여러 읽기 및 쓰기를 조정해야 합니다. 먼저 리뷰 자체를 제출해야 하며, 그런 다음 레스토랑의 평점 countaverage rating 업데이트해야 합니다. 이들 중 하나가 실패하고 다른 하나는 실패하면 데이터베이스의 한 부분에 있는 데이터가 다른 부분의 데이터와 일치하지 않는 일관성 없는 상태가 됩니다.

다행히 Cloud Firestore는 단일 원자적 작업으로 여러 읽기 및 쓰기를 수행할 수 있는 트랜잭션 기능을 제공하여 데이터의 일관성을 유지합니다.

  1. scripts/FriendlyEats.Data.js 파일로 돌아갑니다.
  2. FriendlyEats.prototype.addRating 함수를 찾으세요.
  3. 전체 함수를 다음 코드로 바꿉니다.

FriendlyEats.Data.js

FriendlyEats.prototype.addRating = function(restaurantID, rating) {
  var collection = firebase.firestore().collection('restaurants');
  var document = collection.doc(restaurantID);
  var newRatingDocument = document.collection('ratings').doc();

  return firebase.firestore().runTransaction(function(transaction) {
    return transaction.get(document).then(function(doc) {
      var data = doc.data();

      var newAverage =
          (data.numRatings * data.avgRating + rating.rating) /
          (data.numRatings + 1);

      transaction.update(document, {
        numRatings: data.numRatings + 1,
        avgRating: newAverage
      });
      return transaction.set(newRatingDocument, rating);
    });
  });
};

위 블록에서는 레스토랑 문서의 avgRatingnumRatings 숫자 값을 업데이트하는 트랜잭션을 트리거합니다. 동시에 ratings 하위 컬렉션에 새 rating 추가합니다.

12. 데이터 보안

이 Codelab을 시작할 때 우리는 읽기 또는 쓰기가 가능하도록 데이터베이스를 완전히 열도록 앱의 보안 규칙을 설정했습니다. 실제 애플리케이션에서는 바람직하지 않은 데이터 액세스나 수정을 방지하기 위해 훨씬 더 세분화된 규칙을 설정하려고 합니다.

  1. Firebase 콘솔의 빌드 섹션에서 Firestore Database를 클릭합니다.
  2. Cloud Firestore 섹션에서 규칙 탭을 클릭하세요(또는 여기를 클릭하여 바로 이동하세요).
  3. 기본값을 다음 규칙으로 바꾼 다음 게시를 클릭합니다.

Firestore.rules

rules_version = '2';
service cloud.firestore {

  // Determine if the value of the field "key" is the same
  // before and after the request.
  function unchanged(key) {
    return (key in resource.data) 
      && (key in request.resource.data) 
      && (resource.data[key] == request.resource.data[key]);
  }

  match /databases/{database}/documents {
    // Restaurants:
    //   - Authenticated user can read
    //   - Authenticated user can create/update (for demo purposes only)
    //   - Updates are allowed if no fields are added and name is unchanged
    //   - Deletes are not allowed (default)
    match /restaurants/{restaurantId} {
      allow read: if request.auth != null;
      allow create: if request.auth != null;
      allow update: if request.auth != null
                    && (request.resource.data.keys() == resource.data.keys()) 
                    && unchanged("name");
      
      // Ratings:
      //   - Authenticated user can read
      //   - Authenticated user can create if userId matches
      //   - Deletes and updates are not allowed (default)
      match /ratings/{ratingId} {
        allow read: if request.auth != null;
        allow create: if request.auth != null
                      && request.resource.data.userId == request.auth.uid;
      }
    }
  }
}

이러한 규칙은 클라이언트가 안전한 변경만 수행할 수 있도록 액세스를 제한합니다. 예를 들어:

  • 레스토랑 문서를 업데이트하면 등급만 변경할 수 있으며 이름이나 기타 변경 불가능한 데이터는 변경할 수 없습니다.
  • 평점은 사용자 ID가 로그인한 사용자와 일치하는 경우에만 생성될 수 있으므로 스푸핑을 방지할 수 있습니다.

Firebase 콘솔을 사용하는 대신 Firebase CLI를 사용하여 Firebase 프로젝트에 규칙을 배포할 수 있습니다. 작업 디렉터리의 firestore.rules 파일에는 이미 위의 규칙이 포함되어 있습니다. Firebase 콘솔을 사용하지 않고 로컬 파일 시스템에서 이러한 규칙을 배포하려면 다음 명령을 실행합니다.

firebase deploy --only firestore:rules

13. 결론

이 Codelab에서는 Cloud Firestore를 사용하여 기본 및 고급 읽기 및 쓰기를 수행하는 방법과 보안 규칙을 사용하여 데이터 액세스를 보호하는 방법을 배웠습니다. Quickstarts-js 저장소 에서 전체 솔루션을 찾을 수 있습니다.

Cloud Firestore에 대해 자세히 알아보려면 다음 리소스를 방문하세요.

14. [선택사항] 앱체크로 시행

Firebase 앱 체크는 앱에 대한 원치 않는 트래픽을 검증하고 방지하여 보호 기능을 제공합니다. 이 단계에서는 reCAPTCHA Enterprise를 사용하여 앱 체크를 추가하여 서비스에 대한 액세스를 보호합니다.

먼저 앱 체크와 reCaptcha를 활성화해야 합니다.

reCaptcha Enterprise 활성화

  1. Cloud 콘솔의 보안 아래에서 reCaptcha Enterprise를 찾아 선택합니다.
  2. 메시지에 따라 서비스를 활성화하고 키 생성 을 클릭합니다.
  3. 메시지에 따라 표시 이름을 입력하고 플랫폼 유형으로 웹사이트를 선택합니다.
  4. 배포된 URL을 도메인 목록 에 추가하고 '체크박스 챌린지 사용' 옵션이 선택 해제되어 있는지 확인하세요.
  5. Create Key 를 클릭하고 생성된 키를 안전한 보관을 위해 어딘가에 저장합니다. 이 단계의 뒷부분에서 필요합니다.

앱 체크 활성화

  1. Firebase 콘솔의 왼쪽 패널에서 빌드 섹션을 찾습니다.
  2. App Check 를 클릭한 다음 시작하기 버튼을 클릭합니다(또는 콘솔 로 직접 리디렉션).
  3. 등록을 클릭하고 메시지가 표시되면 reCaptcha Enterprise 키를 입력한 다음 저장을 클릭합니다.
  4. API 보기에서 Storage를 선택하고 Enforce를 클릭합니다. Cloud Firestore 에도 동일한 작업을 수행합니다.

이제 앱 체크가 시행되어야 합니다! 앱을 새로 고치고 레스토랑을 생성/조회해 보세요. 다음과 같은 오류 메시지가 표시됩니다.

Uncaught Error in snapshot listener: FirebaseError: [code=permission-denied]: Missing or insufficient permissions.

이는 앱 체크가 기본적으로 확인되지 않은 요청을 차단한다는 의미입니다. 이제 앱에 유효성 검사를 추가해 보겠습니다.

FriendlyEats.View.js 파일로 이동하여 initAppCheck 함수를 업데이트하고 reCaptcha 키를 추가하여 앱 체크를 초기화하세요.

FriendlyEats.prototype.initAppCheck = function() {
    var appCheck = firebase.appCheck();
    appCheck.activate(
    new firebase.appCheck.ReCaptchaEnterpriseProvider(
      /* reCAPTCHA Enterprise site key */
    ),
    true // Set to true to allow auto-refresh.
  );
};

appCheck 인스턴스는 키를 사용하여 ReCaptchaEnterpriseProvider 로 초기화되며 isTokenAutoRefreshEnabled 사용하면 앱에서 토큰이 자동으로 새로 고쳐질 수 있습니다.

로컬 테스트를 활성화하려면 FriendlyEats.js 파일에서 앱이 초기화되는 섹션을 찾아 FriendlyEats.prototype.initAppCheck 함수에 다음 줄을 추가하세요.

if(isLocalhost) {
  self.FIREBASE_APPCHECK_DEBUG_TOKEN = true;
}

그러면 다음과 유사하게 로컬 웹 앱의 콘솔에 디버그 토큰이 기록됩니다.

App Check debug token: 8DBDF614-649D-4D22-B0A3-6D489412838B. You will need to add it to your app's App Check settings in the Firebase console for it to work.

이제 Firebase 콘솔에서 앱 체크의 앱 보기 로 이동합니다.

더보기 메뉴를 클릭하고 디버그 토큰 관리 를 선택합니다.

그런 다음 디버그 토큰 추가를 클릭하고 메시지가 표시되면 콘솔에서 디버그 토큰을 붙여넣습니다.

축하해요! 이제 앱 체크가 앱에서 작동할 것입니다.