환경 설정
* 환경 : firebase DB & REST API
1. firebase에서 프로젝트를 선택(or 생성)합니다.
2. Database -> 규칙
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}

3. Authntication -> 로그인 방법 -> 이메일/비밀번호 활성화

유저 생성
아래와 같이 구글에서 firebase auth rest api를 검색합니다.
https://firebase.google.com/docs/reference/rest/auth
Firebase Auth REST API | Firebase
API Usage You can query the Firebase Auth backend through a REST API. This can be used for various operations such as creating new users, signing in existing ones and editing or deleting these users. Throughout this document, API_KEY refers to the Web API
firebase.google.com
사이트 오른쪽 목차에서 Sign up with email / password 로 아래와 같은 조건을 만족시키는 통신을 합니다.
Method: POST
Content-Type: application/json
Request Body Payload
Property | NameType | Description |
string | 유저 생성을 위한 email | |
password | string | 유저 생성을 위한 비밀번호 |
returnSecureToken | boolean |
ID 와 새로고침 토큰을 반환 하는지 안하는지, 항상 true입니다. |
post로 https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=[API_KEY] 를 합니다.
API_KEY는 화면 왼쪽 drawer 에서 톱니바퀴(설정)을 눌러 확인할 수 있습니다.

내 프로젝트 [웹 API 키]를 복사하여 이용합니다.
// auth.service.ts
signup(email: string, password: string) {
return this.http
.post(
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=AIzaSyC4NdTkUmluvTX-wfuFKVZPdFCXvZKiWEQ',
{
email: email,
password: password,
returnSecureToken: true
}
)
Observable을 반환하기 때문에 return을 사용해줍니다.
Response Payload
Property | NameType | Description |
idToken | string | 신규 유저의 Auth ID 토큰. |
string | 신규 유저를 위한 email. | |
refreshToken | string | 신규 유저를 위한 Auth 새로고침 토큰. |
expiresIn | string | ID 토큰의 만료를 초를 숫자로 나타냅니다. |
localId | string | The uid of the newly created user. |
리스폰스에 알맞는 데이터를 받기 위해 아래와 같은 인터페이스를 만들어 포스트의 리턴타입을 입력합니다.
// auth.service.ts
export interface AuthResponseData {
idToken: string;
email: string;
refreshToken: string;
expiresIn: string;
localId: string;
}
signup(email: string, password: string) {
return this.http
.post<AuthResponseData>(
'https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=AIzaSyC4NdTkUmluvTX-wfuFKVZPdFCXvZKiWEQ',
{
email: email,
password: password,
returnSecureToken: true
}
)
// auth.component.ts
onSubmit(form: NgForm) {
if (!form.valid) {
return;
}
const email = form.value.email;
const password = form.value.password;
this.authService.subscribe(
resData => {
console.log(resData);
},
errorMessage => {
console.log(errorMessage);
}
);
form.reset();
}
로딩 스피너
1. 컴포넌트 만들기
share폴더에
loading-spinner 폴더와 ts, css파일을 생성합니다.
loading-spinner.component.ts
loading-spinner.component.css
구글에 검색합니다. css loading spinners https://loading.io/css/
해당 사이트에서 원하는 스피너를 선택한 뒤에, css에 값과 html값을 복붙합니다.
// loading-spinner.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-loading-spinner',
template:
'<div class="lds-ring"><div></div><div></div><div></div><div></div></div>',
styleUrls: ['./loading-spinner.component.css']
})
export class LoadingSpinnerComponent {}
2. HTML에 추가하기
<!-- auth.component.html -->
<div class="row">
<div class="col-xs-12 col-md-6 col-md-offset-3">
<div *ngIf="isLoading">
<app-loading-spinner></app-loading-spinner>
</div>
<form #authForm="ngForm" (ngSubmit)="onSubmit(authForm)" *ngIf="!isLoading">
<!-- ... -->
</form>
</div>
</div>
form 태그에 isLoading을 이용하여 조건별로 나타날 수 있도록 합니다.
에러 핸들링
UI를 위해 error를 추가합니다.
<!-- auth.component.html -->
<div class="row">
<div class="col-xs-12 col-md-6 col-md-offset-3">
<div class="alert alert-danger" *ngIf="error">
<p>{{ error }}</p>
</div>
<div *ngIf="isLoading">
<app-loading-spinner></app-loading-spinner>
</div>
<form #authForm="ngForm" (ngSubmit)="onSubmit(authForm)" *ngIf="!isLoading">
<!-- ... -->
</form>
</div>
</div>
// auth.component.ts
isLoading = false;
error: string = null;
onSubmit(form: NgForm) {
if (!form.valid) {
return;
}
const email = form.value.email;
const password = form.value.password;
this.isLoading = true;
this.authService.subscribe(
resData => {
console.log(resData);
this.isLoading = false;
},
errorMessage => {
console.log(errorMessage);
this.error = errorMessage;
this.isLoading = false;
}
);
form.reset();
}
firebase auth rest api 에서 조금 더 아래로 내리면 아래와 같은 내용을 발견할 수 있습니다.
Common error codes
- EMAIL_EXISTS: The email address is already in use by another account.
- OPERATION_NOT_ALLOWED: Password sign-in is disabled for this project.
- TOO_MANY_ATTEMPTS_TRY_LATER: We have blocked all requests from this device due to unusual activity. Try again later.
errorRes를 보면,
error안에 error가 있고, message를 보면, EAMIL_EXISTS와 같은 코드를 받을 수 있습니다.
에러 코드를 이용하면 사용자에게 쉽게 안내할 수 있습니다.
errorRes => {
console.log(errorRes);
switch (errorRes.error.error.message) {
case 'EMAIL_EXISTS':
this.error = 'This email exists already';
break;
case 'EMAIL_NOT_FOUND':
this.error = 'This email does not exist.';
break;
case 'INVALID_PASSWORD':
this.error = 'This password is not correct.';
break;
}
}
로그인 만료 시간 핸들링
로그인 하기 전에는 브라우져로 다시 들어갈 경우, 지속적으로 로그인 상태가 됩니다.
이런 상황을 방지하기 위한 방법 입니다.
Response Payload
Property NameTypeDescription
expiresIn | string | The number of seconds in which the ID token expires. |
expiresIn은 ID토큰의 만료 시간입니다.
const expirationDate = new Date(new Date().getTime() + expiresIn * 1000);
곱하기 1000을 해서 밀리세컨드(ms = 1/1000 초)를 하여 생성되는 아이디의 초과 시간을 Local에 저장해 둡니다.
Auto Loggin
로컬 스토리지에 userData저장.
localStorage.setItem('userData', JSON.stringify(user)); // JSON.stringify()를 꼭사용해주세요.
불러오기
autoLogin() {
// userData snapshot을 불러옵니다.
const userData: {
email: string;
id: string;
_token: string;
_tokenExpirationDate: string;
} = JSON.parse(localStorage.getItem('userData')); // JSON.parse로 반드시 파싱합니다.
if (!userData) { // 데이터가 없을경우 끗.
return;
}
const loadedUser = new User(
userData.email,
userData.id,
userData._token,
new Date(userData._tokenExpirationDate)
);
// 로그인 제한시간을 확인합니다.
if (loadedUser.token) {
this.user.next(loadedUser);
const expirationDuration =
new Date(userData._tokenExpirationDate).getTime() -
new Date().getTime();
this.autoLogout(expirationDuration);
}
}
Guard (Router Protection)
// auth.guard.ts
import {
CanActivate,
ActivatedRouteSnapshot,
RouterStateSnapshot
} from '@angular/router';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { AuthService } from './auth.service';
@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService) {}
canActivate(
route: ActivatedRouteSnapshot,
router: RouterStateSnapshot
):
| boolean
| Promise<boolean>
| Observable<boolean> {
return this.authService.user.pipe(
map(user => {
return !!user;
})
);
}
}
Activate 인터페이슨는 route와 state(RouterStateSnapshot)을 필요로 합니다.
또한 boolean, Promise<boolean>, Observable<boolean>의 형태로 반환해야 합니다.
Authenticated User는 pipe(map())으로 묶어서 반환합니다.
// app-routing.module.ts
canActivate: [AuthGuard]//를 추가합니다
리다이렉트 시키기!
URLTree를 이용하여 원하는 곳으로 이동시킬 수 있습니다.
constructor(private authService: AuthService, private router: Router) {}
canActivate(
route: ActivatedRouteSnapshot,
router: RouterStateSnapshot
):
| boolean
| UrlTree
| Promise<boolean | UrlTree>
| Observable<boolean | UrlTree> {
return this.authService.user.pipe(
take(1), // 불필요한 subscribe를 방지합니다. 1번만 실행.
map(user => {
const isAuth = !!user;
if (isAuth) {
return true;
}
return this.router.createUrlTree(['/auth']); // URLTree를 생성하여 login하도록 리다이렉트.
})
// angular 옛 버전에서는 직접 리다이렉트 주소를 기입해야 합니다.
// tap(isAuth => {
// if (!isAuth) {
// this.router.navigate(['/auth']); // 일일히 리다이렉트 주소 입력.
// }
// })
);
'프로그래밍 > Angular' 카테고리의 다른 글
Service VS EventEmitter (0) | 2020.02.19 |
---|---|
Dynamic Component (0) | 2019.09.13 |
Http (0) | 2019.08.20 |
Pipe (0) | 2019.08.15 |
Form - 복습 및 보충 내용 (0) | 2019.08.14 |