Cloud Firestore 웹 Codelab

1. 개요

목표

이 코드 랩, 당신은에 의해 구동 레스토랑 추천 웹 응용 프로그램 구축 할 수 있습니다 클라우드 경우 FireStore을 .

img5.png

배울 내용

  • 웹 앱에서 Cloud Firestore로 데이터 읽기 및 쓰기
  • Cloud Firestore 데이터의 변화를 실시간으로 들어보세요.
  • Firebase 인증 및 보안 규칙을 사용하여 Cloud Firestore 데이터 보호
  • 복잡한 Cloud Firestore 쿼리 작성

필요한 것

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

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

Firebase 프로젝트 만들기

  1. 에서 중포 기지 콘솔 , 다음, 추가 프로젝트를 클릭 중포 기지 프로젝트 FriendlyEats의 이름을 지정합니다.

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

  1. 프로젝트 만들기를 클릭합니다.

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

  • 중포 기지 인증은 쉽게 사용자를 식별하는
  • 클라우드 경우 FireStore는 클라우드에 구조화 된 데이터를 저장하고 데이터가 업데이트 될 때 즉시 알림을받을 수 있습니다
  • 중포 기지의 고정 자산을 호스트에 호스팅 및 제공

이 특정 코드랩에서는 이미 Firebase 호스팅을 구성했습니다. 그러나 Firebase Auth 및 Cloud Firestore의 경우 Firebase 콘솔을 사용하여 서비스를 구성하고 활성화하는 방법을 안내해 드립니다.

익명 인증 사용

인증이 이 코드랩의 초점은 아니지만 앱에 어떤 형태의 인증을 포함하는 것이 중요합니다. 우리는 익명 로그인을 사용합니다 - 사용자가 자동으로 입력하지 않고도 로그인이 계속 유지 될 수 있음을 의미한다.

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

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

img7.png

이렇게 하면 사용자가 웹 앱에 액세스할 때 애플리케이션이 자동으로 로그인할 수 있습니다. 읽기 자유롭게 익명 인증 설명서를 자세히 알아.

Cloud Firestore 사용

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

Cloud Firestore를 사용 설정해야 합니다. 중포 기지 콘솔의 빌드 섹션의 경우 FireStore 데이터베이스를 클릭합니다. 클라우드 경우 FireStore 창에서 데이터베이스 만들기를 클릭합니다.

Cloud Firestore의 데이터에 대한 액세스는 보안 규칙에 의해 제어됩니다. 이 코드랩의 뒷부분에서 규칙에 대해 더 자세히 설명하겠지만 먼저 시작하려면 데이터에 몇 가지 기본 규칙을 설정해야 합니다. 에서 규칙 탭 중포 기지 콘솔의 다음과 같은 규칙을 추가 한 다음 게시를 클릭합니다.

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

시작 앱 가져오기

당신의 IDE (WebStorm, 아톰, 숭고한, 비주얼 스튜디오 코드 ...) 공개를 사용하거나 📁 가져 friendlyeats-web 디렉토리. 이 디렉토리에는 아직 기능하지 않는 레스토랑 추천 앱으로 구성된 코드랩의 시작 코드가 포함되어 있습니다. 이 코드랩 전체에서 작동하도록 만들 것이므로 곧 해당 디렉토리에서 코드를 편집해야 합니다.

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를 선택합니다.

별칭은 여러 환경(프로덕션, 스테이징 등)이 있는 경우에 유용합니다. 그러나,이 코드 랩에 대한, 그냥의 별칭 사용할 수 있도록 default .

  1. 명령줄의 나머지 지침을 따릅니다.

5. 로컬 서버 실행

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

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

우리는 사용하고있는 중포 기지 호스팅 로컬로 우리의 응용 프로그램을 제공하기 위해 에뮬레이터를. 웹 응용 프로그램은 지금부터 사용할 수 있어야 에 http : // localhost를 : 5000 .

  1. 귀하의 앱을 실행 에 http : // localhost를 : 5000 .

Firebase 프로젝트에 연결된 FriendlyEats 사본이 표시되어야 합니다.

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

img2.png

6. Cloud Firestore에 데이터 쓰기

이 섹션에서는 앱의 UI를 채울 수 있도록 일부 데이터를 Cloud Firestore에 씁니다. 이것은을 통해 수동으로 수행 할 수 있습니다 중포 기지 콘솔 , 그러나 우리는 기본적인 클라우드 경우 FireStore 기록을 보여 앱 자체에서 할 수 있습니다.

데이터 모델

Firestore 데이터는 컬렉션, 문서, 필드 및 하위 컬렉션으로 분할됩니다. (이)라는 최상위 모음의 문서로 각 레스토랑 저장할 restaurants .

img3.png

나중에, 우리는라는 하위 컬렉션의 각 리뷰 저장할 수 있습니다 ratings 각 레스토랑에서합니다.

img4.png

Firestore에 레스토랑 추가

우리 앱의 주요 모델 객체는 레스토랑입니다. 하자가에 레스토랑 문서에 추가하는 코드 작성 restaurants 모음.

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

프렌들리이츠.Data.js

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

위의 코드는에 새 문서 추가 restaurants 수집. 문서 데이터는 일반 JavaScript 개체에서 가져옵니다. 우리는 먼저 클라우드 경우 FireStore 수집에 대한 참조를 얻는하여이 작업을 수행 restaurants 다음 add 데이터를 보내고 '.

레스토랑을 추가하자!

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

응용 프로그램은 자동으로 다음, 레스토랑 객체의 임의의 집합을 생성하여 호출 addRestaurant 기능을. 우리는 여전히 데이터합니다 (코드 랩의 다음 섹션을) 검색 구현해야하기 때문에, 아직 실제 웹 응용 프로그램의 데이터를 볼 수 없습니다.

당신이로 이동하는 경우 클라우드 경우 FireStore 탭 중포 기지 콘솔하지만, 이제 새로운 문서가 나타납니다 restaurants 모음!

img6.png

축하합니다. 웹 앱에서 Cloud Firestore에 데이터를 작성했습니다.

다음 섹션에서는 Cloud Firestore에서 데이터를 검색하고 앱에 표시하는 방법을 배웁니다.

7. Cloud Firestore의 데이터 표시

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

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

  1. 파일로 이동 scripts/FriendlyEats.Data.js .
  2. 함수 찾기 FriendlyEats.prototype.getAllRestaurants .
  3. 전체 함수를 다음 코드로 바꿉니다.

프렌들리이츠.Data.js

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

  this.getDocumentsInQuery(query, renderer);
};

위의 코드에서, 우리는 최상위 모음의 이름에서 50 개 레스토랑까지 검색하는 쿼리 구성 restaurants 평균 평점 (현재 모두 0)에 의해 정렬됩니다. 우리는이 쿼리를 선언 한 후, 우리는 그것을 통과 getDocumentsInQuery() 로드 및 데이터의 렌더링을하는 방법.

스냅샷 수신기를 추가하여 이 작업을 수행합니다.

  1. 파일로 이동 scripts/FriendlyEats.Data.js .
  2. 함수 찾기 FriendlyEats.prototype.getDocumentsInQuery .
  3. 전체 함수를 다음 코드로 바꿉니다.

프렌들리이츠.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 쿼리의 결과에 변화가있을 때마다 콜백을 트리거합니다.

  • 전체 의미 - 처음, 콜백은 쿼리의 전체 결과 집합으로 트리거 restaurants 클라우드 경우 FireStore에서 수집. 그런 다음에 모든 개별 문서 전달 renderer.display 기능을.
  • 문서가 삭제되면 change.type 같음 removed . 따라서 이 경우 UI에서 레스토랑을 제거하는 함수를 호출합니다.

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

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

img5.png

8. Get() 데이터

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

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

  1. 귀하의 파일로 이동 scripts/FriendlyEats.Data.js .
  2. 함수 찾기 FriendlyEats.prototype.getRestaurant .
  3. 전체 함수를 다음 코드로 바꿉니다.

프렌들리이츠.Data.js

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

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

img1.png

현재로서는 나중에 코드랩에서 평가 추가를 구현해야 하므로 평가를 추가할 수 없습니다.

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. 전체 함수를 다음 코드로 바꿉니다.

프렌들리이츠.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/.../database/firestore/indexes?create_index=...

이러한 오류는 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. 전체 함수를 다음 코드로 바꿉니다.

프렌들리이츠.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 레스토랑 문서에. 동시에, 우리는 새로운 추가 rating 받는 ratings 하위 컬렉션을.

12. 데이터 보호

이 코드랩의 시작 부분에서 앱의 보안 규칙을 설정하여 데이터베이스를 모든 읽기 또는 쓰기에 완전히 개방합니다. 실제 응용 프로그램에서는 바람직하지 않은 데이터 액세스 또는 수정을 방지하기 위해 훨씬 더 세분화된 규칙을 설정하려고 합니다.

  1. 중포 기지 콘솔의 빌드 섹션의 경우 FireStore 데이터베이스를 클릭합니다.
  2. 클라우드 경우 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. 결론

이 코드랩에서는 Cloud Firestore로 기본 및 고급 읽기 및 쓰기를 수행하는 방법과 보안 규칙으로 데이터 액세스를 보호하는 방법을 배웠습니다. 당신은에서 전체 솔루션을 찾을 수 있습니다 빠른 시작-JS 저장소를 .

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