Cloud Firestore Android Codelab

1. 개요

목표

이 코드랩에서는 Cloud Firestore가 지원하는 Android에서 레스토랑 추천 앱을 빌드합니다. 다음 방법을 배우게 됩니다.

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

전제 조건

이 코드랩을 시작하기 전에 다음 사항을 확인하세요.

  • 안드로이드 스튜디오 4.0 이상
  • 안드로이드 에뮬레이터
  • Node.js를 버전 10 이상
  • Java 버전 8 이상

2. Firebase 프로젝트 생성

  1. 에 로그인 중포 기지 콘솔 Google 계정으로.
  2. 에서 중포 기지 콘솔 , 추가 프로젝트를 클릭합니다.
  3. 아래 화면 캡처에 나타낸 바와 같이, (예를 들어, "친절한 먹"), 당신의 중포 기지 프로젝트의 이름을 입력하고 계속을 클릭합니다.

9d2f625aebcab6af.png

  1. 이 코드랩에서는 선택이 중요하지 않으므로 Google Analytics를 활성화하라는 메시지가 표시될 수 있습니다.
  2. 1분 정도 지나면 Firebase 프로젝트가 준비됩니다. 계속을 클릭합니다.

3. 샘플 프로젝트 설정

코드 다운로드

다음 명령을 실행하여 이 코드랩의 샘플 코드를 복제합니다. 이것은라는 폴더 생성됩니다 friendlyeats-android 사용자의 컴퓨터에를 :

$ git clone https://github.com/firebase/friendlyeats-android

컴퓨터에 git이 없는 경우 GitHub에서 직접 코드를 다운로드할 수도 있습니다.

안드로이드 스튜디오에 프로젝트를 가져옵니다. 당신은 아마 어쩌면 약간의 편집 오류 또는 누락에 대한 경고가 표시됩니다 google-services.json 파일을. 다음 섹션에서 이를 수정하겠습니다.

Firebase 구성 추가

  1. 에서 중포 기지 콘솔 , 왼쪽 탐색 메뉴에서 프로젝트 개요를 선택합니다. 플랫폼을 선택하기 위해 안드로이드 버튼을 클릭합니다. 패키지 이름을 사용하라는 메시지가 표시되면 com.google.firebase.example.fireeats

73d151ed16016421.png

  1. 등록 앱을 클릭하고 다운로드 지시에 따라 google-services.json 파일을, 그리고로 이동 app/ 샘플 코드의 폴더. 그런 다음 다음을 클릭합니다.

4. Firebase 에뮬레이터 설정

이 코드 랩에서 당신은 사용할 것이다 중포 기지 에뮬레이터 스위트 룸을 로컬 클라우드 경우 FireStore 및 기타 중포 기지 서비스를 에뮬레이트 할 수 있습니다. 이는 앱을 빌드할 수 있는 안전하고 빠른 무료 로컬 개발 환경을 제공합니다.

Firebase CLI 설치

먼저 설치해야합니다 중포 기지 CLI를 . 이 작업을 수행하는 가장 쉬운 방법은 사용하는 것입니다 npm :

npm install -g firebase-tools

당신이없는 경우 npm 하거나 오류가 발생, 읽기 설치 지침을 해당 플랫폼 용 독립형 바이너리를 얻을.

당신은 CLI를 설치 한 후, 실행 firebase --version 버전보고해야한다 9.0.0 이상 :

$ firebase --version
9.0.0

로그인

실행 firebase login Google 계정에 CLI를 연결합니다. 로그인 프로세스를 완료하기 위해 새 브라우저 창이 열립니다. 이전에 Firebase 프로젝트를 만들 때 사용한 것과 동일한 계정을 선택해야 합니다.

내에서 friendlyeats-android 폴더 실행 firebase use --add 당신의 중포 기지 프로젝트에 지역 프로젝트를 연결할 수 있습니다. 이전에 만든 프로젝트를 선택 프롬프트에 따라 경우 별칭 입력 선택하라는 메시지가 default .

5. 앱 실행

이제 Firebase Emulator Suite와 FriendlyEats Android 앱을 처음으로 실행할 차례입니다.

에뮬레이터 실행

내에서 터미널에서 friendlyeats-android 디렉토리의 실행 firebase emulators:start 중포 기지 에뮬레이터를 시작합니다. 다음과 같은 로그가 표시되어야 합니다.

$ firebase emulators:start
i  emulators: Starting emulators: auth, firestore
i  firestore: Firestore Emulator logging to firestore-debug.log
i  ui: Emulator UI logging to ui-debug.log

┌─────────────────────────────────────────────────────────────┐
│ ✔  All emulators ready! It is now safe to connect your app. │
│ i  View Emulator UI at http://localhost:4000                │
└─────────────────────────────────────────────────────────────┘

┌────────────────┬────────────────┬─────────────────────────────────┐
│ Emulator       │ Host:Port      │ View in Emulator UI             │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Authentication │ localhost:9099 │ http://localhost:4000/auth      │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Firestore      │ localhost:8080 │ http://localhost:4000/firestore │
└────────────────┴────────────────┴─────────────────────────────────┘
  Emulator Hub running at localhost:4400
  Other reserved ports: 4500

Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.

이제 컴퓨터에서 실행 중인 완전한 로컬 개발 환경이 생겼습니다! 나머지 Codelab에 대해 이 명령을 계속 실행해야 합니다. Android 앱은 에뮬레이터에 연결해야 합니다.

에뮬레이터에 앱 연결

파일 열기 FirebaseUtil.java 안드로이드 스튜디오에서입니다. 이 파일에는 Firebase SDK를 머신에서 실행되는 로컬 에뮬레이터에 연결하는 로직이 포함되어 있습니다.

파일 상단에서 다음 줄을 검사합니다.

    /** Use emulators only in debug builds **/
    private static final boolean sUseEmulators = BuildConfig.DEBUG;

우리가 사용하는 BuildConfig 우리의 응용 프로그램이 실행되는 경우 우리는 단지 에뮬레이터에 연결할 수 있는지 확인 debug 모드. 우리가 응용 프로그램을 컴파일 할 때 release 모드이 조건이 거짓 일 것이다.

이제 살펴 보도록 getFirestore() 방법 :

    public static FirebaseFirestore getFirestore() {
        if (FIRESTORE == null) {
            FIRESTORE = FirebaseFirestore.getInstance();

            // Connect to the Cloud Firestore emulator when appropriate. The host '10.0.2.2' is a
            // special IP address to let the Android emulator connect to 'localhost'.
            if (sUseEmulators) {
                FIRESTORE.useEmulator("10.0.2.2", 8080);
            }
        }

        return FIRESTORE;
    }

우리가 사용하고있는 것을 볼 수 있습니다 useEmulator(host, port) 지방의 경우 FireStore 에뮬레이터에 중포 기지 SDK를 연결하는 방법을. 응용 프로그램 전반에 걸쳐 우리가 사용 FirebaseUtil.getFirestore() 의 인스턴스에 액세스 할 수 FirebaseFirestore 우리가 실행할 때 우리는 항상 경우 FireStore 에뮬레이터에 연결하고 있는지 그래서 debug 모드.

앱 실행

당신이 추가 한 경우 google-services.json 제대로 파일을,이 프로젝트는 이제 컴파일해야한다. 안드로이드 스튜디오 클릭 빌드에서> 프로젝트를 재 구축하고 더 남아있는 오류가 없는지 확인합니다.

안드로이드 스튜디오를 실행 안드로이드 에뮬레이터에서 응용 프로그램. 처음에는 "로그인" 화면이 표시됩니다. 이메일과 비밀번호를 사용하여 앱에 로그인할 수 있습니다. 이 로그인 프로세스는 Firebase 인증 에뮬레이터에 연결 중이므로 실제 자격 증명이 전송되지 않습니다.

이제 이동하여 에뮬레이터 UI를 열 에 http : // localhost를 : 4000를 웹 브라우저에서. 그런 다음 인증 탭을 클릭하고 방금 만든 계정을 볼 수 :

Firebase 인증 에뮬레이터

로그인 프로세스를 완료하면 앱 홈 화면이 표시되어야 합니다.

de06424023ffb4b9.png

곧 홈 화면을 채우는 데이터를 추가할 것입니다.

6. Firestore에 데이터 쓰기

이 섹션에서는 현재 비어 있는 홈 화면을 채울 수 있도록 Firestore에 일부 데이터를 작성합니다.

우리의 응용 프로그램의 주요 모델 객체는 레스토랑 (참조입니다 model/Restaurant.java ). Firestore 데이터는 문서, 컬렉션 및 하위 컬렉션으로 분할됩니다. (이)라는 최상위 모음의 문서로 각 레스토랑 저장합니다 "restaurants" . 경우 FireStore 데이터 모델에 대한 자세한 내용은 문서 및 컬렉션에 대해 읽어 문서 .

시연을 위해 오버플로 메뉴에서 "임의 항목 추가" 버튼을 클릭할 때 10개의 임의 레스토랑을 만드는 기능을 앱에 추가합니다. 파일 열기 MainActivity.java 및 채우기 onAddItemsClicked() 메소드를 :

    private void onAddItemsClicked() {
        // Get a reference to the restaurants collection
        CollectionReference restaurants = mFirestore.collection("restaurants");

        for (int i = 0; i < 10; i++) {
            // Get a random Restaurant POJO
            Restaurant restaurant = RestaurantUtil.getRandom(this);

            // Add a new document to the restaurants collection
            restaurants.add(restaurant);
        }
    }

위의 코드에서 주의해야 할 몇 가지 중요한 사항이 있습니다.

  • 우리는에 대한 참조를 얻기 시작 "restaurants" 수집. 컬렉션은 문서가 추가될 때 묵시적으로 생성되기 때문에 데이터를 쓰기 전에 컬렉션을 생성할 필요가 없었습니다.
  • 문서는 각 레스토랑 문서를 만드는 데 사용하는 POJO를 사용하여 만들 수 있습니다.
  • add() 우리는 각 레스토랑에 대한 고유 ID를 지정할 필요가 없었다, 그래서 방법은 자동으로 생성 된 ID를 가진 콜렉션에 문서를 추가합니다.

이제 앱을 다시 실행하고 오버플로 메뉴에서 "임의 항목 추가" 버튼을 클릭하여 방금 작성한 코드를 호출합니다.

95691e9b71ba55e3.png

이제 이동하여 에뮬레이터 UI를 열 에 http : // localhost를 : 4000를 웹 브라우저에서. 그런 다음 경우 FireStore 탭을 클릭하면 방금 추가 한 데이터를 볼 수 :

Firebase 인증 에뮬레이터

이 데이터는 100% 컴퓨터에 로컬입니다. 사실, 실제 프로젝트에는 아직 Firestore 데이터베이스도 포함되어 있지 않습니다! 즉, 결과 없이 이 데이터를 수정하고 삭제하는 실험을 하는 것이 안전합니다.

축하합니다. 방금 Firestore에 데이터를 썼습니다! 다음 단계에서는 이 데이터를 앱에 표시하는 방법을 배웁니다.

7. Firestore의 데이터 표시

이 단계에서는 Firestore에서 데이터를 검색하고 앱에 표시하는 방법을 배웁니다. 경우 FireStore에서 데이터를 읽는 첫 번째 단계는 만드는 것입니다 Query . 수정 onCreate() 메소드를 :

        mFirestore = FirebaseUtil.getFirestore();

        // Get the 50 highest rated restaurants
        mQuery = mFirestore.collection("restaurants")
                .orderBy("avgRating", Query.Direction.DESCENDING)
                .limit(LIMIT);

이제 쿼리를 수신하여 일치하는 모든 문서를 얻고 실시간으로 향후 업데이트에 대한 알림을 받길 원합니다. 우리의 궁극적 인 목표는이 데이터를 바인딩하는 것이기 때문에 RecyclerView , 우리는 만들 필요가 RecyclerView.Adapter 데이터를들을 수있는 클래스를.

오픈 FirestoreAdapter 부분적으로 이미 구현 된 클래스를. 먼저, 어댑터가 구현 만들어 보자 EventListener 하고 정의 onEvent 그것은 경우 FireStore 쿼리에 대한 업데이트를받을 수 있도록 기능을 :

public abstract class FirestoreAdapter<VH extends RecyclerView.ViewHolder>
        extends RecyclerView.Adapter<VH>
        implements EventListener<QuerySnapshot> { // Add this "implements"

    // ...

    // Add this method
    @Override
    public void onEvent(QuerySnapshot documentSnapshots,
                        FirebaseFirestoreException e) {

        // Handle errors
        if (e != null) {
            Log.w(TAG, "onEvent:error", e);
            return;
        }

        // Dispatch the event
        for (DocumentChange change : documentSnapshots.getDocumentChanges()) {
            // Snapshot of the changed document
            DocumentSnapshot snapshot = change.getDocument();

            switch (change.getType()) {
                case ADDED:
                    // TODO: handle document added
                    break;
                case MODIFIED:
                    // TODO: handle document modified
                    break;
                case REMOVED:
                    // TODO: handle document removed
                    break;
            }
        }

        onDataChanged();
    }

  // ...
}

초기로드에 리스너는 하나 개 받게됩니다 ADDED 각각의 새 문서에 대한 이벤트를. 쿼리의 결과 집합이 시간이 지남에 따라 변경됨에 따라 수신기는 변경 사항이 포함된 더 많은 이벤트를 수신하게 됩니다. 이제 리스너 구현을 마치겠습니다. 먼저 세 개의 새로운 방법을 추가 onDocumentAdded , onDocumentModified , 그리고에 onDocumentRemoved :

    protected void onDocumentAdded(DocumentChange change) {
        mSnapshots.add(change.getNewIndex(), change.getDocument());
        notifyItemInserted(change.getNewIndex());
    }

    protected void onDocumentModified(DocumentChange change) {
        if (change.getOldIndex() == change.getNewIndex()) {
            // Item changed but remained in same position
            mSnapshots.set(change.getOldIndex(), change.getDocument());
            notifyItemChanged(change.getOldIndex());
        } else {
            // Item changed and changed position
            mSnapshots.remove(change.getOldIndex());
            mSnapshots.add(change.getNewIndex(), change.getDocument());
            notifyItemMoved(change.getOldIndex(), change.getNewIndex());
        }
    }

    protected void onDocumentRemoved(DocumentChange change) {
        mSnapshots.remove(change.getOldIndex());
        notifyItemRemoved(change.getOldIndex());
    }

다음에서 이러한 새로운 방법 전화 onEvent :

    @Override
    public void onEvent(QuerySnapshot documentSnapshots,
                        FirebaseFirestoreException e) {

        // ...

        // Dispatch the event
        for (DocumentChange change : documentSnapshots.getDocumentChanges()) {
            // Snapshot of the changed document
            DocumentSnapshot snapshot = change.getDocument();

            switch (change.getType()) {
                case ADDED:
                    onDocumentAdded(change); // Add this line
                    break;
                case MODIFIED:
                    onDocumentModified(change); // Add this line
                    break;
                case REMOVED:
                    onDocumentRemoved(change); // Add this line
                    break;
            }
        }

        onDataChanged();
    }

마지막으로 구현 startListening() 리스너를 연결하는 방법 :

    public void startListening() {
        if (mQuery != null && mRegistration == null) {
            mRegistration = mQuery.addSnapshotListener(this);
        }
    }

이제 앱이 Firestore에서 데이터를 읽도록 완전히 구성되었습니다. 다시 응용 프로그램을 실행하고 이전 단계에서 추가 한 레스토랑을 볼 수 :

9e45f40faefce5d0.png

이제 브라우저에서 에뮬레이터 UI로 돌아가 레스토랑 이름 중 하나를 편집합니다. 앱에서 거의 즉시 변경되는 것을 볼 수 있습니다!

8. 데이터 정렬 및 필터링

앱은 현재 전체 컬렉션에서 최고 등급의 레스토랑을 표시하지만 실제 레스토랑 앱에서는 사용자가 데이터를 정렬하고 필터링하기를 원할 것입니다. 예를 들어 앱은 "필라델피아 최고의 해산물 레스토랑" 또는 "가장 저렴한 피자"를 표시할 수 있어야 합니다.

앱 상단의 흰색 막대를 클릭하면 필터 대화 상자가 나타납니다. 이 섹션에서는 Firestore 쿼리를 사용하여 이 대화 상자가 작동하도록 합니다.

67898572a35672a5.png

하자의 편집 onFilter() 의 방법 MainActivity.java . 이 방법은 허용 Filters 우리는 필터 대화 상자의 출력을 캡처하기 위해 만든 도우미 개체입니다 개체를. 필터에서 쿼리를 구성하도록 이 메서드를 변경합니다.

    @Override
    public void onFilter(Filters filters) {
        // Construct query basic query
        Query query = mFirestore.collection("restaurants");

        // Category (equality filter)
        if (filters.hasCategory()) {
            query = query.whereEqualTo("category", filters.getCategory());
        }

        // City (equality filter)
        if (filters.hasCity()) {
            query = query.whereEqualTo("city", filters.getCity());
        }

        // Price (equality filter)
        if (filters.hasPrice()) {
            query = query.whereEqualTo("price", filters.getPrice());
        }

        // Sort by (orderBy with direction)
        if (filters.hasSortBy()) {
            query = query.orderBy(filters.getSortBy(), filters.getSortDirection());
        }

        // Limit items
        query = query.limit(LIMIT);

        // Update the query
        mQuery = query;
        mAdapter.setQuery(query);

        // Set header
        mCurrentSearchView.setText(Html.fromHtml(filters.getSearchDescription(this)));
        mCurrentSortByView.setText(filters.getOrderDescription(this));

        // Save filters
        mViewModel.setFilters(filters);
    }

위의 코드에서 우리는 구축 Query 부착하여 객체를 whereorderBy 주어진 필터와 일치하는 절을.

다시 응용 프로그램을 실행하고 가장 인기있는 저가 레스토랑을 보여주기 위해 다음과 같은 필터를 선택 :

7a67a8a400c80c50.png

이제 저가 옵션만 포함된 필터링된 레스토랑 목록이 표시됩니다.

a670188398c3c59.png

여기까지 했다면 이제 Firestore에서 완벽하게 작동하는 레스토랑 추천 보기 앱을 구축한 것입니다! 이제 실시간으로 레스토랑을 정렬하고 필터링할 수 있습니다. 다음 몇 섹션에서는 앱에 대한 리뷰와 보안을 게시합니다.

9. 하위 컬렉션의 데이터 구성

이 섹션에서는 사용자가 자신이 가장 좋아하는(또는 가장 좋아하지 않는) 레스토랑을 리뷰할 ​​수 있도록 앱에 평점을 추가합니다.

컬렉션 및 하위 컬렉션

지금까지 우리는 "레스토랑"이라는 최상위 컬렉션에 모든 레스토랑 데이터를 저장했습니다. 사용자의 요금에는 레스토랑 우리는 새로운 추가 할 때 Rating 레스토랑에 개체를. 이 작업을 위해 우리는 하위 컬렉션을 사용할 것입니다. 하위 컬렉션은 문서에 첨부된 컬렉션으로 생각할 수 있습니다. 따라서 각 레스토랑 문서에는 등급 문서로 가득 찬 등급 하위 컬렉션이 있습니다. 하위 컬렉션은 문서를 부풀리거나 복잡한 쿼리를 요구하지 않고 데이터를 구성하는 데 도움이 됩니다.

하위 컬렉션, 통화에 액세스하려면 .collection() 부모 문서에를 :

CollectionReference subRef = mFirestore.collection("restaurants")
        .document("abc123")
        .collection("ratings");

최상위 컬렉션과 마찬가지로 하위 컬렉션에 액세스하고 쿼리할 수 있으며 크기 제한이나 성능 변경이 없습니다. 당신은 경우 FireStore 데이터 모델에 대한 자세한 내용을보실 수 있습니다 여기에 .

트랜잭션에 데이터 쓰기

추가 Rating 적절한 하위 컬렉션에 만 호출 필요 .add() , 그러나 우리는 또한 업데이트해야 Restaurant 새로운 데이터를 반영하기 위해 개체의 평균 평가와 평가의 수입니다. 이러한 두 가지 변경을 수행하기 위해 별도의 작업을 사용하는 경우 유효하지 않거나 잘못된 데이터를 초래할 수 있는 여러 경쟁 조건이 있습니다.

평가가 제대로 추가되었는지 확인하기 위해 트랜잭션을 사용하여 레스토랑에 평가를 추가합니다. 이 트랜잭션은 몇 가지 작업을 수행합니다.

  • 레스토랑의 현재 평점을 읽고 새로운 평점을 계산하세요.
  • 하위 컬렉션에 등급 추가
  • 레스토랑의 평균 평점 및 평점 수 업데이트

열기 RestaurantDetailActivity.java 과 구현 addRating 기능 :

    private Task<Void> addRating(final DocumentReference restaurantRef,
                                 final Rating rating) {
        // Create reference for new rating, for use inside the transaction
        final DocumentReference ratingRef = restaurantRef.collection("ratings")
                .document();

        // In a transaction, add the new rating and update the aggregate totals
        return mFirestore.runTransaction(new Transaction.Function<Void>() {
            @Override
            public Void apply(Transaction transaction)
                    throws FirebaseFirestoreException {

                Restaurant restaurant = transaction.get(restaurantRef)
                        .toObject(Restaurant.class);

                // Compute new number of ratings
                int newNumRatings = restaurant.getNumRatings() + 1;

                // Compute new average rating
                double oldRatingTotal = restaurant.getAvgRating() *
                        restaurant.getNumRatings();
                double newAvgRating = (oldRatingTotal + rating.getRating()) /
                        newNumRatings;

                // Set new restaurant info
                restaurant.setNumRatings(newNumRatings);
                restaurant.setAvgRating(newAvgRating);

                // Commit to Firestore
                transaction.set(restaurantRef, restaurant);
                transaction.set(ratingRef, rating);

                return null;
            }
        });
    }

addRating() 함수는 반환 Task 전체 트랜잭션을 대표합니다. 에서 onRating() 함수 리스너는 트랜잭션의 결과에 대응하는 작업에 추가됩니다.

이제 다시 응용 프로그램을 실행하고 레스토랑 상세 화면을 표시해야하는 레스토랑 중 하나를 클릭합니다. 리뷰를 추가 시작 + 버튼을 클릭합니다. 많은 별을 선택하고 텍스트를 입력하여 리뷰를 추가하십시오.

78fa16cdf8ef435a.png

거래를 킥오프 것이다 제출 명중. 거래가 완료되면 아래에 귀하의 리뷰가 표시되고 레스토랑의 리뷰 수에 대한 업데이트가 표시됩니다.

f9e670f40bd615b0.png

축하 해요! 이제 Cloud Firestore에 구축된 소셜, 지역, 모바일 레스토랑 리뷰 앱이 있습니다. 요즘 인기가 많다고 들었습니다.

10. 데이터 보호

지금까지 우리는 이 애플리케이션의 보안을 고려하지 않았습니다. 사용자가 자신의 올바른 데이터만 읽고 쓸 수 있다는 것을 어떻게 알 수 있습니까? 경우 FireStore datbases가 호출 구성 파일에 의해 보호되는 보안 규칙 .

열기 firestore.rules 파일을, 당신은 다음을 참조한다 :

rules_version = '2';
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;
    }
  }
}

이 규칙하자의 변화는 acesss 또는 변경 원치 않는 데이터를 방지 엽니 다 firestore.rules 파일을 다음과 컨텐츠를 대체 :

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Determine if the value of the field "key" is the same
    // before and after the request.
    function isUnchanged(key) {
      return (key in resource.data)
        && (key in request.resource.data)
        && (resource.data[key] == request.resource.data[key]);
    }

    // Restaurants
    match /restaurants/{restaurantId} {
      // Any signed-in user can read
      allow read: if request.auth != null;

      // Any signed-in user can create
      // WARNING: this rule is for demo purposes only!
      allow create: if request.auth != null;

      // Updates are allowed if no fields are added and name is unchanged
      allow update: if request.auth != null
                    && (request.resource.data.keys() == resource.data.keys())
                    && isUnchanged("name");

      // Deletes are not allowed.
      // Note: this is the default, there is no need to explicitly state this.
      allow delete: if false;

      // Ratings
      match /ratings/{ratingId} {
        // Any signed-in user can read
        allow read: if request.auth != null;

        // Any signed-in user can create if their uid matches the document
        allow create: if request.auth != null
                      && request.resource.data.userId == request.auth.uid;

        // Deletes and updates are not allowed (default)
        allow update, delete: if false;
      }
    }
  }
}

이러한 규칙은 클라이언트가 안전한 변경만 수행할 수 있도록 액세스를 제한합니다. 예를 들어 레스토랑 문서에 대한 업데이트는 등급만 변경할 수 있으며 이름이나 기타 변경할 수 없는 데이터는 변경할 수 없습니다. 사용자 ID가 로그인한 사용자와 일치하는 경우에만 등급을 생성할 수 있으므로 스푸핑을 방지할 수 있습니다.

보안 규칙에 대한 자세한 내용은 다음 페이지를 참조 설명서를 .

11. 결론

이제 Firestore 위에 모든 기능을 갖춘 앱을 만들었습니다. 다음을 포함한 가장 중요한 Firestore 기능에 대해 배웠습니다.

  • 문서 및 컬렉션
  • 데이터 읽기 및 쓰기
  • 쿼리를 사용한 정렬 및 필터링
  • 하위 컬렉션
  • 업무

더 알아보기

Firestore에 대해 계속 배우려면 다음과 같이 시작하는 것이 좋습니다.

이 코드랩의 레스토랑 앱은 "Friendly Eats" 예제 애플리케이션을 기반으로 했습니다. 당신은이 응용 프로그램의 소스 코드를 찾아 볼 수 있습니다 여기에 .

선택 사항: 프로덕션에 배포

지금까지 이 앱은 Firebase Emulator Suite만 사용했습니다. 이 앱을 실제 Firebase 프로젝트에 배포하는 방법을 알아보려면 다음 단계로 계속 진행하세요.

12. (선택 사항) 앱 배포

지금까지 이 앱은 완전히 로컬이었고 모든 데이터는 Firebase 에뮬레이터 제품군에 포함되어 있습니다. 이 섹션에서는 이 앱이 프로덕션 환경에서 작동하도록 Firebase 프로젝트를 구성하는 방법을 배웁니다.

Firebase 인증

중포 기지에서에 인증 섹션 및 탐색으로 이동 consle 로그인 공급자 탭 .

이메일 로그인 방법 활성화:

334ef7f6ff4da4ce.png

소방서

데이터베이스 생성

이동 콘솔의 경우 FireStore 섹션데이터베이스 만들기를 클릭합니다 :

  1. 보안 규칙이 잠금 모드로 시작하는 선택에 대해 묻는 메시지가 표시되면, 우리는 곧 그 규칙을 업데이트 할 수 있습니다.
  2. 앱에 사용할 데이터베이스 위치를 선택합니다. 데이터베이스 위치가 영구적 인 결정입니다 선택을 참고 변경하는 새 프로젝트를 생성해야합니다. 프로젝트 위치를 선택하는 방법에 대한 자세한 내용은 참조 설명서를 .

규칙 배포

이전에 작성한 보안 규칙을 배포하려면 codelab 디렉터리에서 다음 명령을 실행합니다.

$ firebase deploy --only firestore:rules

이 내용 배포 할 firestore.rules 콘솔에있는 규칙 탭에서 확인할 수 있습니다 프로젝트에.

인덱스 배포

FriendlyEats 앱에는 여러 사용자 지정 복합 색인이 필요한 복잡한 정렬 및 필터링이 있습니다. 이들은 중포 기지 콘솔에서 손으로 만든하지만 자신의 정의를 작성하는 간단 할 수있다 firestore.indexes.json 파일과 중포 기지 CLI를 사용하여 배포 할 수 있습니다.

당신이 열 경우 firestore.indexes.json 파일을 당신은 필요한 인덱스가 이미 제공 한 것을 볼 수 있습니다 :

{
  "indexes": [
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "city", "mode": "ASCENDING" },
        { "fieldPath": "avgRating", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "category", "mode": "ASCENDING" },
        { "fieldPath": "avgRating", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "price", "mode": "ASCENDING" },
        { "fieldPath": "avgRating", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "city", "mode": "ASCENDING" },
        { "fieldPath": "numRatings", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "category", "mode": "ASCENDING" },
        { "fieldPath": "numRatings", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "price", "mode": "ASCENDING" },
        { "fieldPath": "numRatings", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "city", "mode": "ASCENDING" },
        { "fieldPath": "price", "mode": "ASCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "fields": [
        { "fieldPath": "category", "mode": "ASCENDING" },
        { "fieldPath": "price", "mode": "ASCENDING" }
      ]
    }
  ],
  "fieldOverrides": []
}

이러한 인덱스를 배포하려면 다음 명령을 실행합니다.

$ firebase deploy --only firestore:indexes

색인 생성은 즉시 수행되지 않으며 Firebase 콘솔에서 진행 상황을 모니터링할 수 있습니다.

앱 구성

에서 FirebaseUtil 클래스 우리는 디버그 모드에서 에뮬레이터에 연결하기 위해 중포 기지 SDK를 구성 :

public class FirebaseUtil {

    /** Use emulators only in debug builds **/
    private static final boolean sUseEmulators = BuildConfig.DEBUG;

    // ...
}

실제 Firebase 프로젝트로 앱을 테스트하려면 다음 중 하나를 수행할 수 있습니다.

  1. 릴리스 모드에서 앱을 빌드하고 기기에서 실행합니다.
  2. 일시적으로 변경 sUseEmulatorsfalse 다시 응용 프로그램을 실행합니다.

앱에서 로그 아웃 제대로 생산에 연결하려면 다시 로그인해야 할 수도 있습니다.