AngularFire 웹 Codelab

1. 개요

이 Codelab에서는 AngularFire를 사용하여 Firebase 제품 및 서비스를 통해 채팅 클라이언트를 구현하고 배포하여 웹 애플리케이션을 만드는 방법을 알아봅니다.

사용자가 Firebase에 관해 논의하는 채팅 앱

학습할 내용

  • Angular 및 Firebase를 사용하여 웹 앱을 빌드합니다.
  • Cloud Firestore 및 Firebase용 Cloud Storage를 사용하여 데이터 동기화
  • Firebase 인증을 사용하여 사용자를 인증합니다.
  • Firebase App Hosting에 웹 앱을 배포합니다.
  • Firebase 클라우드 메시징으로 알림을 보냅니다.
  • 웹 앱의 성능 데이터를 수집합니다.

필요한 사항

  • GitHub 계정
  • Firebase 프로젝트를 Blaze 요금제로 업그레이드하는 기능
  • 원하는 IDE/텍스트 편집기(예: WebStorm, Sublime, VS Code)
  • 일반적으로 Node.js와 함께 제공되는 패키지 관리자 npm
  • 터미널/콘솔
  • 원하는 브라우저(예: Chrome)
  • Codelab의 샘플 코드 (코드 가져오기 방법은 Codelab의 다음 단계를 참고하세요.)

2. 샘플 코드 가져오기

GitHub 저장소 만들기

Codelab 소스는 https://github.com/firebase/codelab-friendlychat-web에서 확인할 수 있습니다. 저장소에는 여러 플랫폼용 샘플 프로젝트가 있습니다. 그러나 이 Codelab에서는 angularfire-start 디렉터리만 사용합니다.

angularfire-start 폴더를 자체 저장소에 복사합니다.

  1. 터미널을 사용하여 컴퓨터에 새 폴더를 만들고 새 디렉터리로 변경합니다.
    mkdir codelab-friendlyeats-web
    
    cd codelab-friendlyeats-web
    
  2. giget npm 패키지를 사용하여 angularfire-start 폴더만 가져옵니다.
    npx giget@latest gh:firebase/codelab-friendlychat-web/angularfire-start#master . --install
    
  3. git을 사용하여 로컬에서 변경사항 추적:
    git init
    
    git add .
    
    git commit -m "codelab starting point"
    
    git branch -M main
    
  4. 새 GitHub 저장소(https://github.com/new)를 만듭니다. 원하는 이름을 지정합니다.
    1. GitHub에서 https://github.com/[user-name]/[repository-name].git 또는 git@github.com:[user-name]/[repository-name].git과 같은 새 저장소 URL을 제공합니다. 이 URL을 복사합니다.
  5. 새 GitHub 저장소에 로컬 변경사항을 푸시합니다. 다음 명령어를 실행하여 your-repository-url 자리표시자를 저장소 URL로 바꿉니다.
    git remote add origin your-repository-url
    
    git push -u origin main
    
  6. 이제 GitHub 저장소에 시작 코드가 표시됩니다.

3. Firebase 프로젝트 만들기 및 설정

Firebase 프로젝트 만들기

  1. Firebase Console에 로그인합니다.
  2. Firebase Console에서 프로젝트 추가를 클릭한 후 Firebase 프로젝트의 이름을 FriendlyChat으로 지정합니다. Firebase 프로젝트의 프로젝트 ID를 기억합니다.
  3. 이 프로젝트에 Google 애널리틱스 사용 설정을 선택 해제합니다.
  4. 프로젝트 만들기를 클릭합니다.

빌드하려는 애플리케이션은 웹 앱에 사용할 수 있는 Firebase 제품을 사용합니다.

  • Firebase 인증 - 사용자가 앱에 간편하게 로그인할 수 있음
  • Cloud Firestore: 클라우드에 구조화된 데이터를 저장하고 데이터가 변경되면 인스턴트 알림을 받을 수 있습니다.
  • Firebase용 Cloud Storage: 클라우드에 파일 저장
  • Firebase App Hosting: 앱을 빌드, 호스팅, 제공합니다.
  • Firebase 클라우드 메시징: 푸시 알림을 전송하고 브라우저 팝업 알림을 표시합니다.
  • Firebase Performance Monitoring: 앱의 사용자 성능 데이터를 수집합니다.

이러한 제품 중에는 특수 구성이 필요하거나 Firebase Console을 사용하여 사용 설정해야 하는 제품이 있습니다.

Firebase 요금제 업그레이드

Firebase App Hosting 및 Firebase용 Cloud Storage를 사용하려면 Firebase 프로젝트가 사용한 만큼만 지불 (Blaze) 요금제를 사용해야 합니다. 즉, Cloud Billing 계정에 연결되어 있어야 합니다.

  • Cloud Billing 계정에는 신용카드와 같은 결제 수단이 필요합니다.
  • Firebase와 Google Cloud를 처음 사용하는 경우 $300 크레딧과 무료 체험판 Cloud Billing 계정을 받을 자격이 되는지 확인하세요.
  • 이벤트의 일환으로 이 Codelab을 진행하는 경우 주최자에게 사용 가능한 Cloud 크레딧이 있는지 문의하세요.

프로젝트를 Blaze 요금제로 업그레이드하려면 다음 단계를 따르세요.

  1. Firebase Console에서 요금제를 업그레이드하도록 선택합니다.
  2. Blaze 요금제를 선택합니다. 화면에 표시된 안내에 따라 Cloud Billing 계정을 프로젝트에 연결합니다.
    이 업그레이드의 일환으로 Cloud Billing 계정을 만들어야 하는 경우 업그레이드를 완료하기 위해 Firebase Console의 업그레이드 흐름으로 돌아가야 할 수 있습니다.

프로젝트에 Firebase 웹 앱 추가

  1. 웹 아이콘 58d6543a156e56f9.png을 클릭하여 새 Firebase 웹 앱을 만듭니다.
  2. 앱을 Friendly Chat이라는 닉네임으로 등록합니다. 이 앱의 Firebase 호스팅도 설정합니다 옆의 체크박스를 선택하지 마세요. 앱 등록을 클릭합니다.
  3. 다음 단계에서는 구성 객체가 표시됩니다. 지금은 별도로 조치를 취할 필요가 없습니다. Console로 이동을 클릭합니다.

웹 앱 스크린샷 등록

인증 설정

사용자가 Google 계정으로 웹 앱에 로그인할 수 있도록 하려면 Google 로그인 방법을 사용합니다.

  1. Firebase Console에서 인증으로 이동합니다.
  2. 시작하기를 클릭합니다.
  3. 추가 제공업체 열에서 Google > 사용 설정을 클릭합니다.
  4. 프로젝트의 공개용 이름 텍스트 상자에 기억하기 쉬운 이름(예: My Next.js app)을 입력합니다.
  5. 프로젝트 지원 이메일 드롭다운에서 이메일 주소를 선택합니다.
  6. 저장을 클릭합니다.

Cloud Firestore 설정

웹 앱은 Cloud Firestore를 사용하여 채팅 메시지를 저장하고 새 채팅 메시지를 수신합니다.

Firebase 프로젝트에서 Cloud Firestore를 설정하는 방법은 다음과 같습니다.

  1. Firebase Console의 왼쪽 패널에서 빌드를 펼친 다음 Firestore 데이터베이스를 선택합니다.
  2. 데이터베이스 만들기를 클릭합니다.
  3. 데이터베이스 ID(default)로 설정된 채로 둡니다.
  4. 데이터베이스의 위치를 선택한 다음 다음을 클릭합니다.
    실제 앱의 경우 사용자와 가까운 위치를 선택해야 합니다.
  5. 테스트 모드로 시작을 클릭합니다. 보안 규칙에 관한 면책 조항을 읽습니다.
    이 Codelab의 후반부에서 데이터를 보호하는 보안 규칙을 추가합니다. 데이터베이스에 대한 보안 규칙을 추가하지 않은 채 앱을 공개적으로 배포하거나 노출하지 마세요.
  6. 만들기를 클릭합니다.

Firebase용 Cloud Storage 설정

웹 앱은 Firebase용 Cloud Storage를 사용하여 사진을 저장, 업로드, 공유합니다.

Firebase 프로젝트에서 Firebase용 Cloud Storage를 설정하는 방법은 다음과 같습니다.

  1. Firebase Console의 왼쪽 패널에서 빌드를 펼친 다음 스토리지를 선택합니다.
  2. 시작하기를 클릭합니다.
  3. 기본 Storage 버킷의 위치를 선택합니다.
    US-WEST1, US-CENTRAL1, US-EAST1의 버킷은 Google Cloud Storage의 '항상 무료' 등급을 활용할 수 있습니다. 다른 모든 위치의 버킷은 Google Cloud Storage 가격 책정 및 사용량을 따릅니다.
  4. 테스트 모드로 시작을 클릭합니다. 보안 규칙에 관한 면책 조항을 읽습니다.
    이 Codelab의 후반부에서 데이터를 보호하는 보안 규칙을 추가합니다. 스토리지 버킷에 대한 보안 규칙을 추가하지 않은 채 앱을 공개적으로 배포하거나 노출하지 마세요.
  5. 만들기를 클릭합니다.

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

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

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

Firebase CLI 버전이 vv13.9.0 이상인지 확인합니다.

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

앱의 로컬 디렉터리 (이전 Codelab에서 클론한 저장소)에서 Firebase 호스팅을 위한 앱 구성을 가져오도록 웹 앱 템플릿을 설정했습니다. 하지만 구성을 가져오려면 앱을 Firebase 프로젝트와 연결해야 합니다.

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

별칭은 여러 환경 (프로덕션, 스테이징 등)이 있는 경우에 유용합니다. 그러나 이 Codelab에서는 default 별칭을 사용하면 됩니다.

  1. 명령줄에서 나머지 안내를 따릅니다.

5. AngularFire 설치

프로젝트를 실행하기 전에 Angular CLI 및 AngularFire가 설정되어 있는지 확인합니다.

  1. 콘솔에서 다음 명령어를 실행합니다.
npm install -g @angular/cli
  1. 그런 다음 angularfire-start 디렉터리의 콘솔에서 다음 Angular CLI 명령어를 실행합니다.
ng add @angular/fire

이렇게 하면 프로젝트에 필요한 모든 종속 항목이 설치됩니다.

  1. 메시지가 표시되면 스페이스바를 눌러 ng deploy -- hosting를 선택 해제합니다. 화살표 키와 스페이스바를 사용하여 다음 기능을 선택합니다.
    • Authentication
    • Firestore
    • Cloud Messaging
    • Cloud Storage
  2. enter 키를 누르고 나머지 프롬프트를 따릅니다.
  3. 'Install AngularFire' 커밋 메시지로 커밋을 만들고 GitHub 저장소에 푸시합니다.

6. App Hosting 백엔드 만들기

이 섹션에서는 Git 저장소의 브랜치를 감시하도록 앱 호스팅 백엔드를 설정합니다.

이 섹션이 끝나면 GitHub의 저장소에 연결된 App Hosting 백엔드가 생성되며, 이 백엔드는 main 브랜치에 새 커밋을 푸시할 때마다 새 버전의 앱을 자동으로 다시 빌드하고 출시합니다.

  1. Firebase Console에서 앱 호스팅 페이지로 이동합니다.

'시작' 버튼이 있는 App Hosting 콘솔의 0 상태

  1. '시작하기'를 클릭하여 백엔드 생성 흐름을 시작합니다. 다음과 같이 백엔드를 구성합니다.
  2. 첫 번째 단계의 메시지에 따라 이전에 만든 GitHub 저장소를 연결합니다.
  3. 배포 설정을 설정합니다.
    1. 루트 디렉터리를 /로 유지
    2. 실시간 브랜치를 main로 설정합니다.
    3. 자동 출시 사용 설정
  4. 백엔드 이름을 friendlychat-codelab으로 지정합니다.
  5. 'Firebase 웹 앱 만들기 또는 연결'에서 '기존 Firebase 웹 앱 선택' 드롭다운에서 앞서 구성한 웹 앱을 선택합니다.
  6. '완료 및 배포'를 클릭합니다. 잠시 후 새 페이지로 이동하여 새 앱 호스팅 백엔드의 상태를 확인할 수 있습니다.
  7. 출시가 완료되면 '도메인'에서 무료 도메인을 클릭합니다. DNS 전파로 인해 작동하기 시작하는 데 몇 분 정도 걸릴 수 있습니다.

초기 웹 앱을 배포했습니다. GitHub 저장소의 main 브랜치에 새 커밋을 푸시할 때마다 Firebase Console에서 새 빌드 및 출시가 시작되고 출시가 완료되면 사이트가 자동으로 업데이트됩니다.

'시작' 버튼이 있는 App Hosting 콘솔의 0 상태

FriendlyChat 앱의 로그인 화면이 표시되지만 아직 작동하지 않습니다.

지금은 앱이 아무것도 할 수 없지만 여러분의 도움을 받아 곧 기능을 사용할 수 있게 될 것입니다.

이제 실시간 채팅 앱을 빌드해 보겠습니다.

7. Firebase 가져오기 및 구성

Firebase 구성

사용 중인 Firebase 프로젝트를 알려주도록 Firebase SDK를 구성해야 합니다.

  1. Firebase Console의 프로젝트 설정으로 이동합니다.
  2. '내 앱' 카드에서 구성 객체가 필요한 앱의 닉네임을 선택합니다.
  3. Firebase SDK 스니펫 창에서 '구성'을 선택합니다.

환경 파일 /angularfire-start/src/environments/environment.ts가 자동으로 생성된 것을 확인할 수 있습니다.

  1. 구성 객체 스니펫을 복사한 다음 angularfire-start/src/firebase-config.js에 추가합니다.

environment.ts

export const environment = {
  firebase: {
    apiKey: "API_KEY",
    authDomain: "PROJECT_ID.firebaseapp.com",
    projectId: "PROJECT_ID",
    storageBucket: "PROJECT_ID.firebasestorage.app",
    messagingSenderId: "SENDER_ID",
    appId: "APP_ID",
  },
};

AngularFire 설정 보기

콘솔에서 선택한 기능이 /angularfire-start/src/app/app.config.ts 파일에 자동으로 추가된 것을 확인할 수 있습니다. 이렇게 하면 앱에서 Firebase 기능을 사용할 수 있습니다.

8. 사용자 로그인 설정

이제 AngularFire가 app.config.ts에서 가져와서 초기화되었으므로 사용할 수 있습니다. 이제 Firebase 인증을 사용하여 사용자 로그인을 구현합니다.

승인된 도메인 추가

Firebase 인증은 관리자가 제어하는 지정된 도메인 목록에서만 로그인을 허용합니다. 도메인 목록에 무료 앱 호스팅 도메인을 추가합니다.

  1. App Hosting으로 이동합니다.
  2. 백엔드의 도메인을 복사합니다.
  3. Authentication settings(인증 설정)로 이동합니다.
  4. 승인된 도메인 탭을 선택합니다.
  5. 도메인 추가를 클릭하고 App Hosting 백엔드의 도메인을 붙여넣습니다.

Google 로그인을 사용하여 사용자 인증

앱에서 사용자가 Google 계정으로 로그인 버튼을 클릭하면 login 함수가 트리거됩니다. 이 Codelab에서는 Firebase가 Google을 ID 공급업체로 사용하도록 승인합니다. 팝업을 사용하지만 Firebase에서는 다른 여러 가지 방법을 사용할 수 있습니다.

  1. 하위 디렉터리 /src/app/services/에서 chat.service.ts를 엽니다.
  2. login 함수를 찾습니다.
  3. 함수 전체를 다음 코드로 대체합니다.

chat.service.ts

// Signs-in Friendly Chat.
login() {
    signInWithPopup(this.auth, this.provider).then((result) => {
        const credential = GoogleAuthProvider.credentialFromResult(result);
        this.router.navigate(['/', 'chat']);
        return credential;
    })
}

logout 함수는 사용자가 로그아웃 버튼을 클릭할 때 트리거됩니다.

  1. src/app/services/chat.service.ts 파일로 돌아갑니다.
  2. logout 함수를 찾습니다.
  3. 함수 전체를 다음 코드로 대체합니다.

chat.service.ts

// Logout of Friendly Chat.
logout() {
    signOut(this.auth).then(() => {
        this.router.navigate(['/', 'login'])
        console.log('signed out');
    }).catch((error) => {
        console.log('sign out error: ' + error);
    })
}

인증 상태 추적

이에 따라 UI를 업데이트하려면 사용자가 로그인했는지 로그아웃했는지 확인하는 방법이 필요합니다. AngularFire는 인증 상태가 변경될 때마다 업데이트되는 관찰 가능한 항목을 가져오는 함수를 제공합니다. 이 기능은 이미 구현되었지만 한번 살펴볼 만합니다.

  1. src/app/services/chat.service.ts 파일로 돌아갑니다.
  2. 변수 할당 user$를 찾습니다.

chat.service.ts

// observable that is updated when the auth state changes
user$ = user(this.auth);

위의 코드는 관찰 가능한 사용자를 반환하는 AngularFire 함수 user를 호출합니다. 인증 상태가 변경될 때마다 (사용자가 로그인하거나 로그아웃할 때) 트리거됩니다. FriendlyChat의 Angular 템플릿 구성요소는 이 관찰 가능한 항목을 사용하여 UI를 업데이트하여 리디렉션하고, 헤더 탐색 메뉴에 사용자를 표시하는 등의 작업을 실행합니다.

앱 로그인 테스트

  1. 'Google 인증 추가' 커밋 메시지로 커밋을 만들고 GitHub 저장소에 푸시합니다.
  2. Firebase Console에서 앱 호스팅 페이지를 열고 새 출시가 완료될 때까지 기다립니다.
  3. 웹 앱에서 페이지를 새로고침하고 로그인 버튼과 Google 계정을 사용하여 앱에 로그인합니다. auth/operation-not-allowed 오류 메시지가 표시되면 Firebase Console에서 Google 로그인을 인증 제공업체로 사용 설정했는지 확인합니다.
  4. 로그인하면 프로필 사진과 사용자 이름이 표시됩니다. angularfire-3.png

9. Cloud Firestore에 메시지 쓰기

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

데이터 모델

Cloud Firestore 데이터는 컬렉션, 문서, 필드, 하위 컬렉션으로 분할됩니다. 채팅의 각 메시지를 messages라는 최상위 컬렉션에 문서로 저장합니다.

688d7bc5fb662b57.png

Cloud Firestore에 메시지 추가

사용자가 작성한 채팅 메시지를 저장하려면 Cloud Firestore를 사용합니다.

이 섹션에서는 사용자가 데이터베이스에 새 메시지를 쓸 수 있는 기능을 추가합니다. 사용자가 보내기 버튼을 클릭하면 아래 코드 스니펫이 트리거됩니다. 메시지 필드의 콘텐츠가 포함된 메시지 객체를 messages 컬렉션의 Cloud Firestore 인스턴스에 추가합니다. add() 메서드는 자동으로 생성된 ID가 있는 새 문서를 컬렉션에 추가합니다.

  1. src/app/services/chat.service.ts 파일로 돌아갑니다.
  2. addMessage 함수를 찾습니다.
  3. 함수 전체를 다음 코드로 대체합니다.

chat.service.ts

// Adds a text or image message to Cloud Firestore.
addMessage = async (
  textMessage: string | null,
  imageUrl: string | null,
): Promise<void | DocumentReference<DocumentData>> => {
  // ignore empty messages
  if (!textMessage && !imageUrl) {
    console.log(
      "addMessage was called without a message",
      textMessage,
      imageUrl,
    );
    return;
  }

  if (this.currentUser === null) {
    console.log("addMessage requires a signed-in user");
    return;
  }

  const message: ChatMessage = {
    name: this.currentUser.displayName,
    profilePicUrl: this.currentUser.photoURL,
    timestamp: serverTimestamp(),
    uid: this.currentUser?.uid,
  };

  textMessage && (message.text = textMessage);
  imageUrl && (message.imageUrl = imageUrl);

  try {
    const newMessageRef = await addDoc(
      collection(this.firestore, "messages"),
      message,
    );
    return newMessageRef;
  } catch (error) {
    console.error("Error writing new message to Firebase Database", error);
    return;
  }
};

메시지 전송 테스트

  1. 'Firestore에 새 채팅 게시'라는 커밋 메시지로 커밋을 만들고 GitHub 저장소에 푸시합니다.
  2. Firebase Console에서 앱 호스팅 페이지를 열고 새 출시가 완료될 때까지 기다립니다.
  3. FriendlyChat을 새로고침합니다. 로그인한 후 '안녕하세요'와 같은 메시지를 입력한 다음 보내기를 클릭합니다. 이렇게 하면 Cloud Firestore에 메시지가 기록됩니다. 그러나 실제 웹 앱에는 아직 데이터가 표시되지 않습니다. 이는 계속해서 데이터 검색을 구현해야 하기 때문입니다 (이 Codelab의 다음 섹션에서).
  4. Firebase Console에서 새로 추가된 메시지를 확인할 수 있습니다. 에뮬레이터 도구 모음 UI를 엽니다. 빌드 섹션에서 Firestore 데이터베이스를 클릭합니다 (또는 여기를 클릭하면 새로 추가된 메시지가 포함된 메시지 컬렉션이 표시됩니다.

6812efe7da395692.png

10. 메시지 읽기

메시지 동기화

앱에서 메시지를 읽으려면 데이터가 변경될 때 트리거되는 관찰 가능한 항목을 추가한 다음 새 메시지를 표시하는 UI 요소를 만들어야 합니다.

앱에서 새로 추가된 메시지를 리슨하는 코드를 추가합니다. 이 코드에서는 messages 컬렉션의 스냅샷을 검색합니다. 로드 시 매우 긴 기록이 표시되지 않도록 채팅의 마지막 12개 메시지만 표시합니다.

  1. src/app/services/chat.service.ts 파일로 돌아갑니다.
  2. loadMessages 함수를 찾습니다.
  3. 함수 전체를 다음 코드로 대체합니다.

chat.service.ts

// Loads chat message history and listens for upcoming ones.
loadMessages = () => {
  // Create the query to load the last 12 messages and listen for new ones.
  const recentMessagesQuery = query(collection(this.firestore, 'messages'), orderBy('timestamp', 'desc'), limit(12));
  // Start listening to the query.
  return collectionData(recentMessagesQuery);
}

데이터베이스의 메시지를 리슨하려면 collection 함수를 사용하여 리슨할 데이터가 있는 컬렉션을 지정하여 컬렉션에 대한 쿼리를 만듭니다. 위의 코드에서는 채팅 메시지가 저장되는 messages 컬렉션 내의 변경사항을 리슨하고 있습니다. 또한 limit(12)를 사용하여 마지막 12개 메시지만 리슨하고 orderBy('timestamp', 'desc')를 사용하여 날짜순으로 메시지를 정렬하여 가장 최근 12개 메시지를 가져와 제한을 적용합니다.

collectionData 함수는 내부적으로 스냅샷을 사용합니다. 콜백 함수는 쿼리와 일치하는 문서가 변경될 때 트리거됩니다. 메시지가 삭제, 수정 또는 추가된 경우 이러한 현상이 발생할 수 있습니다. 자세한 내용은 Cloud Firestore 문서를 참고하세요.

메시지 동기화 테스트

  1. 'UI에 새 채팅 표시'라는 커밋 메시지로 커밋을 만들고 GitHub 저장소에 푸시합니다.
  2. Firebase Console에서 앱 호스팅 페이지를 열고 새 출시가 완료될 때까지 기다립니다.
  3. FriendlyChat을 새로고침합니다. 이전에 데이터베이스에서 만든 메시지가 FriendlyChat UI에 표시됩니다 (아래 참고). 새 메시지를 자유롭게 작성하세요. 즉시 표시됩니다.
  4. (선택사항) 에뮬레이터 모음의 Firestore 섹션에서 직접 새 메시지를 수동으로 삭제, 수정 또는 추가해 볼 수 있습니다. 변경사항은 UI에 반영됩니다.

수고하셨습니다. 앱에서 Cloud Firestore 문서를 읽고 있습니다.

angularfire-2.png

11. AI 기능 추가

Google AI를 사용하여 채팅 앱에 유용한 보조 기능을 추가합니다.

Google AI API 키 가져오기

  1. Google AI 스튜디오로 이동하여 API 키 만들기를 클릭합니다.
  2. 이 Codelab을 위해 만든 Firebase 프로젝트를 선택합니다. Google Cloud 프로젝트에 대한 메시지이지만 모든 Firebase 프로젝트는 Google Cloud 프로젝트입니다.
  3. 기존 프로젝트에서 API 키 만들기를 클릭합니다.
  4. 결과 API 키 복사

확장 프로그램 설치

이 확장 프로그램은 Firestore의 messages 컬렉션에 새 문서가 추가될 때마다 트리거되는 클라우드 함수를 배포합니다. 이 함수는 Gemini를 호출하고 응답을 문서의 response 필드에 다시 씁니다.

  1. Gemini API를 사용한 챗봇 빌드 확장 프로그램 페이지에서 Firebase Console에 설치를 클릭합니다.
  2. 안내를 따릅니다. 확장 프로그램 구성 단계에 도달하면 다음 매개변수 값을 설정합니다.
    • Gemini API 제공업체: Google AI
    • Google AI API 키: 이전에 만든 키를 붙여넣고 보안 비밀 만들기를 클릭합니다.
    • Firestore 컬렉션 경로: messages
    • 프롬프트 필드: text
    • 응답 필드: response
    • 순서 필드: timestamp
    • 컨텍스트: Keep your answers short, informal, and helpful. Use emojis when possible.
  3. 확장 프로그램 설치를 클릭합니다.
  4. 확장 프로그램 설치가 완료될 때까지 기다립니다.

AI 기능 테스트

FriendlyChat에는 이미 AI 확장 프로그램의 응답을 읽는 코드가 있습니다. 새 채팅 메시지를 보내면 바로 테스트해 볼 수 있습니다.

  1. FriendlyChat을 열고 메시지를 보냅니다.
  2. 잠시 후 메시지 옆에 응답 팝업이 표시됩니다. 실제 사용자가 아닌 생성형 AI로 생성되었음을 명확히 하기 위해 끝에 ✨ ai generated 메모가 있습니다.

12. 이미지 전송

이제 이미지를 공유하는 기능을 추가합니다.

Cloud Firestore는 구조화된 데이터를 저장하는 데 적합하지만 Cloud Storage는 파일을 저장하는 데 더 적합합니다. Firebase용 Cloud Storage는 파일/블롭 저장소 서비스이며, 이를 사용하여 사용자가 앱을 사용하여 공유하는 모든 이미지를 저장할 수 있습니다.

Cloud Storage에 이미지 저장

이 Codelab에서는 파일 선택기 대화상자를 트리거하는 버튼이 이미 추가되어 있습니다. 파일을 선택하면 saveImageMessage 함수가 호출되고 선택한 파일의 참조를 가져올 수 있습니다. saveImageMessage 함수는 다음을 실행합니다.

  1. 이미지를 업로드하는 동안 사용자에게 '로드 중' 애니메이션이 표시되도록 채팅 피드에 '자리표시자' 채팅 메시지를 만듭니다.
  2. Cloud Storage의 /<uid>/<file_name> 경로에 이미지 파일을 업로드합니다.
  3. 이미지 파일을 공개적으로 읽을 수 있는 URL을 생성합니다.
  4. 임시로 로드되는 이미지 대신 새로 업로드된 이미지 파일의 URL로 채팅 메시지를 업데이트합니다.

이제 이미지를 전송하는 기능을 추가합니다.

  1. src/chat.service.ts 파일로 돌아갑니다.
  2. saveImageMessage 함수를 찾습니다.
  3. 함수 전체를 다음 코드로 대체합니다.

chat.service.ts

// Saves a new message containing an image in Firestore.
// This first saves the image in Firebase storage.
saveImageMessage = async(file: any) => {
  try {
    // 1 - Add a message with a loading icon that will get updated with the shared image.
    const messageRef = await this.addMessage(null, this.LOADING_IMAGE_URL);

    // 2 - Upload the image to Cloud Storage.
    const filePath = `${this.auth.currentUser?.uid}/${file.name}`;
    const newImageRef = ref(this.storage, filePath);
    const fileSnapshot = await uploadBytesResumable(newImageRef, file);

    // 3 - Generate a public URL for the file.
    const publicImageUrl = await getDownloadURL(newImageRef);

    // 4 - Update the chat message placeholder with the image's URL.
    messageRef ?
    await updateDoc(messageRef, {
      imageUrl: publicImageUrl,
      storageUri: fileSnapshot.metadata.fullPath
    }): null;
  } catch (error) {
    console.error('There was an error uploading a file to Cloud Storage:', error);
  }
}

이미지 전송 테스트

  1. '이미지를 게시하는 기능 추가'라는 커밋 메시지로 커밋을 만들고 GitHub 저장소에 푸시합니다.
  2. Firebase Console에서 앱 호스팅 페이지를 열고 새 출시가 완료될 때까지 기다립니다.
  3. FriendlyChat을 새로고침합니다. 로그인한 후 왼쪽 하단의 이미지 업로드 버튼 angularfire-4.png을 클릭하고 파일 선택 도구를 사용하여 이미지 파일을 선택합니다. 이미지를 찾고 있다면 이 커피 컵 사진을 사용해도 됩니다.
  4. 선택한 이미지와 함께 새 메시지가 앱의 UI에 표시됩니다. angularfire-2.png

로그인하지 않은 상태에서 이미지를 추가하려고 하면 이미지를 추가하려면 로그인해야 한다는 오류 메시지가 표시됩니다.

13. 알림 표시해 줘

이제 브라우저 알림 지원을 추가합니다. 채팅에 새 메시지가 게시되면 앱에서 사용자에게 알림을 보냅니다. Firebase 클라우드 메시징 (FCM)은 메시지와 알림을 무료로 안정적으로 전송할 수 있는 크로스 플랫폼 메시징 솔루션입니다.

FCM 서비스 워커 추가

웹 앱에는 웹 알림을 수신하고 표시하는 서비스 워커가 필요합니다.

AngularFire가 추가될 때 이미 메시지 제공업체가 설정되었을 것입니다. /angularfire-start/src/app/app.config.ts의 가져오기 섹션에 다음 코드가 있는지 확인합니다.

provideMessaging(() => {
    return getMessaging();
}),

app/app.config.ts

서비스 워커는 Firebase 클라우드 메시징 SDK를 로드하고 초기화하기만 하면 되며, 그러면 알림이 표시됩니다.

FCM 기기 토큰 가져오기

기기 또는 브라우저에서 알림이 사용 설정되면 기기 토큰이 제공됩니다. 이 기기 토큰은 특정 기기 또는 특정 브라우저에 알림을 전송하는 데 사용됩니다.

사용자가 로그인하면 saveMessagingDeviceToken 함수를 호출합니다. 여기에서 브라우저에서 FCM 기기 토큰을 가져와 Cloud Firestore에 저장합니다.

chat.service.ts

  1. saveMessagingDeviceToken 함수를 찾습니다.
  2. 함수 전체를 다음 코드로 대체합니다.

chat.service.ts

// Saves the messaging device token to Cloud Firestore.
saveMessagingDeviceToken= async () => {
    try {
      const currentToken = await getToken(this.messaging);
      if (currentToken) {
        console.log('Got FCM device token:', currentToken);
        // Saving the Device Token to Cloud Firestore.
        const tokenRef = doc(this.firestore, 'fcmTokens', currentToken);
        await setDoc(tokenRef, { uid: this.auth.currentUser?.uid });
 
        // This will fire when a message is received while the app is in the foreground.
        // When the app is in the background, firebase-messaging-sw.js will receive the message instead.
        onMessage(this.messaging, (message) => {
          console.log(
            'New foreground notification from Firebase Messaging!',
            message.notification
          );
        });
      } else {
        // Need to request permissions to show notifications.
        this.requestNotificationsPermissions();
      }
    } catch(error) {
      console.error('Unable to get messaging token.', error);
    };
}

하지만 이 코드는 처음에는 작동하지 않습니다. 앱이 기기 토큰을 가져오려면 사용자가 앱에 알림을 표시할 권한을 부여해야 합니다 (Codelab의 다음 단계).

알림 표시 권한 요청

사용자가 아직 앱에 알림을 표시할 권한을 부여하지 않은 경우 기기 토큰이 제공되지 않습니다. 이 경우 requestPermission() 메서드를 호출하면 이 권한을 요청하는 브라우저 대화상자가 표시됩니다 ( 지원되는 브라우저).

8b9d0c66dc36153d.png

  1. src/app/services/chat.service.ts 파일로 돌아갑니다.
  2. requestNotificationsPermissions 함수를 찾습니다.
  3. 함수 전체를 다음 코드로 대체합니다.

chat.service.ts

// Requests permissions to show notifications.
requestNotificationsPermissions = async () => {
    console.log('Requesting notifications permission...');
    const permission = await Notification.requestPermission();
    
    if (permission === 'granted') {
      console.log('Notification permission granted.');
      // Notification permission granted.
      await this.saveMessagingDeviceToken();
    } else {
      console.log('Unable to get permission to notify.');
    }
}

기기 토큰 가져오기

  1. '이미지를 게시하는 기능 추가'라는 커밋 메시지로 커밋을 만들고 GitHub 저장소에 푸시합니다.
  2. Firebase Console에서 앱 호스팅 페이지를 열고 새 출시가 완료될 때까지 기다립니다.
  3. FriendlyChat을 새로고침합니다. 로그인하면 알림 권한 대화상자가 표시됩니다. bd3454e6dbfb6723.png
  4. 허용을 클릭합니다.
  5. 브라우저의 JavaScript 콘솔을 엽니다. 다음 메시지가 표시됩니다. Got FCM device token: cWL6w:APA91bHP...4jDPL_A-wPP06GJp1OuekTaTZI5K2Tu
  6. 기기 토큰을 복사합니다. Codelab의 다음 단계에 필요합니다.

기기에 알림 전송하기

이제 기기 토큰이 있으므로 알림을 보낼 수 있습니다.

  1. Firebase Console의 클라우드 메시징 탭을 엽니다.
  2. '새 알림'을 클릭합니다.
  3. 알림 제목과 알림 텍스트를 입력합니다.
  4. 화면 오른쪽에서 '테스트 메시지 보내기'를 클릭합니다.
  5. 브라우저의 JavaScript 콘솔에서 복사한 기기 토큰을 입력한 다음 더하기 기호 ("+")를 클릭합니다.
  6. '테스트'를 클릭합니다.

앱이 포그라운드에 있으면 JavaScript 콘솔에 알림이 표시됩니다.

앱이 백그라운드에 있으면 다음 예와 같이 브라우저에 알림이 표시됩니다.

de79e8638a45864c.png

14. Cloud Firestore 보안 규칙

데이터베이스 보안 규칙 보기

Cloud Firestore는 특정 규칙 언어를 사용하여 액세스 권한, 보안, 데이터 유효성 검사를 정의합니다.

이 Codelab의 시작 부분에서 Firebase 프로젝트를 설정할 때 Datastore에 대한 액세스를 제한하지 않도록 '테스트 모드' 기본 보안 규칙을 사용하도록 선택했습니다. Firebase 콘솔데이터베이스 섹션에 있는 규칙 탭에서 이러한 규칙을 확인하고 수정할 수 있습니다.

현재 데이터 스토어에 대한 액세스를 제한하지 않는 기본 규칙이 표시됩니다. 즉, 모든 사용자가 데이터 스토어의 모든 컬렉션을 읽고 쓸 수 있습니다.

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write;
    }
  }
}

다음 규칙을 사용하여 제한사항을 적용하도록 규칙을 업데이트합니다.

firestore.rules

rules_version = '2';

service cloud.firestore {
  match /databases/{database}/documents {
    // Messages:
    //   - Anyone can read.
    //   - Authenticated users can add and edit messages.
    //   - Validation: Check name is same as auth token and text length below 300 char or that imageUrl is a URL.
    //   - Deletes are not allowed.
    match /messages/{messageId} {
      allow read;
      allow create, update: if request.auth != null
                    && request.resource.data.name == request.auth.token.name
                    && (request.resource.data.text is string
                      && request.resource.data.text.size() <= 300
                      || request.resource.data.imageUrl is string
                      && request.resource.data.imageUrl.matches('https?://.*'));
      allow delete: if false;
    }
    // FCM Tokens:
    //   - Anyone can write their token.
    //   - Reading list of tokens is not allowed.
    match /fcmTokens/{token} {
      allow read: if false;
      allow write;
    }
  }
}

보안 규칙이 에뮬레이터 모음으로 자동 업데이트됩니다.

Cloud Storage 보안 규칙 보기

Firebase용 Cloud Storage는 특정 규칙 언어를 사용하여 액세스 권한, 보안, 데이터 검증을 정의합니다.

이 Codelab의 시작 부분에서 Firebase 프로젝트를 설정할 때 인증된 사용자만 Cloud Storage를 사용할 수 있는 기본 Cloud Storage 보안 규칙을 사용하기로 선택했습니다. Firebase Console저장소 섹션에 있는 규칙 탭에서 규칙을 확인하고 수정할 수 있습니다. 로그인한 모든 사용자가 스토리지 버킷의 파일을 읽고 쓸 수 있는 기본 규칙이 표시됩니다.

rules_version = '2';

service firebase.storage {
  match /b/{bucket}/o {
    match /{allPaths=**} {
      allow read, write: if request.auth != null;
    }
  }
}

다음을 수행하도록 규칙을 업데이트합니다.

  • 각 사용자가 자신의 특정 폴더에만 쓸 수 있도록 허용
  • 모든 사용자가 Cloud Storage에서 읽을 수 있도록 허용
  • 업로드된 파일이 이미지인지 확인
  • 업로드할 수 있는 이미지의 크기를 최대 5MB로 제한

다음 규칙을 사용하여 구현할 수 있습니다.

storage.rules

rules_version = '2';

// Returns true if the uploaded file is an image and its size is below the given number of MB.
function isImageBelowMaxSize(maxSizeMB) {
  return request.resource.size < maxSizeMB * 1024 * 1024
      && request.resource.contentType.matches('image/.*');
}

service firebase.storage {
  match /b/{bucket}/o {
    match /{userId}/{messageId}/{fileName} {
      allow write: if request.auth != null && request.auth.uid == userId && isImageBelowMaxSize(5);
      allow read;
    }
  }
}

15. 수고하셨습니다.

Firebase를 사용하여 실시간 채팅 웹 애플리케이션을 빌드했습니다.

학습한 내용

  • Firebase App Hosting
  • Firebase 인증
  • Cloud Firestore
  • Cloud Storage용 Firebase SDK
  • Firebase 클라우드 메시징
  • Firebase Performance Monitoring

다음 단계

자세히 알아보기

16. [선택사항] 앱 체크로 적용

Firebase 앱 체크를 사용하면 원치 않는 트래픽으로부터 서비스를 보호하고 악용으로부터 백엔드를 보호할 수 있습니다. 이 단계에서는 앱 체크 및 reCAPTCHA Enterprise를 사용하여 사용자 인증 정보 유효성 검사를 추가하고 승인되지 않은 클라이언트를 차단합니다.

먼저 앱 체크와 reCaptcha를 사용 설정해야 합니다.

reCaptcha Enterprise 사용 설정

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

앱 체크 사용 설정

  1. Firebase Console의 왼쪽 패널에서 빌드 섹션을 찾습니다.
  2. 앱 체크를 클릭한 다음 로그인 방법 탭을 클릭하여 앱 체크로 이동합니다.
  3. 등록을 클릭하고 메시지가 표시되면 reCaptcha Enterprise 키를 입력한 후 저장을 클릭합니다.
  4. API 뷰에서 저장소를 선택하고 시행을 클릭합니다. Cloud Firestore에도 동일한 작업을 실행합니다.

이제 앱 체크가 적용됩니다. 앱을 새로고침하고 채팅 메시지를 보거나 보내 보세요. 다음과 같은 오류 메시지가 표시됩니다.

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

즉, 앱 체크는 기본적으로 유효성 검사가 이루어지지 않은 요청을 차단합니다. 이제 앱에 유효성 검사를 추가해 보겠습니다.

environment.ts 파일로 이동하여 environment 객체에 reCAPTCHAEnterpriseKey를 추가합니다.

export const environment = {
  firebase: {
    apiKey: 'API_KEY',
    authDomain: 'PROJECT_ID.firebaseapp.com',
    databaseURL: 'https://PROJECT_ID.firebaseio.com',
    projectId: 'PROJECT_ID',
    storageBucket: 'PROJECT_ID.firebasestorage.app',
    messagingSenderId: 'SENDER_ID',
    appId: 'APP_ID',
    measurementId: 'G-MEASUREMENT_ID',
  },
  reCAPTCHAEnterpriseKey: {
    key: "Replace with your recaptcha enterprise site key"
  },
};

key 값을 reCaptcha Enterprise 토큰으로 바꿉니다.

그런 다음 app.config.ts 파일로 이동하여 다음 가져오기를 추가합니다.

import { getApp } from '@angular/fire/app';
import {
  ReCaptchaEnterpriseProvider,
  initializeAppCheck,
  provideAppCheck,
} from '@angular/fire/app-check';

동일한 app.config.ts 파일에 다음과 같은 전역 변수 선언을 추가합니다.

declare global {
  var FIREBASE_APPCHECK_DEBUG_TOKEN: boolean;
}

@NgModule({ ...

가져오기에서 ReCaptchaEnterpriseProvider를 사용하여 App Check 초기화를 추가하고 isTokenAutoRefreshEnabledtrue로 설정하여 토큰이 자동 새로고침되도록 합니다.

imports: [
BrowserModule,
AppRoutingModule,
CommonModule,
FormsModule,
provideFirebaseApp(() => initializeApp(environment.firebase)),
provideAppCheck(() => {
const appCheck = initializeAppCheck(getApp(), {
  provider: new ReCaptchaEnterpriseProvider(
  environment.reCAPTCHAEnterpriseKey.key
  ),
  isTokenAutoRefreshEnabled: true,
  });
  if (location.hostname === 'localhost') {
    self.FIREBASE_APPCHECK_DEBUG_TOKEN = true;
  }
  return appCheck;
}),

로컬 테스트를 허용하려면 self.FIREBASE_APPCHECK_DEBUG_TOKENtrue로 설정합니다. localhost에서 앱을 새로고침하면 다음과 유사한 디버그 토큰이 콘솔에 로깅됩니다.

App Check debug token: CEFC0C76-7891-494B-B764-349BDFD00D00. You will need to add it to your app's App Check settings in the Firebase console for it to work.

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

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

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

chat.service.ts 파일로 이동하여 다음 가져오기를 추가합니다.

import { AppCheck } from '@angular/fire/app-check';

동일한 chat.service.ts 파일에서 다른 Firebase 서비스와 함께 앱 체크를 삽입합니다.

export class ChatService {
appCheck: AppCheck = inject(AppCheck);
...
  1. '앱 체크로 승인되지 않은 클라이언트 차단' 커밋 메시지로 커밋을 만들고 GitHub 저장소에 푸시합니다.
  2. Firebase Console에서 앱 호스팅 페이지를 열고 새 출시가 완료될 때까지 기다립니다.

수고하셨습니다. 이제 앱에서 앱 체크가 작동합니다.