Firebase Angular 웹 프레임워크 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>로 지정되지만 <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 로그인을 사용 설정하려면 다음 안내를 따르세요.

  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의 빌드 섹션에서 스토리지를 클릭합니다.
  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 문서를 읽고 업데이트해야 하므로 모든 페이지에 동일한 AngularFire 함수를 반복적으로 삽입하지 않도록 src/app/services/travel.service.ts에서 함수를 구현할 수 있습니다.

이전 단계와 마찬가지로 Auth를 삽입하고 서비스에 Firestore를 삽입합니다. 현재 인증 상태를 수신 대기하는 관찰 가능한 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)
}

데이터를 관측 가능한 항목으로 읽기

이동 과정에서 발생하는 이동 게시물과 정류장은 생성 후에 수정할 수 있으므로 문서 객체를 관찰 가능한 항목으로 가져와서 변경사항을 구독하는 것이 더 유용할 수 있습니다. 이 기능은 @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})
}

좋습니다. 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를 생성자에서 호출하여 모든 이동의 관찰 가능한 배열을 가져옵니다.

현재 사용자의 이동만 필요한 경우에는 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에서 파일에 액세스하는 것의 주요 차이점은 두 가지 모두 폴더 구조화된 경로를 따르지만 기본 URL과 경로 조합은 getDownloadURL를 통해 얻은 다음 파일에 저장하여 사용할 수 있다는 것입니다.

앱에서 함수 사용

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 Codelabs도 확인하세요.

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 및 Storage 데이터는 읽기 및 쓰기에 액세스할 수 있지만 사람들이 다른 사용자의 게시물을 변경하는 것을 원하지 않습니다. 보안 규칙을 사용하여 컬렉션 및 문서에 대한 액세스를 제한할 수 있습니다.

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