Firebase Angular 웹 프레임워크 Codelab

1. 무엇을 만들 것인가

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

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

무엇을 배울 것인가

  • Emulator Suite를 사용하여 로컬에서 Firebase 제품을 개발하는 방법
  • AngularFire로 웹 앱을 향상시키는 방법
  • Firestore에서 데이터를 유지하는 방법
  • 저장소에 미디어를 유지하는 방법
  • 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 콘솔에서 프로젝트 추가를 클릭한 다음 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 콘솔을 사용하여 활성화해야 합니다.

프로젝트에 Firebase 웹 앱 추가

  1. 새 Firebase 웹 앱을 만들려면 웹 아이콘을 클릭하세요.
  2. 다음 단계에서는 구성 개체를 볼 수 있습니다. 이 개체의 내용을 environments/environment.ts 파일에 복사합니다.

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

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

Google 로그인을 활성화하려면:

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

Cloud Firestore 활성화

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

클라우드 저장소 활성화

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

  1. Firebase 콘솔의 빌드 섹션에서 Storage 를 클릭합니다.
  2. 시작하기 버튼이 없다면 클라우드 스토리지가 이미

활성화되어 있으므로 아래 단계를 따를 필요가 없습니다.

  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에서 수신 대기합니다.

에뮬레이터UI 열기

웹 브라우저에서 http://127.0.0.1:4000/ 으로 이동합니다. Emulator Suite 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)
}

관찰 가능 항목으로 데이터 읽기

여행지와 경유지는 생성 후 수정될 수 있으므로 문서 객체를 관찰 가능 항목으로 가져오고 변경 사항을 구독하는 것이 더 유용할 것입니다. 이 기능은 @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/ travels/ /stops/

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

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

멋진! Firestore 기능이 Travel 서비스에 구현되었으므로 이제 실제로 작동하는 모습을 볼 수 있습니다.

앱에서 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 기능을 이용하세요.

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

그런 다음 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. 애플리케이션 배포

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

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

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 콘솔에서 모든 데이터와 분석에 액세스할 수 있습니다.

AngularFire, 함수, 보안 규칙에 관한 더 많은 기능을 보려면 아래의 선택적 단계와 기타 Firebase Codelab을 확인하는 것을 잊지 마세요!

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

Firebase 인증과 함께 AngularFire는 경로에 대한 인증 기반 가드도 제공하므로 액세스 권한이 부족한 사용자를 리디렉션할 수 있습니다. 이는 사용자가 보호된 데이터에 액세스하지 못하도록 앱을 보호하는 데 도움이 됩니다.

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