AngularFire 웹 코드랩

1. 개요

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

angularfire-2.png

무엇을 배울 것인가

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

필요한 것

  • WebStorm , Atom , Sublime 또는 VS Code 등 원하는 IDE/텍스트 편집기
  • 일반적으로 Node.js 와 함께 제공되는 패키지 관리자 npm
  • 터미널/콘솔
  • Chrome 등 원하는 브라우저
  • Codelab의 샘플 코드(코드를 가져오는 방법은 Codelab의 다음 단계를 참조하세요.)

2. 샘플 코드 받기

명령줄에서 Codelab의 GitHub 저장소를 클론합니다.

git clone https://github.com/firebase/codelab-friendlychat-web

또는 git이 설치되어 있지 않은 경우 저장소를 ZIP 파일로 다운로드 할 수 있습니다.

시작 앱 가져오기

IDE를 사용하여 복제된 저장소에서 angularfire-start 디렉터리를 열거나 가져옵니다. 이 📁angularfire angularfire-start 디렉터리에는 완전한 기능을 갖춘 채팅 웹 앱이 될 Codelab의 시작 코드가 포함되어 있습니다.

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

Firebase 프로젝트 만들기

  1. Firebase 에 로그인합니다.
  2. Firebase 콘솔에서 Add Project 를 클릭한 다음 Firebase 프로젝트 이름을 FriendlyChat 으로 지정합니다. Firebase 프로젝트의 프로젝트 ID를 기억하세요.
  3. 이 프로젝트에 Google Analytics 활성화를 선택 취소하세요.
  4. 프로젝트 생성 을 클릭합니다.

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

  • 사용자가 앱에 쉽게 로그인할 수 있도록 하는 Firebase 인증입니다 .
  • Cloud Firestore는 구조화된 데이터를 클라우드에 저장하고 데이터가 변경되면 즉시 알림을 받습니다.
  • 클라우드에 파일을 저장하는 Firebase용 Cloud Storage
  • 자산을 호스팅하고 제공하는 Firebase 호스팅입니다 .
  • 푸시 알림을 보내고 브라우저 팝업 알림을 표시하는 Firebase 클라우드 메시징 .
  • 앱의 사용자 성능 데이터를 수집하는 Firebase 성능 모니터링 .

이러한 제품 중 일부는 특별한 구성이 필요하거나 Firebase 콘솔을 사용하여 활성화해야 합니다.

프로젝트에 Firebase 웹 앱 추가

  1. 웹 아이콘을 클릭하세요 58d6543a156e56f9.png 새로운 Firebase 웹 앱을 만듭니다.
  2. 닉네임 Friendly Chat 으로 앱을 등록한 다음 이 앱에 대한 Firebase 호스팅도 설정 옆의 확인란을 선택합니다. 앱 등록 을 클릭합니다.
  3. 다음 단계에서는 구성 개체를 볼 수 있습니다. 주변 HTML이 아닌 JS 객체만 firebase-config.js 에 복사합니다.

웹앱 스크린샷 등록

Firebase 인증을 위해 Google 로그인을 활성화합니다.

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

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

  1. Firebase 콘솔의 왼쪽 패널에서 빌드 섹션을 찾습니다.
  2. 인증 을 클릭한 다음 로그인 방법 탭을 클릭합니다(또는 여기를 클릭하여 바로 이동합니다).
  3. Google 로그인 공급자를 활성화한 다음 저장 을 클릭합니다.
  4. 앱의 공개 이름을 Friendly Chat 으로 설정하고 드롭다운 메뉴에서 프로젝트 지원 이메일을 선택하세요.
  5. Google Cloud Console 에서 OAuth 동의 화면을 구성하고 로고를 추가합니다.

d89fb3873b5d36ae.png

Cloud Firestore 활성화

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

Cloud Firestore를 활성화해야 합니다.

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

729991a081e7cd5.png

  1. 테스트 모드에서 시작 옵션을 선택한 후 보안 규칙에 대한 고지 사항을 읽은 후 다음을 클릭합니다.

테스트 모드에서는 개발 중에 데이터베이스에 자유롭게 쓸 수 있습니다. 이 Codelab의 뒷부분에서 데이터베이스를 더욱 안전하게 만들 것입니다.

77e4986cbeaf9dee.png

  1. Cloud Firestore 데이터가 저장되는 위치를 설정합니다. 이를 기본값으로 두거나 가까운 지역을 선택할 수 있습니다. 완료를 클릭하여 Firestore를 프로비저닝합니다.

9f2bb0d4e7ca49c7.png

클라우드 저장소 활성화

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

Cloud Storage를 사용 설정해야 합니다.

  1. Firebase 콘솔의 빌드 섹션에서 Storage 를 클릭합니다.
  2. 시작하기 버튼이 없다면 클라우드 저장소가 이미 활성화되어 있다는 의미이므로 아래 단계를 따를 필요가 없습니다.
  3. 시작하기 를 클릭합니다.
  4. Firebase 프로젝트의 보안 규칙에 대한 면책조항을 읽은 후 다음 을 클릭하세요.

기본 보안 규칙을 사용하면 인증된 사용자는 누구나 Cloud Storage에 무엇이든 쓸 수 있습니다. 이 Codelab의 뒷부분에서 저장소를 더욱 안전하게 만들 것입니다.

62f1afdcd1260127.png

  1. Cloud Storage 위치는 Cloud Firestore 데이터베이스에 대해 선택한 것과 동일한 지역으로 미리 선택되어 있습니다. 완료를 클릭하면 설정이 완료됩니다.

1d7f49ebaddb32fc.png

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

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

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

Firebase CLI 버전이 v4.1.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. 메시지가 표시되면 Firebase 콘솔에 설정된 기능( ng deploy -- hosting , Authentication , Firestore , Cloud Functions (callable) , Cloud Messaging , Cloud Storage )을 선택하고 콘솔에 표시되는 메시지를 따릅니다.

6. 로컬에서 스타터 앱 실행

이제 프로젝트를 가져오고 구성했으므로 처음으로 웹앱을 실행할 준비가 되었습니다.

  1. angularfire-start 디렉터리의 콘솔에서 다음 Firebase CLI 명령어를 실행합니다.
firebase emulators:start
  1. 명령줄에 다음 응답이 표시되어야 합니다.
✔  hosting: Local server: http://localhost:5000

Firebase 호스팅 에뮬레이터를 사용하여 앱을 로컬에서 제공하고 있습니다. 이제 http://localhost:5000 에서 웹 앱을 사용할 수 있습니다. src 하위 디렉터리에 있는 모든 파일이 제공됩니다.

  1. 브라우저를 사용하여 http://localhost:5000 에서 앱을 엽니다.

(아직!) 작동하지 않는 FriendlyChat 앱의 UI가 표시되어야 합니다.

anglefire-2.png

지금은 앱에서 아무것도 할 수 없지만 여러분의 도움으로 곧 할 수 있을 것입니다! 지금까지는 UI만 배치했습니다.

이제 실시간 채팅을 만들어 봅시다!

7. Firebase 가져오기 및 구성

Firebase 구성

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

  1. Firebase 콘솔에서 프로젝트 설정 으로 이동하세요.
  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",
    databaseURL: "https://PROJECT_ID.firebaseio.com",
    projectId: "PROJECT_ID",
    storageBucket: "PROJECT_ID.appspot.com",
    messagingSenderId: "SENDER_ID",
    appId: "APP_ID",
    measurementId: "G-MEASUREMENT_ID",
  },
};

AngularFire 가져오기

콘솔에서 선택한 기능이 /angularfire-start/src/app/app.module.ts 파일에 자동으로 라우팅되었음을 알 수 있습니다. 이를 통해 앱에서 Firebase 기능을 사용할 수 있습니다. 하지만 로컬 환경에서 개발하려면 에뮬레이터 제품군을 사용하도록 연결해야 합니다.

  1. /angularfire-start/src/app/app.module.ts 에서 imports 섹션을 찾고, 비프로덕션 환경에서 에뮬레이터 제품군에 연결하도록 제공된 기능을 수정합니다.
// ...

import { provideAuth,getAuth, connectAuthEmulator } from '@angular/fire/auth';
import { provideFirestore,getFirestore, connectFirestoreEmulator } from '@angular/fire/firestore';
import { provideFunctions,getFunctions, connectFunctionsEmulator } from '@angular/fire/functions';
import { provideMessaging,getMessaging } from '@angular/fire/messaging';
import { provideStorage,getStorage, connectStorageEmulator } from '@angular/fire/storage';

// ...

provideFirebaseApp(() => initializeApp(environment.firebase)),
provideAuth(() => {
    const auth = getAuth();
    if (location.hostname === 'localhost') {
        connectAuthEmulator(auth, 'http://127.0.0.1:9099', { disableWarnings: true });
    }
    return auth;
}),
provideFirestore(() => {
    const firestore = getFirestore();
    if (location.hostname === 'localhost') {
        connectFirestoreEmulator(firestore, '127.0.0.1', 8080);
    }
    return firestore;
}),
provideFunctions(() => {
    const functions = getFunctions();
    if (location.hostname === 'localhost') {
        connectFunctionsEmulator(functions, '127.0.0.1', 5001);
    }
    return functions;
}),
provideStorage(() => {
    const storage = getStorage();
    if (location.hostname === 'localhost') {
        connectStorageEmulator(storage, '127.0.0.1', 5001);
    }
    return storage;
}),
provideMessaging(() => {
    return getMessaging();
}),

// ...

app.module.ts

이 Codelab에서는 Firebase 인증, Cloud Firestore, Cloud Storage, 클라우드 메시징, Performance Monitoring을 사용하므로 해당 라이브러리를 모두 가져옵니다. 향후 앱에서는 필요한 Firebase 부분만 가져와서 앱 로드 시간을 단축하세요.

8. 사용자 로그인 설정

이제 AngularFire를 app.module.ts 에서 가져오고 초기화했으므로 사용할 준비가 되었습니다. 이제 Firebase 인증을 사용하여 사용자 로그인을 구현하겠습니다.

Google 로그인으로 사용자 인증

앱에서는 사용자가 Google로 로그인 버튼을 클릭하면 login 기능이 실행됩니다. (이미 설정해 놓았습니다!) 이 Codelab에서는 Google을 ID 공급업체로 사용하도록 Firebase에 승인하려고 합니다. 팝업을 사용하지만 Firebase에서 몇 가지 다른 방법을 사용할 수 있습니다.

  1. angularfire-start 디렉터리의 하위 디렉터리 /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를 업데이트하려면 사용자가 로그인했는지 또는 로그아웃했는지 확인하는 방법이 필요합니다. Firebase 인증을 사용하면 인증 상태가 변경될 때마다 트리거되는 사용자 상태에 대한 관찰 가능 항목을 검색할 수 있습니다.

  1. src/app/services/chat.service.ts 파일로 돌아갑니다.
  2. 변수 할당 user$ 찾으세요.
  3. 전체 할당을 다음 코드로 바꿉니다.

chat.service.ts

// Observable user
user$ = user(this.auth);

위의 코드는 관찰 가능한 사용자를 반환하는 AngularFire 함수 user 호출합니다. 인증 상태가 변경될 때마다(사용자가 로그인하거나 로그아웃할 때) 트리거됩니다. 이 시점에서 UI를 업데이트하여 리디렉션하고 헤더 탐색 메뉴에 사용자를 표시하는 등의 작업을 수행합니다. 이러한 UI 부분은 모두 이미 구현되었습니다.

앱 로그인 테스트

  1. 앱이 계속 제공되는 경우 브라우저에서 앱을 새로 고치세요. 그렇지 않은 경우 명령줄에서 firebase emulators:start 실행하여 http://localhost:5000 에서 앱 제공을 시작한 다음 브라우저에서 엽니다.
  2. 로그인 버튼과 Google 계정을 사용하여 앱에 로그인하세요. auth/operation-not-allowed 라는 오류 메시지가 표시되면 Firebase 콘솔에서 Google 로그인을 인증 공급자로 활성화했는지 확인하세요.
  3. 로그인하면 프로필 사진과 사용자 이름이 표시됩니다. anglefire-3.png

9. Cloud Firestore에 메시지 쓰기

이 섹션에서는 앱의 UI를 채울 수 있도록 Cloud Firestore에 일부 데이터를 작성합니다. 이 작업은 Firebase 콘솔을 사용하여 수동으로 수행할 수 있지만 기본적인 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>> => {
    let data: any;
    try {
      this.user$.subscribe(async (user) => 
      { 
        if(textMessage && textMessage.length > 0) {
          data =  await addDoc(collection(this.firestore, 'messages'), {
            name: user?.displayName,
            text: textMessage,
            profilePicUrl: user?.photoURL,
            timestamp: serverTimestamp(),
            uid: user?.uid
          })}
          else if (imageUrl && imageUrl.length > 0) {
            data =  await addDoc(collection(this.firestore, 'messages'), {
              name: user?.displayName,
              imageUrl: imageUrl,
              profilePicUrl: user?.photoURL,
              timestamp: serverTimestamp(),
              uid: user?.uid
            });
          }
          return data;
        }
      );
    }
    catch(error) {
      console.error('Error writing new message to Firebase Database', error);
      return;
    }
}

메시지 보내기 테스트

  1. 앱이 계속 제공되는 경우 브라우저에서 앱을 새로 고치세요. 그렇지 않은 경우 명령줄에서 firebase emulators:start 실행하여 http://localhost:5000 에서 앱 제공을 시작한 다음 브라우저에서 엽니다.
  2. 로그인 후 "Hey there!"와 같은 메시지를 입력한 후 SEND를 클릭하세요. 그러면 메시지가 Cloud Firestore에 기록됩니다. 그러나 데이터 검색을 구현해야 하기 때문에 실제 웹 앱에서는 아직 데이터가 표시되지 않습니다 (Codelab의 다음 섹션).
  3. Firebase 콘솔에서 새로 추가된 메시지를 확인할 수 있습니다. 에뮬레이터 제품군 UI를 엽니다. Build 섹션에서 Firestore Database를 클릭하거나 여기를 클릭하면 새로 추가된 메시지가 포함된 메시지 컬렉션이 표시됩니다.

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 limit(12) 사용하여 마지막 12개 메시지만 수신하고 orderBy('timestamp', 'desc') 사용하여 날짜별로 메시지를 정렬하여 12개의 최신 메시지를 가져오는 방식으로 제한을 적용합니다.

collectionData 함수는 내부적으로 스냅샷을 사용합니다. 쿼리와 일치하는 문서에 변경 사항이 있으면 콜백 함수가 트리거됩니다. 메시지가 삭제, 수정 또는 추가된 경우가 이에 해당할 수 있습니다. 이에 대한 자세한 내용은 Cloud Firestore 문서를 참조하세요.

메시지 동기화 테스트

  1. 앱이 계속 제공되는 경우 브라우저에서 앱을 새로 고치세요. 그렇지 않은 경우 명령줄에서 firebase emulators:start 실행하여 http://localhost:5000 에서 앱 제공을 시작한 다음 브라우저에서 엽니다.
  2. 이전에 데이터베이스에서 생성한 메시지가 FriendlyChat UI에 표시되어야 합니다(아래 참조). 새로운 메시지를 자유롭게 작성해 주세요. 즉시 나타나야 합니다.
  3. (선택사항) 에뮬레이터 제품군의 Firestore 섹션에서 직접 새 메시지를 수동으로 삭제, 수정, 추가해 볼 수 있습니다. 모든 변경 사항은 UI에 반영되어야 합니다.

축하해요! 앱에서 Cloud Firestore 문서를 읽고 있습니다!

anglefire-2.png

11. 이미지 보내기

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

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

Cloud Storage에 이미지 저장

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

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

이제 이미지를 보내는 기능을 추가하겠습니다.

  1. src/index.js 파일로 돌아갑니다.
  2. saveImageMessage 함수를 찾으세요.
  3. 전체 함수를 다음 코드로 바꿉니다.

index.js

// Saves a new message containing an image in Firebase.
// This first saves the image in Firebase storage.
saveImageMessage = async(file: any) => {
  try {
    // 1 - You 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. 앱이 계속 제공되는 경우 브라우저에서 앱을 새로 고치세요. 그렇지 않은 경우 명령줄에서 firebase emulators:start 실행하여 http://localhost:5000 에서 앱 제공을 시작한 다음 브라우저에서 엽니다.
  2. 로그인 후 좌측 하단의 이미지 업로드 버튼을 클릭하세요. anglefire-4.png 파일 선택기를 사용하여 이미지 파일을 선택합니다. 이미지를 찾고 있다면 이 멋진 커피잔 사진을 자유롭게 사용하세요.
  3. 선택한 이미지와 함께 앱 UI에 새 메시지가 표시되어야 합니다. anglefire-2.png

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

12. 알림 표시

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

FCM 서비스 워커 추가

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

메시징 공급자는 AngularFire가 추가되었을 때 이미 설정되어 있어야 합니다. /angularfire-start/src/app/app.module.ts 의 가져오기 섹션에 다음 코드가 있는지 확인하세요.

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

app/app.module.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. 앱이 계속 제공되는 경우 브라우저에서 앱을 새로 고치세요. 그렇지 않은 경우 명령줄에서 firebase emulators:start 실행하여 http://localhost:5000 에서 앱 제공을 시작한 다음 브라우저에서 엽니다.
  2. 로그인하면 알림 권한 대화 상자가 나타납니다. bd3454e6dbfb6723.png
  3. 허용 을 클릭합니다.
  4. 브라우저의 JavaScript 콘솔을 엽니다. 다음 메시지가 표시되어야 합니다. Got FCM device token: cWL6w:APA91bHP...4jDPL_A-wPP06GJp1OuekTaTZI5K2Tu
  5. 장치 토큰을 복사하세요. Codelab의 다음 단계에서 필요합니다.

기기에 알림 보내기

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

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

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

앱이 백그라운드에 있는 경우 다음 예와 같이 브라우저에 알림이 표시되어야 합니다.

de79e8638a45864c.png

13. Cloud Firestore 보안 규칙

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

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

이 Codelab을 시작할 때 Firebase 프로젝트를 설정할 때 데이터 저장소에 대한 액세스를 제한하지 않도록 '테스트 모드' 기본 보안 규칙을 사용하도록 선택했습니다. 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 콘솔저장소 섹션에 있는 규칙 탭에서 규칙을 보고 수정할 수 있습니다. 로그인한 모든 사용자가 스토리지 버킷에 있는 모든 파일을 읽고 쓸 수 있도록 허용하는 기본 규칙이 표시되어야 합니다.

rules_version = '2';

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

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

  • 각 사용자가 자신의 특정 폴더에만 쓸 수 있도록 허용
  • 누구나 Cloud Storage에서 읽을 수 있도록 허용
  • 업로드된 파일이 이미지인지 확인하세요.
  • 업로드할 수 있는 이미지 크기를 최대 5MB로 제한합니다.

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

스토리지.규칙

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

14. Firebase 호스팅을 사용하여 앱 배포

Firebase는 자산과 웹 앱을 제공하는 호스팅 서비스를 제공합니다. Firebase CLI를 사용하여 Firebase 호스팅에 파일을 배포할 수 있습니다. 배포하기 전에 배포해야 하는 로컬 파일을 firebase.json 파일에서 지정해야 합니다. 이 Codelab에서는 파일을 제공하는 데 이 단계가 필요했기 때문에 이 작업을 이미 수행했습니다. 호스팅 설정은 hosting 속성 아래에 지정됩니다.

Firebase.json

{
  // If you went through the "Cloud Firestore Security Rules" step.
  "firestore": {
    "rules": "firestore.rules"
  },
  // If you went through the "Storage Security Rules" step.
  "storage": {
    "rules": "storage.rules"
  },
  "hosting": {
    "public": "./public"
  }
}

이러한 설정은 ./public 디렉터리( "public": "./public" )의 모든 파일을 배포하려고 한다는 것을 CLI에 알려줍니다.

  1. 명령줄이 앱의 로컬 angularfire-start 디렉터리에 액세스하고 있는지 확인하세요.
  2. 다음 명령어를 실행하여 Firebase 프로젝트에 파일을 배포합니다.
ng deploy

그런 다음 Firebase 옵션을 선택하고 명령줄의 프롬프트를 따릅니다.

  1. 콘솔에 다음이 표시되어야 합니다.
=== Deploying to 'friendlychat-1234'...

i  deploying firestore, storage, hosting
i  storage: checking storage.rules for compilation errors...
✔  storage: rules file storage.rules compiled successfully
i  firestore: checking firestore.rules for compilation errors...
✔  firestore: rules file firestore.rules compiled successfully
i  storage: uploading rules storage.rules...
i  firestore: uploading rules firestore.rules...
i  hosting[friendlychat-1234]: beginning deploy...
i  hosting[friendlychat-1234]: found 8 files in ./public
✔  hosting[friendlychat-1234]: file upload complete
✔  storage: released rules storage.rules to firebase.storage/friendlychat-1234.appspot.com
✔  firestore: released rules firestore.rules to cloud.firestore
i  hosting[friendlychat-1234]: finalizing version...
✔  hosting[friendlychat-1234]: version finalized
i  hosting[friendlychat-1234]: releasing new version...
✔  hosting[friendlychat-1234]: release complete

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/friendlychat-1234/overview
Hosting URL: https://friendlychat-1234.firebaseapp.com
  1. 이제 자체 Firebase 하위 도메인 중 두 곳에서 Firebase 호스팅을 사용하여 글로벌 CDN에서 완전히 호스팅되는 웹 앱을 방문하세요.
  • https://<firebase-projectId>.firebaseapp.com
  • https://<firebase-projectId>.web.app

또는 명령줄에서 firebase open hosting:site 실행할 수 있습니다.

Firebase 호스팅 작동 방식 에 대해 자세히 알아보려면 설명서를 참조하세요.

프로젝트의 Firebase 콘솔 호스팅 섹션으로 이동하여 배포 기록, 앱의 이전 버전으로 롤백하는 기능, 맞춤 도메인을 설정하는 워크플로 등 유용한 호스팅 정보와 도구를 확인하세요.

15. 축하합니다!

Firebase를 사용하여 실시간 채팅 웹 애플리케이션을 구축하셨습니다.

당신이 다룬 내용

  • Firebase 인증
  • 클라우드 파이어스토어
  • Cloud Storage용 Firebase SDK
  • Firebase 클라우드 메시징
  • Firebase 성능 모니터링
  • Firebase 호스팅

다음 단계

더 알아보기

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

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

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

reCaptcha Enterprise 활성화

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

앱 체크 활성화

  1. Firebase 콘솔의 왼쪽 패널에서 빌드 섹션을 찾습니다.
  2. 앱 체크 를 클릭한 다음 로그인 방법 탭을 클릭하여 앱 체크 로 이동합니다.
  3. 등록을 클릭하고 메시지가 표시되면 reCaptcha Enterprise 키를 입력한 다음 저장을 클릭합니다.
  4. API 보기에서 Storage를 선택하고 Enforce를 클릭합니다. 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.appspot.com',
    messagingSenderId: 'SENDER_ID',
    appId: 'APP_ID',
    measurementId: 'G-MEASUREMENT_ID',
  },
  reCAPTCHAEnterpriseKey: {
    key: "Replace with your recaptcha enterprise site key"
  },
};

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

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

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

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

declare global {
  var FIREBASE_APPCHECK_DEBUG_TOKEN: boolean;
}

@NgModule({ ...

가져오기에서 ReCaptchaEnterpriseProvider 사용하여 앱 체크 초기화를 추가하고 isTokenAutoRefreshEnabled true 로 설정하여 토큰이 자동으로 새로 고쳐지도록 합니다.

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_TOKEN true 로 설정하세요. 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 콘솔에서 앱 체크의 앱 보기 로 이동합니다.

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

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

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

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

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

export class ChatService {
appCheck: AppCheck = inject(AppCheck);
...

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