Google은 흑인 공동체를 위한 인종적 평등을 추구하기 위해 노력하고 있습니다. 자세히 알아보기

Cloud Firestore Android Codelab

목표

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

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

전제 조건

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

  • Android Studio 4.0 이상
  • Android 에뮬레이터
  • Node.js 버전 10 이상
  • Java 버전 8 이상
  1. Google 계정으로 Firebase 콘솔 에 로그인합니다.
  2. Firebase 콘솔 에서 프로젝트 추가를 클릭합니다.
  3. 아래 화면 캡처에 표시된대로 Firebase 프로젝트의 이름 (예 : 'Friendly Eats')을 입력하고 계속을 클릭합니다.

9d2f625aebcab6af.png

  1. 이 코드 랩의 목적 상 귀하의 선택은 중요하지 않으므로 Google Analytics를 활성화하라는 요청을받을 수 있습니다.
  2. 1 분 정도 지나면 Firebase 프로젝트가 준비됩니다. 계속을 클릭합니다.

코드 다운로드

다음 명령어를 실행하여이 코드 랩의 샘플 코드를 복제합니다. 그러면 컴퓨터에 friendlyeats-android 라는 폴더가 생성됩니다.

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

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

프로젝트를 Android Studio로 가져 옵니다. 컴파일 오류 또는 누락 된 google-services.json 파일에 대한 경고가 표시 될 수 있습니다. 다음 섹션에서이를 수정하겠습니다.

Firebase 구성 추가

  1. Firebase 콘솔 의 왼쪽 탐색 메뉴에서 프로젝트 개요 를 선택합니다. Android 버튼을 클릭하여 플랫폼을 선택하십시오. 패키지 이름을 입력하라는 메시지가 표시되면 com.google.firebase.example.fireeats 사용 com.google.firebase.example.fireeats

73d151ed16016421.png

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

이 Codelab에서는 Firebase Emulator Suite 를 사용하여 Cloud Firestore 및 기타 Firebase 서비스를 로컬에서 에뮬레이션합니다. 이는 앱을 빌드 할 수있는 안전하고 빠르며 무료 로컬 개발 환경을 제공합니다.

Firebase CLI 설치

먼저 Firebase CLI 를 설치해야합니다. 이를 수행하는 가장 쉬운 방법은 npm 을 사용하는 것입니다.

npm install -g firebase-tools

npm 없거나 오류가 발생하는 경우 설치 지침 을 읽고 플랫폼 용 독립 실행 형 바이너리를 가져옵니다.

CLI를 설치 한 후 firebase --version 실행하면 9.0.0 이상의 버전이보고됩니다.

$ firebase --version
9.0.0

로그인

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

friendlyeats-android 폴더 내에서 firebase use --add 를 실행하여 로컬 프로젝트를 Firebase 프로젝트에 연결합니다. 프롬프트에 따라 이전에 만든 프로젝트를 선택하고 별칭을 선택하라는 메시지가 표시되면 default 입력합니다.

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

에뮬레이터 실행

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

$ 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.

이제 컴퓨터에서 완전한 로컬 개발 환경이 실행됩니다! 나머지 코드 랩에서는이 명령어를 실행 상태로 두어야합니다. Android 앱은 에뮬레이터에 연결해야합니다.

에뮬레이터에 앱 연결

Android Studio에서 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) 메서드를 사용하여 Firebase SDK를 로컬 Firestore 에뮬레이터에 연결하고 있음을 알 수 있습니다. 앱 전체에서 FirebaseUtil.getFirestore() 를 사용하여이 FirebaseFirestore 인스턴스에 액세스하므로 debug 모드에서 실행할 때 항상 Firestore 에뮬레이터에 연결됩니다.

앱 실행

google-services.json 파일을 올바르게 추가했다면 이제 프로젝트가 컴파일됩니다. Android Studio에서 Build > Rebuild Project를 클릭하고 나머지 오류가 없는지 확인합니다.

Android Studio에서 Android 에뮬레이터에서 앱을 실행 합니다. 처음에는 "로그인"화면이 표시됩니다. 이메일과 비밀번호를 사용하여 앱에 로그인 할 수 있습니다. 이 로그인 프로세스는 Firebase 인증 에뮬레이터에 연결되므로 실제 사용자 인증 정보가 전송되지 않습니다.

이제 웹 브라우저에서 http : // localhost : 4000 으로 이동하여 에뮬레이터 UI를 엽니 다. 그런 다음 인증 탭을 클릭하면 방금 생성 한 계정이 표시됩니다.

Firebase 인증 에뮬레이터

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

de06424023ffb4b9.png

곧 홈 화면에 데이터를 추가 할 예정입니다.

이 섹션에서는 현재 비어있는 홈 화면을 채울 수 있도록 일부 데이터를 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

이제 웹 브라우저에서 http : // localhost : 4000 으로 이동하여 에뮬레이터 UI를 엽니 다. 그런 다음 Firestore 탭을 클릭하면 방금 추가 한 데이터가 표시됩니다.

Firebase 인증 에뮬레이터

이 데이터는 머신에 100 % 로컬입니다. 실제로 실제 프로젝트에는 아직 Firestore 데이터베이스가 포함되어 있지 않습니다! 이것은 결과없이이 데이터를 수정하고 삭제하는 실험이 안전하다는 것을 의미합니다.

축하합니다. 방금 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 구현하고 Firestore 쿼리에 대한 업데이트를 수신 할 수 있도록 onEvent 함수를 정의하겠습니다.

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 , onDocumentModifiedonDocumentAdded 세 가지 새 메서드를 추가 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로 돌아가 레스토랑 이름 중 하나를 편집합니다. 거의 즉시 앱에서 변경되는 것을 볼 수 있습니다!

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

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

67898572a35672a5.png

MainActivity.javaonFilter() 메소드를 편집 해 보겠습니다. 이 메서드는 필터 대화 상자의 출력을 캡처하기 위해 만든 도우미 개체 인 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);
    }

위의 스 니펫에서 주어진 필터와 일치하도록 whereorderBy 절을 첨부하여 Query 객체를 빌드합니다.

앱을 다시 실행 하고 다음 필터를 선택하여 가장 인기있는 저가 레스토랑을 표시합니다.

7a67a8a400c80c50.png

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

a670188398c3c59.png

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

이 섹션에서는 사용자가 가장 좋아하는 (또는 가장 적게 좋아하는) 레스토랑을 리뷰 할 수 있도록 앱에 등급을 추가합니다.

컬렉션 및 하위 컬렉션

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

하위 .collection() 액세스하려면 상위 문서에서 .collection() 을 호출합니다.

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

최상위 컬렉션과 마찬가지로 하위 컬렉션에 액세스하고 쿼리 할 수 ​​있으며 크기 제한이나 성능 변경은 없습니다. 여기 에서 Firestore 데이터 모델에 대해 자세히 알아볼 수 있습니다.

트랜잭션에 데이터 쓰기

적절한 하위 .add() Rating 을 추가하려면 .add() 호출하기 .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에 빌드 된 소셜, 지역, 모바일 레스토랑 리뷰 앱이 있습니다. 요즘 인기가 많다고 들었어요.

지금까지이 응용 프로그램의 보안을 고려하지 않았습니다. 사용자가 올바른 데이터를 읽고 쓸 수만 있다는 것을 어떻게 알 수 있습니까? Firestore 데이터베이스는 Security Rules 라는 구성 파일로 보호됩니다.

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;
    }
  }
}

원치 않는 데이터 액세스 또는 변경을 방지하기 위해 이러한 규칙을 변경하고, 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가 로그인 한 사용자와 일치하는 경우에만 생성 할 수 있으므로 스푸핑을 방지합니다.

보안 규칙에 대한 자세한 내용 은 설명서를 참조하십시오 .

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

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

더 알아보기

Firestore에 대해 계속 배우기 위해 시작하기에 좋은 곳은 다음과 같습니다.

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

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

지금까지이 앱은 Firebase Emulator Suite 만 사용했습니다. 이 앱을 실제 Firebase 프로젝트에 배포하는 방법을 배우려면 다음 단계를 계속합니다.

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

Firebase 인증

Firebase 콘솔에서 인증 섹션으로 이동하고 로그인 제공 업체 탭으로 이동 합니다.

이메일 로그인 방법을 활성화합니다.

334ef7f6ff4da4ce.png

Firestore

데이터베이스 생성

콘솔의 Firestore 섹션으로 이동하여 데이터베이스 생성을 클릭합니다.

  1. 보안 규칙이 잠금 모드 로 시작하도록 선택하라는 메시지가 표시되면 해당 규칙이 곧 업데이트됩니다.
  2. 앱에 사용할 데이터베이스 위치를 선택하십시오. 데이터베이스 위치 선택은 영구적 인 결정이며이를 변경하려면 새 프로젝트를 만들어야합니다. 프로젝트 위치 선택에 대한 자세한 내용은 설명서를 참조하십시오.

규칙 배포

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

$ firebase deploy --only firestore:rules

그러면 firestore.rules 의 콘텐츠가 프로젝트에 배포되며 콘솔의 규칙 탭으로 이동하여 확인할 수 있습니다.

인덱스 배포

FriendlyEats 앱에는 복잡한 정렬 및 필터링 기능이있어 여러 사용자 지정 복합 색인이 필요합니다. Firebase 콘솔에서 직접 만들 수 있지만 firestore.indexes.json 파일에 정의를 작성하고 Firebase 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 클래스에서 디버그 모드에있을 때 에뮬레이터에 연결하도록 Firebase SDK를 구성했습니다.

public class FirebaseUtil {

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

    // ...
}

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

  1. 릴리스 모드에서 앱을 빌드하고 장치에서 실행합니다.
  2. 일시적으로 sUseEmulatorsfalse 변경하고 앱을 다시 실행하십시오.

프로덕션에 제대로 연결하려면 앱에서 로그 아웃 했다가 다시 로그인 해야 할 수 있습니다.