본문 바로가기
Back-End/Firebase firestore

Angular 11 + firebase(firestore) 프로젝트 #03 - 데이터 CRUD -2 기능별 관리하기 + 확인까지

by Junmannn 2020. 12. 11.
반응형

이전편에서 일단 app.component.ts에서 모든 작업을 했었는데, 이러한 모든 기능들이 다 app.component에 몰려있으면 안되겟지 당연히. 모든 기능적인 함수가 main() 문에 다 때려박혀있는 지저분한 코드나 마찬가지이므로 일단은 분리를 해보자.

나는 기능별로  component로 나눠서 관리를 할 거야.

 

솔직한 마음으로는 이 1,2 편 작성하면서 이거 찾으려고 여기저기 다들 돌아다녔을게 뻔함...(경험)

 

이후 진행하다가 손쓸 수 없게 터지면 여기로 돌아오자. 초기 세팅한 코드들이라고 생각하면 됨

 

그럼 일단 component를 생성해야지. 이제 약자에 많이 익숙해짐ㅇㅇ

cd src -> cd app

그냥 app폴더 밑에 만들으라는거고 비주얼스튜디오 내에서 내장 터미널 여는거 했었지  ⌃ + ⇧ + ~  이거 누르면 됨

terminal에서 ng g c user 해서 user을 위한 컴포넌트를 생성하자. 

 

컴포넌트 (ng g c 컴포넌트이름)

: 화면구성. 
: 화면에 대한 모든 기능을 담당.
: 컴포넌트간의 데이터 - 컴포넌트에서 속성이나 Input 또는 Output같은 데코레이터 사용.
: 컴포넌트간의 데이터 공유는 서비스를 통해 구독(subscribe)하는 형태로도 공유.

 

user.component.ts에 아까 app.component.ts에 있던 내용들을 옮긴다.

user.component.ts

import { Component, OnInit } from '@angular/core';
import { AngularFirestore, AngularFirestoreCollection, CollectionReference } from '@angular/fire/firestore';
import { AngularFireStorage } from '@angular/fire/storage';

@Component({
  selector: 'app-user',
  templateUrl: './user.component.html',
  styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
  ngOnInit(): void { }

  private database : AngularFirestore;  // 파이어스토어를 쓸건데 그건 private해야겠지 당연히. database는 내가 지은 이름이고.
  private itemCollection: AngularFirestoreCollection<any>;  //collection을 가져올건데 일단 다 가져와

  collections = new Array();
  private storage : AngularFireStorage; // 나중에 쓸 저장소인데 이거 써볼라다가 일단 지웟음 일단 안 쓰고 있는 다

  tang: object; // 지금 user에 객체의 상태로 2개가 만들어져있으니까 객체 타입으로 받는다 하고

  constructor(db : AngularFirestore) {  // AngularFirestore을 db라는 이름으로 가져올거고
    this.database = db; // this.database 가 AngularFirestore 이라는 걸 알려주고
    this.getItem('user').subscribe((res)=> {  // 아까 firestore페이지에서 user 라는 collection 만들었었잖아. 그거 구독하자.
      console.log(res);     // F12 눌러서 나오는 콘솔에 내용을 찍어보자
      this.tang = this.database;
      this.collections = res;  
    });
  }

  getItem(db_name : string){  // getItem() 의 인자로 들어가는 이름의 collection에 접근을 할 거라고 설정
    this.itemCollection = this.database.collection<any>(db_name, (ref : CollectionReference) => {
      return ref.orderBy('phone_number','asc');  //시작, 한계점 추가
    });  
    return this.itemCollection.valueChanges();  //리턴
  }
}

 

로그인하는 컴포넌트도 만들어줄거다

ng g c login

이놈이 로그인을 제대로 한 놈인지 확인하는 ask 라는 서비스를 만들거다

ng g s ask

ask에서 만든 로그인 했냐? 확인하는 함수를 가드가 쓰게 할거야. 그런 auth 라는 가드를 만들거다

ng g g auth

→ CanActivate

 

기능 넣을거니까 쫄지마 쫄지마. 내가 이때까지 삽질하고 프로젝트 여러번 밀고 그러면서 짜놓은 안전한 코드니까 이거 그냥 복붙해도 됨 이건 전체 선택 해서 전체 붙여넣기 하면 됨

login.component.ts

import { Component, OnInit, OnDestroy } from '@angular/core';
import { AskService } from '../ask.service';
import { Router } from '@angular/router';
import { Subscription } from 'rxjs';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit, OnDestroy {

  id : string;      // 화면 상에서 입력할 id 에 해당할 부분
  passwd : string;  // 화면 상에서 입력할비밀번호에 해당될 부분

  constructor(private service : AskService, private rout: Router) { 
      // constructor. 그러니까 얘가 시작이 될 때에 바로 consttructor가 실행됨. 실행되는 서비스로 Ask 서비스를 돌리고, rout는 라우터라고 만들어놓았던 라우팅이 시작
  }

  ngOnInit(): void { }

  private script : Subscription;  // 여기에 로그인 했을때의 결과를 담을 거야

  login() { 	// 화면상에서 누르면 이 기능이 실행되게끔 하는 함수야
    if(this.script){
      console.log(this.script);
      this.script.unsubscribe();  //구독 종료
    }
    this.script = this.service.tryToLogin({id : this.id, passwd : this.passwd}).subscribe( (arg:any)=>{
      if(arg.status == true){ // 둘 다 맞아서 true 되눈 경우에만 이렇게 하라
        alert('로그인 성공!') // 이러면 화면 상에 팝업 뜨면서 로그인 성공 뜸
        this.rout.navigate(['/user']);  // 로그인 성공 하면 user이라고 라우팅 해놓은 곳으로 가라는 소리임
      }
    });
  }
  ngOnDestroy(): void { // 파괴될 때에는 구독 종료
    console.log(this.script);
    if(this.script){
      this.script.unsubscribe(); //구독 종료
    }
  }
}

 로그인하는 component를 만들었으면 로그인 화면, 그러니까 login.component.html을 꾸미자

login.component.html

<input type="text" placeholder="아이디" [(ngModel)]='id' />
<input type="text" placeholder="비밀번호" [(ngModel)]='passwd'/>
<input type="button" value="로그인" (click)='login()'/>

아이디, 비밀번호 넣는 input 필드 만들었고, 로그인이라는 버튼을 누르는 순간 내가 위에 login() 함수만들었던것이 실행되도록 함. onclick 을 이렇게 표현되는거임. 쫄지마 모르는거 나와도

 

로그인 기능을 담당할 컴포넌트를 만들었고, 이런 컴포넌트들을 실행시키려고 ask라는 서비스를 만들었었지. 이번엔 서비스를 구현해보자.

ask.service.ts

import { Injectable } from '@angular/core';
import { AngularFirestore, AngularFirestoreCollection, CollectionReference } from '@angular/fire/firestore';
import { BehaviorSubject, Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class AskService {

  private database: AngularFirestore;
  private itemCollection: AngularFirestoreCollection<any>;

  //저장소(나중에 데이터베이스 서버)
  private readonly storage = {  
    id: 'bearbook',
    passwd: 'club'
  } // 지금 당장에는 이 bearbook, club 이 입력되지 않으면 login 화면에서 user 화면으로 넘기지 않을거야

  constructor(db: AngularFirestore) {   // Firestore을 db라고 부를거야.
    this.database = db;                 // database = 이 Forestore 넣어주고
  }

  getItem(db_name: string) {
    this.itemCollection = this.database.collection<any>(db_name, (ref: CollectionReference) => {
      return ref;
    });  //리턴
    return this.itemCollection; // itemCollection이라 한거를 퉤 뱉어라
  }

  addItem(){  
    // 나중에 firestore에 데이터 집어넣는 코드 개발할거고
  }

  //로그인을 시도하는 함수
  tryToLogin(your_input: any) {
    return new Observable(arg => {
      if (your_input.id == this.storage.id && your_input.passwd == this.storage.passwd) {
        arg.next({ status: true });
        localStorage.setItem('status', "true");
      } else {
        arg.next({ status: false, reason: 'wrong information' });
      }
      arg.complete();
    });
  }

  readonly isLogged: BehaviorSubject<boolean> = new BehaviorSubject(false); // auth 에서는 이걸 볼 거야

  //로그인이 되었는지 확인하는 함수
  isLogIn(): void {
    if (localStorage.getItem('status') == 'true') { // getItem 해봐서 true 그러니까 로그인이 되어있으면 next로 넘어가라
      this.isLogged.next(true);
    } else {
      this.isLogged.next(false);  // 로그인이 되어있지 않다면 갖다 버려라
    }
  }
}

서비스 (ng g s 서비스 이름)

: 비지니스 로직과 같은 형태의 기능.
: 자주 사용되는 함수나 데이터등을 서비스로 만든 뒤 모듈에서 의존성 주입(Inject)을 통해서 컴포넌트에게 제공.

: 컴포넌트는 생성자를 통해서 이러한 서비스를 받아서 사용.

 

우리는 이런 서비스를 만들었고, 서비스를 실행시킴에 있어서 사용자의 요청이 컴포넌트로 전달되기 전에 클럽 가드처럼 슥 보면서 확인을 하는거지 이놈을 보내도 되나~? 하면서.

ex) 클럽가드, 방화벽 .... 뭐 이런 식의 설명이려나 하여튼 고쳐보자

auth.guard.ts

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';
import { AskService } from './ask.service';

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {

  private status : boolean = false;

  constructor(private serv : AskService){
    serv.isLogged.subscribe( result=>{
      console.log('서버에 물어보고온 결과', result);
      this.status = result;
    });
  }

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    this.serv.isLogIn();
    return this.status;
  }
}

가드 (ng g g 가드 이름)

: 일종의 서비스의 형태이며, 모듈에서 라우터 정보에서 canActivate 에 추가하여 사용 가능.
: 사용자의 요청이 컴포넌트에게 전달되기 전(pre) 동작을 하며 조건을 통해 해당 컴포넌트로 이동할지 말지를 결정.

 

마지막으로 바로 보일 화면을 조정해봅시다

난잡한거 딱 싫다했음. 일단 다 지우고 심플하게 가자고

app.component.html

<h2> Login 하면 User들이 보일 것입니다</h2>
<app-login></app-login>

 

실행 화면

 

 

 

반응형