Firebase Angular Web 프레임워크 Codelab

1. 만들 항목

이 Codelab에서는 Angular 라이브러리의 최신 AngularFire를 사용하여 실시간 공동작업 지도로 여행 블로그를 빌드합니다. 최종 웹 앱은 여행한 각 위치에 이미지를 업로드할 수 있는 여행 블로그로 구성됩니다.

AngularFire는 웹 앱을 빌드하는 데 사용되며, 로컬 테스트를 위한 에뮬레이터 모음, 사용자 데이터를 추적하는 인증, Cloud Functions를 기반으로 데이터와 미디어를 유지하는 Firestore 및 Storage, 마지막으로 앱을 배포하는 Firebase 호스팅이 사용됩니다.

학습할 내용

  • 에뮬레이터 도구 모음을 사용하여 로컬에서 Firebase 제품으로 개발하는 방법
  • AngularFire로 웹 앱을 개선하는 방법
  • Firestore에 데이터를 유지하는 방법
  • Storage에 미디어를 유지하는 방법
  • Firebase 호스팅에 앱을 배포하는 방법
  • Cloud Functions를 사용하여 데이터베이스 및 API와 상호작용하는 방법

필요한 사항

  • Node.js 버전 10 이상
  • Firebase 프로젝트 생성 및 관리에 사용하는 Google 계정
  • Firebase CLI 버전 11.14.2 이상
  • 원하는 브라우저(예: Chrome)
  • Angular 및 JavaScript에 관한 기본 이해

2. 샘플 코드 가져오기

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

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

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

GitHub 저장소에는 여러 플랫폼을 위한 샘플 프로젝트가 포함되어 있습니다.

이 Codelab에서는 다음과 같이 웹 프레임워크 저장소만 사용합니다.

  • 📁 webframework: 이 Codelab에서 빌드할 시작 코드입니다.

종속 항목 설치

클론 후 웹 앱을 빌드하기 전에 루트 및 functions 폴더에 종속 항목을 설치합니다.

cd webframework && npm install
cd functions && npm install

Firebase CLI 설치

터미널에서 다음 명령어를 사용하여 Firebase CLI를 설치합니다.

npm install -g firebase-tools

다음을 사용하여 Firebase CLI 버전이 11.14.2보다 큰지 다시 확인합니다.

firebase  --version

버전이 11.14.2 미만인 경우 다음을 사용하여 업데이트하시기 바랍니다.

npm update firebase-tools

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

Firebase 프로젝트 만들기

  1. Firebase에 로그인합니다.
  2. Firebase Console에서 프로젝트 추가를 클릭한 후 Firebase 프로젝트의 이름을 <your-project>로 지정합니다. Firebase 프로젝트의 프로젝트 ID를 기억합니다.
  3. 프로젝트 만들기를 클릭합니다.

중요: Firebase 프로젝트의 이름은 <your-project>이지만 Firebase는 <your-project>-1234 형식으로 고유한 프로젝트 ID를 자동으로 할당합니다. 이 고유 식별자는 CLI를 포함하여 프로젝트를 실제로 식별하는 데 사용되며, <your-project>는 단순히 표시 이름입니다.

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

  • Firebase 인증: 사용자가 앱에 간편하게 로그인하도록 허용
  • Cloud Firestore: 클라우드에 구조화된 데이터를 저장하고 데이터가 변경되면 인스턴트 알림을 받을 수 있습니다.
  • Firebase용 Cloud Storage: 클라우드에 파일을 저장합니다.
  • Firebase 호스팅: 애셋을 호스팅하고 제공합니다.
  • 내부 및 외부 API와 상호작용하는 함수

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

프로젝트에 Firebase 웹 앱 추가

  1. 웹 아이콘을 클릭하여 새 Firebase 웹 앱을 만듭니다.
  2. 다음 단계에서 구성 객체가 표시됩니다. 이 객체의 콘텐츠를 environments/environment.ts 파일에 복사합니다.

Firebase 인증에 Google 로그인 사용 설정

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

Google 로그인을 사용 설정하려면 다음 단계를 따르세요.

  1. Firebase Console의 왼쪽 패널에서 빌드 섹션을 찾습니다.
  2. 인증을 클릭한 후 로그인 방법 탭을 클릭합니다(또는 여기를 클릭하여 로그인 방법 탭으로 바로 이동).
  3. Google 로그인 제공업체를 사용 설정한 다음 저장을 클릭합니다.
  4. 앱의 공개 이름을 <your-project-name>으로 설정하고 드롭다운 메뉴에서 프로젝트 지원 이메일을 선택합니다.

Cloud Firestore 사용 설정

  1. Firebase Console의 빌드 섹션에서 Firestore 데이터베이스를 클릭합니다.
  2. Cloud Firestore 창에서 데이터베이스 만들기를 클릭합니다.
  3. Cloud Firestore 데이터가 저장되는 위치를 설정합니다. 기본값으로 두거나 가까운 리전을 선택할 수 있습니다.

Cloud Storage 사용 설정

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

  1. Firebase Console의 빌드 섹션에서 Storage를 클릭합니다.
  2. 시작하기 버튼이 없으면 Cloud Storage가 이미 사용 중임을 의미합니다.

사용 설정되어 있으며 아래 단계를 따를 필요가 없습니다.

  1. 시작하기를 클릭합니다.
  2. Firebase 프로젝트의 보안 규칙에 관한 면책 조항을 읽은 후 다음을 클릭합니다.
  3. Cloud Storage 위치는 Cloud Firestore 데이터베이스에 선택한 리전과 동일한 리전으로 미리 선택됩니다. 완료를 클릭하여 설정을 완료합니다.

기본 보안 규칙을 사용하면 인증된 모든 사용자가 Cloud Storage에 무엇이든 쓸 수 있습니다. 이 Codelab의 후반부에서 저장소의 보안을 강화합니다.

4. Firebase 프로젝트에 연결

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

명령줄에서 앱의 로컬 webframework 디렉터리에 액세스하는지 확인합니다.

웹 앱 코드를 Firebase 프로젝트에 연결합니다. 먼저 명령줄에서 Firebase CLI에 로그인합니다.

firebase login

그런 다음 다음 명령어를 실행하여 프로젝트 별칭을 만듭니다. $YOUR_PROJECT_ID를 Firebase 프로젝트의 ID로 바꿉니다.

firebase  use  $YOUR_PROJECT_ID

AngularFire 추가

앱에 AngularFire를 추가하려면 다음 명령어를 실행합니다.

ng add @angular/fire

그런 다음 명령줄 안내에 따라 Firebase 프로젝트에 있는 기능을 선택합니다.

Firebase 초기화

Firebase 프로젝트를 초기화하려면 다음을 실행합니다.

firebase init

그런 다음 명령줄 메시지에 따라 Firebase 프로젝트에 사용된 기능과 에뮬레이터를 선택합니다.

에뮬레이터 시작

webframework 디렉터리에서 다음 명령어를 실행하여 에뮬레이터를 시작합니다.

firebase  emulators:start

최종적으로 다음과 같은 내용이 표시됩니다.

$  firebase  emulators:start

i  emulators:  Starting  emulators:  auth,  functions,  firestore,  hosting,  functions

i  firestore:  Firestore  Emulator  logging  to  firestore-debug.log

i  hosting:  Serving  hosting  files  from:  public

✔  hosting:  Local  server:  http://localhost:5000

i  ui:  Emulator  UI  logging  to  ui-debug.log

i  functions:  Watching  "/functions"  for  Cloud  Functions...

✔  functions[updateMap]:  firestore  function  initialized.

  

┌─────────────────────────────────────────────────────────────┐

│  ✔  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  │

├────────────────┼────────────────┼─────────────────────────────────┤

│  Functions  │  localhost:5001  │  http://localhost:4000/functions  │

├────────────────┼────────────────┼─────────────────────────────────┤

│  Firestore  │  localhost:8080  │  http://localhost:4000/firestore  │

├────────────────┼────────────────┼─────────────────────────────────┤

│  Hosting  │  localhost:5000  │  n/a  │

└────────────────┴────────────────┴─────────────────────────────────┘

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.

✔All emulators ready! 메시지가 표시되면 에뮬레이터를 사용할 수 있습니다.

아직 작동하지 않는 여행 앱의 UI가 표시됩니다.

이제 빌드를 시작해 보겠습니다.

5. 에뮬레이터에 웹 앱 연결

에뮬레이터 로그의 표에 따르면 Cloud Firestore 에뮬레이터는 포트 8080에서 리슨하고 인증 에뮬레이터는 포트 9099에서 리슨합니다.

EmulatorUI 열기

웹브라우저에서 http://127.0.0.1:4000/으로 이동합니다. 에뮬레이터 도구 모음 UI가 표시됩니다.

에뮬레이터를 사용하도록 앱 라우팅

src/app/app.module.ts에서 AppModule의 가져오기 목록에 다음 코드를 추가합니다.

@NgModule({
	declarations: [...],
	imports: [
		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;
		}),
		...
	]

이제 앱이 로컬 에뮬레이터를 사용하도록 구성되어 로컬에서 테스트 및 개발을 할 수 있습니다.

6. 인증 추가

이제 앱에 에뮬레이터가 설정되었으므로 각 사용자가 메시지를 게시하기 전에 로그인하도록 인증 기능을 추가할 수 있습니다.

이렇게 하려면 AngularFire에서 직접 signin 함수를 가져오고 authState 함수로 사용자의 인증 상태를 추적하면 됩니다. 로드 시 페이지에서 사용자 인증 상태를 확인하도록 로그인 페이지 함수를 수정합니다.

AngularFire 인증 삽입

src/app/pages/login-page/login-page.component.ts@angular/fire/auth에서 Auth를 가져와 LoginPageComponent에 삽입합니다. 인증 제공업체(예: Google) 및 signin, signout 등의 함수도 동일한 패키지에서 직접 가져와서 앱에서 사용할 수 있습니다.

import { Auth, GoogleAuthProvider, signInWithPopup, signOut, user } from  '@angular/fire/auth';

export  class  LoginPageComponent  implements  OnInit {
	private  auth: Auth = inject(Auth);
	private  provider = new  GoogleAuthProvider();
	user$ = user(this.auth);
	constructor() {}  

	ngOnInit(): void {} 

	login() {
		signInWithPopup(this.auth, this.provider).then((result) => {
			const  credential = GoogleAuthProvider.credentialFromResult(result);
			return  credential;
		})
	}

	logout() {
		signOut(this.auth).then(() => {
			console.log('signed out');}).catch((error) => {
				console.log('sign out error: ' + error);
		})
	}
}

이제 로그인 페이지가 작동합니다. 로그인해 보고 인증 에뮬레이터에서 결과를 확인합니다.

7. Firestore 구성하기

이 단계에서는 Firestore에 저장된 여행 블로그 게시물을 게시하고 업데이트하는 기능을 추가합니다.

인증과 마찬가지로 Firestore 함수는 AngularFire에서 사전 패키징됩니다. 각 문서는 컬렉션에 속하며 각 문서에는 중첩된 컬렉션이 있을 수도 있습니다. 여행 블로그 게시물을 만들고 업데이트하려면 Firestore에 있는 문서의 path를 알아야 합니다.

TravelService 구현

웹 앱에서 Firestore 문서를 읽고 업데이트해야 하는 페이지가 많으므로 src/app/services/travel.service.ts에서 함수를 구현하여 모든 페이지에 동일한 AngularFire 함수를 반복적으로 삽입하지 않도록 할 수 있습니다.

먼저 이전 단계와 유사한 AuthFirestore를 서비스에 삽입합니다. 현재 인증 상태를 수신 대기하는 관찰 가능한 user$ 객체를 정의하는 것도 유용합니다.

import { doc, docData, DocumentReference, Firestore, getDoc, setDoc, updateDoc, collection, addDoc, deleteDoc, collectionData, Timestamp } from  "@angular/fire/firestore";

export  class  TravelService {
	firestore: Firestore = inject(Firestore);
	auth: Auth = inject(Auth);
	user$ = authState(this.auth).pipe(filter(user  =>  user !== null), map(user  =>  user!));
	router: Router = inject(Router);

여행 게시물 추가

여행 게시물은 Firestore에 저장된 문서로 존재하며, 문서는 컬렉션 내에 있어야 하므로 모든 여행 게시물이 포함된 컬렉션의 이름은 travels입니다. 따라서 여행 게시물의 경로는 travels/가 됩니다.

AngularFire의 addDoc 함수를 사용하면 객체를 컬렉션에 삽입할 수 있습니다.

async  addEmptyTravel(userId: String) {
	...
	addDoc(collection(this.firestore, 'travels'), travelData).then((travelRef) => {
		collection(this.firestore, `travels/${travelRef.id}/stops`);
		setDoc(travelRef, {... travelData, id:  travelRef.id})
		this.router.navigate(['edit', `${travelRef.id}`]);
		return  travelRef;

	})
}

데이터 업데이트 및 삭제

여행 게시물의 UID를 통해 Firestore에 저장된 문서의 경로를 추론할 수 있습니다. 그런 다음 AngularFire의 updateFocdeleteDoc 함수를 사용하여 문서를 읽거나 업데이트하거나 삭제할 수 있습니다.

async  updateData(path: string, data: Partial<Travel | Stop>) {
	await  updateDoc(doc(this.firestore, path), data)
}

async  deleteData(path: string) {
	const  ref = doc(this.firestore, path);
	await  deleteDoc(ref)
}

관측 가능한 데이터로 데이터 읽기

그 과정에서 이동 게시물과 정류장은 생성 후 수정할 수 있으므로 문서 객체를 observable로 가져와서 모든 변경사항을 구독하는 것이 더 유용할 수 있습니다. 이 기능은 @angular/fire/firestoredocDatacollectionData 함수에서 제공합니다.

getDocData(path: string) {
	return  docData(doc(this.firestore, path), {idField:  'id'}) as  Observable<Travel | Stop>
}

  
getCollectionData(path: string) {
	return  collectionData(collection(this.firestore, path), {idField:  'id'}) as  Observable<Travel[] | Stop[]>
}

여행 게시물에 경유지 추가하기

이동 후 작업이 설정되었으므로 이제 정류장을 고려할 차례입니다. 정류장은 다음과 같이 여행 게시물의 하위 컬렉션 아래에 있습니다. travels//stops/

이는 여행 게시물 작성과 거의 동일하므로 직접 구현해 보거나 아래의 구현을 확인하세요.

async  addStop(travelId: string) {
	...
	const  ref = await  addDoc(collection(this.firestore, `travels/${travelId}/stops`), stopData)
	setDoc(ref, {...stopData, id:  ref.id})
}

좋은 소식입니다. 이제 Travel 서비스에 Firestore 함수가 구현되었으므로 작동하는 모습을 확인할 수 있습니다.

앱에서 Firestore 함수 사용

src/app/pages/my-travels/my-travels.component.ts로 이동하고 TravelService를 삽입하여 함수를 사용합니다.

travelService = inject(TravelService);
travelsData$: Observable<Travel[]>;
stopsList$!: Observable<Stop[]>;
constructor() {
	this.travelsData$ = this.travelService.getCollectionData(`travels`) as  Observable<Travel[]>
}

TravelService는 생성자에서 호출되어 모든 이동의 Observable 배열을 가져옵니다.

현재 사용자의 여행만 필요한 경우에는 query 함수를 사용합니다.

보안을 보장하는 다른 방법으로는 보안 규칙을 구현하거나 아래의 선택적 단계에서 살펴본 것처럼 Firestore와 함께 Cloud Functions를 사용하는 것이 있습니다.

그런 다음 TravelService에 구현된 함수를 호출하기만 하면 됩니다.

async  createTravel(userId: String) {
	this.travelService.addEmptyTravel(userId);
}

deleteTravel(travelId: String) {
	this.travelService.deleteData(`travels/${travelId}`)
}

이제 '내 여행' 페이지가 작동합니다. 새 여행 게시물을 만들 때 Firestore 에뮬레이터에서 어떤 일이 일어나는지 확인합니다.

그런 다음 /src/app/pages/edit-travels/edit-travels.component.ts의 업데이트 함수를 반복합니다.

travelService: TravelService = inject(TravelService)
travelId = this.activatedRoute.snapshot.paramMap.get('travelId');
travelData$: Observable<Travel>;
stopsData$: Observable<Stop[]>;

constructor() {
	this.travelData$ = this.travelService.getDocData(`travels/${this.travelId}`) as  Observable<Travel>
	this.stopsData$ = this.travelService.getCollectionData(`travels/${this.travelId}/stops`) as  Observable<Stop[]>
}

updateCurrentTravel(travel: Partial<Travel>) {
	this.travelService.updateData(`travels${this.travelId}`, travel)
}

  

updateCurrentStop(stop: Partial<Stop>) {
	stop.type = stop.type?.toString();
	this.travelService.updateData(`travels${this.travelId}/stops/${stop.id}`, stop)
}

  

addStop() {
	if (!this.travelId) return;
	this.travelService.addStop(this.travelId);
}

deleteStop(stopId: string) {
	if (!this.travelId || !stopId) {
		return;
	}
	this.travelService.deleteData(`travels${this.travelId}/stops/${stopId}`)
	this.stopsData$ = this.travelService.getCollectionData(`travels${this.travelId}/stops`) as  Observable<Stop[]>

}

8. 스토리지 구성

이제 이미지 및 기타 유형의 미디어를 저장하는 Storage를 구현합니다.

Cloud Firestore는 JSON 객체와 같은 구조화된 데이터를 저장하는 데 가장 적합합니다. Cloud Storage는 파일 또는 blob을 저장하도록 설계되었습니다. 이 앱에서 사용자가 여행 사진을 공유하도록 허용하는 데 사용합니다.

Firestore와 마찬가지로 Storage로 파일을 저장하고 업데이트하려면 파일마다 고유 식별자가 필요합니다.

TraveService에서 함수를 구현해 보겠습니다.

파일 업로드

src/app/services/travel.service.ts로 이동하여 AngularFire에서 Storage를 삽입합니다.

export  class  TravelService {
firestore: Firestore = inject(Firestore);
auth: Auth = inject(Auth);
storage: Storage = inject(Storage);

업로드 함수를 구현합니다.

async  uploadToStorage(path: string, input: HTMLInputElement, contentType: any) {
	if (!input.files) return  null
	const  files: FileList = input.files;
		for (let  i = 0; i  <  files.length; i++) {
			const  file = files.item(i);
			if (file) {
				const  imagePath = `${path}/${file.name}`
				const  storageRef = ref(this.storage, imagePath);
				await  uploadBytesResumable(storageRef, file, contentType);
				return  await  getDownloadURL(storageRef);
			}
		}
	return  null;
}

Firestore의 문서와 Cloud Storage의 파일에 액세스하는 것의 주된 차이점은 두 파일 모두 구조화된 폴더 경로를 따르지만 getDownloadURL를 통해 기본 URL과 경로 조합을 가져온 다음 파일에 저장하고 사용할 수 있다는 점입니다.

앱에서 함수 사용

src/app/components/edit-stop/edit-stop.component.ts로 이동하고 다음을 사용하여 업로드 함수를 호출합니다.

	async  uploadFile(file: HTMLInputElement, stop: Partial<Stop>) {
	const  path = `/travels/${this.travelId}/stops/${stop.id}`
	const  url = await  this.travelService.uploadToStorage(path, file, {contentType:  'image/png'});
	stop.image = url ? url : '';
	this.travelService.updateData(path, stop);
}

이미지가 업로드되면 미디어 파일 자체가 저장소에 업로드되고 URL이 Firestore의 문서에 저장됩니다.

9. 애플리케이션 배포

이제 애플리케이션을 배포할 준비가 되었습니다.

firebase 구성을 src/environments/environment.ts에서 src/environments/environment.prod.ts로 복사하고 다음을 실행합니다.

firebase deploy

다음과 같은 결과를 확인할 수 있습니다.

✔ Browser application bundle generation complete.
✔ Copying assets complete.
✔ Index html generation complete.

=== Deploying to 'friendly-travels-b6a4b'...

i  deploying storage, firestore, hosting
i  firebase.storage: checking storage.rules for compilation errors...
✔  firebase.storage: rules file storage.rules compiled successfully
i  firestore: reading indexes from firestore.indexes.json...
i  cloud.firestore: checking firestore.rules for compilation errors...
✔  cloud.firestore: rules file firestore.rules compiled successfully
i  storage: latest version of storage.rules already up to date, skipping upload...
i  firestore: deploying indexes...
i  firestore: latest version of firestore.rules already up to date, skipping upload...
✔  firestore: deployed indexes in firestore.indexes.json successfully for (default) database
i  hosting[friendly-travels-b6a4b]: beginning deploy...
i  hosting[friendly-travels-b6a4b]: found 6 files in .firebase/friendly-travels-b6a4b/hosting
✔  hosting[friendly-travels-b6a4b]: file upload complete
✔  storage: released rules storage.rules to firebase.storage
✔  firestore: released rules firestore.rules to cloud.firestore
i  hosting[friendly-travels-b6a4b]: finalizing version...
✔  hosting[friendly-travels-b6a4b]: version finalized
i  hosting[friendly-travels-b6a4b]: releasing new version...
✔  hosting[friendly-travels-b6a4b]: release complete

✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/friendly-travels-b6a4b/overview
Hosting URL: https://friendly-travels-b6a4b.web.app

10. 수고하셨습니다.

이제 애플리케이션이 완성되고 Firebase 호스팅에 배포되었습니다. 이제 Firebase Console에서 모든 데이터와 분석에 액세스할 수 있습니다.

AngularFire, Functions, 보안 규칙과 관련된 더 많은 기능을 알아보려면 아래의 선택적 단계와 기타 Firebase Codelab을 확인하세요.

11. 선택사항: AngularFire 인증 가드

AngularFire는 Firebase 인증과 함께 액세스 권한이 충분하지 않은 사용자를 리디렉션할 수 있도록 경로에 인증 기반 가드를 제공합니다. 이렇게 하면 보호된 데이터에 액세스하는 사용자로부터 앱을 보호할 수 있습니다.

src/app/app-routing.module.ts에서 가져오기

import {AuthGuard, redirectLoggedInTo, redirectUnauthorizedTo} from  '@angular/fire/auth-guard'

그런 다음 특정 페이지에서 사용자가 언제, 어디로 리디렉션되어야 하는지와 같은 함수를 정의할 수 있습니다.

const  redirectUnauthorizedToLogin = () =>  redirectUnauthorizedTo(['signin']);
const  redirectLoggedInToTravels = () =>  redirectLoggedInTo(['my-travels']);

그런 다음 경로에 추가하기만 하면 됩니다.

const  routes: Routes = [
	{path:  '', component:  LoginPageComponent, canActivate: [AuthGuard], data: {authGuardPipe:  redirectLoggedInToTravels}},
	{path:  'signin', component:  LoginPageComponent, canActivate: [AuthGuard], data: {authGuardPipe:  redirectLoggedInToTravels}},
	{path:  'my-travels', component:  MyTravelsComponent, canActivate: [AuthGuard], data: {authGuardPipe:  redirectUnauthorizedToLogin}},
	{path:  'edit/:travelId', component:  EditTravelsComponent, canActivate: [AuthGuard], data: {authGuardPipe:  redirectUnauthorizedToLogin}},
];

12. 선택사항: 보안 규칙

Firestore와 Cloud Storage 모두 보안 규칙(각각 firestore.rulessecurity.rules)을 사용하여 보안을 적용하고 데이터의 유효성을 검사합니다.

현재 Firestore 및 스토리지 데이터는 읽기 및 쓰기에 대한 공개 액세스 권한이 있지만 사용자가 다른 사용자의 게시물을 변경하는 것은 바람직하지 않습니다. 보안 규칙을 사용하여 컬렉션 및 문서에 대한 액세스를 제한할 수 있습니다.

Firestore 규칙

인증된 사용자만 여행 게시물을 볼 수 있도록 하려면 firestore.rules 파일로 이동하여 다음을 추가합니다.

rules_version  =  '2';
service  cloud.firestore  {
	match  /databases/{database}/travels  {
		allow  read:  if  request.auth.uid  !=  null;
		allow  write:
		if  request.auth.uid  ==  request.resource.data.userId;
	}
}

보안 규칙은 데이터를 검증하는 데도 사용할 수 있습니다.

rules_version  =  '2';
service  cloud.firestore  {
	match  /databases/{database}/posts  {
		allow  read:  if  request.auth.uid  !=  null;
		allow  write:
		if  request.auth.uid  ==  request.resource.data.userId;
		&&  "author"  in  request.resource.data
		&&  "text"  in  request.resource.data
		&&  "timestamp"  in  request.resource.data;
	}
}

스토리지 규칙

마찬가지로 보안 규칙을 사용하여 storage.rules의 스토리지 데이터베이스에 대한 액세스를 적용할 수 있습니다. 더 복잡한 검사에는 함수를 사용할 수도 있습니다.

rules_version  =  '2';

function  isImageBelowMaxSize(maxSizeMB)  {
	return  request.resource.size  <  maxSizeMB  *  1024  *  1024
		&&  request.resource.contentType.matches('image/.*');
}

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