본문 바로가기
블로그 이미지

방문해 주셔서 감사합니다! 항상 행복하세요!

  
   - 문의사항은 메일 또는 댓글로 언제든 연락주세요.
   - "해줘","답 내놔" 같은 질문은 답변드리지 않습니다.
   - 메일주소 : lts06069@naver.com


앵귤러, 리엑트, 뷰/Angular Tutorial(old)

앵귤러 튜토리얼 (Angular tutorial) -21 with 파이어 베이스

야근없는 행복한 삶을 위해 ~
by 마샤와 곰 2019. 7. 16.

* 제가 다시 작성한 최신 튜토리얼 수정본 입니다. 아래 주소를 통해서 진행하시는 것을 권장 드립니다. ^^

lts0606.tistory.com/328

 

앵귤러 튜토리얼(Angular tutorial) - 1

안녕하세요. 앵귤러에 대해서 알아보기위해 이곳을 찾아주신 분 들께 감사의 말씀 드립니다.^^ 천천히, 초심자도 조금 더 쉽게 접근할 수 있도록 내용을 구성하여 보겠습니다. 어��

lts0606.tistory.com

 

이번시간에는 삭제 기능에 대해 알아보도록 하겠다.
삭제기능은 저번시간에 하였던 update라는 기능과 거의 동일하다.
저번시간에 살펴본 기능에서 문제점은 모두 수정되는 점이 문제였다면 이번시간에도 그냥 단순하게 삭제기능을 구현한다면 마찬가지 문제가 발생 할 것 이다.
그렇다면 수정 및 삭제를 위한 고유 ID값은 어떻게 가져와야 하는 것 인가?
2가지 방법으로 나누어 볼 수 있다.
먼저 첫번째는 사용자에 의해서 임의의 ID값을 주는 방법이다.

//...생략
  constructor(db : AngularFirestore,strg : AngularFireStorage) {   //모듈에서 만들어진 파이어 베이스 접속관련 객체
    this.DataBase = db;
    this.storage = strg;

    this.getItem('board').subscribe((res)=>{  //리스트를 조회하는 구독행위
      console.log(res);
    });

    this.setItem('board',{name:'NAME',number:1234,idx:this.makeID(30)});  //삭제를 위해 idx라는 속성을 추가
  }  

  makeID(length) {  //랜덤 ID를 만드는 메소드
    var result = '';
    var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    var charactersLength = characters.length;
    for ( var i = 0; i < length; i++ ) {
       result += characters.charAt(Math.floor(Math.random() * charactersLength));
    }
    return result;
 }

위 방법데로 한다면 idx라는 속성을 만들어서 추가한다음에 KEY 형식으로 사용하는 방법이다.

즉 key를 직접 만들어 적용하는 점 이다.

두번째 방법으로, 파이어베이스 데이터베이스에서 고유로 만들어진 ID를 사용하는 것 이다.
해당 ID는 데이터를 가져오는 부분을 변경해서 사용 할 수 있다.

//..생략
  getItem(db_name : string){
    var items : BehaviorSubject<any[]> = new BehaviorSubject([]);
    //this.getCollections(db_name).stateChanges();    //기존방식
    return this.getCollections(db_name).stateChanges().pipe(map( changes => {  //바꾼 방식
      return changes.map(a=>{
        const ID = a.payload.doc.id;
        const data = a.payload.doc.data() as any;
        return {data:data,idx:ID};
      });
    }))
  }

두번째 방법으로 사용하면 키를 가져오는 점이 쉽긴한데..구독상태를 바꾸고 조립하는 복잡한 과정이 들어가야한다..
음..쉽게 이야기 하면 첫번째 방법이 두번째 방법보다 쉽다는 점 이다.
여기서는 첫번째 방식으로 구현하여 보겠다.

삭제 함수는 기존의 수정하는 함수와 거의 동일하다.

//..생략
  deleteItem(db_name : string, key: any){
    var subscription = this.getCollections(db_name).stateChanges().pipe(map( changes => { //pip 함수는 map 함수를 붙여주는 역할을 한다.
      return changes.map(a=>{  //map 함수는 데이터의 내용을 바꾸는 역할을 한다.
        const data = a.payload.doc.data() as any;
        const ID = a.payload.doc.id; //고유 아이디 값
        if(data.idx == key){  // idx는 사용자가 만들어준 키 값 이다.
          this.getCollections(db_name).doc(ID).delete();  //key를 통해 삭제한다.
        }
        return data;
      });
    })).subscribe((oo)=>{  //해당 수정행위를 구독한다.
      subscription.unsubscribe();  //getItem 함수를 통해 구독을 하고 있기 때문에 여기서 수정한 구독행위는 바로 종료시킨다.
    });
  }

수정함수랑 거의 동일한 것을 볼 수 있다. 그렇다면 실제 삭제를 하여보자.
간만에 html에 노드를 추가하고 클릭이벤트를 통해서 삭제하는 기능을 구현하여 보자.

app.component.ts에서 데이터를 구독하는 생성자 부분을 바꾸도록 하자.

  //..생략
  private list : any = new Array();  //리스트

  constructor(db : AngularFirestore,strg : AngularFireStorage) {   //모듈에서 만들어진 파이어 베이스 접속관련 객체
    this.DataBase = db;
    this.storage = strg;

    this.getItem('board').subscribe((res : any)=>{  //리스트를 조회하는 구독행위
      this.list = res;
    });

  }  

위 내용에서의 핵심은 getItem을 통해서 가져온 데이터늘 list라는 변수를 바꾸어주는 점 이다.
app.component.html 파일에 리스트 목록을 추가하여본다.

<div>-s-</div>
<table>
  <tr *ngFor='let items of list;let i = index' >
    <td *ngIf="items" (click)='deleteItem("board",items.idx)'>{{items.text}}</td>
  </tr>
</table>
<div>-e-</div>

우앗...다 된거 같다. 막상실행 하여 보면 동작을 하지 않는다. 왜냐하면 idx라는 값을 가진 속성이 없기 때문이다. 
테스트용 데이터를 몇개 넣어주자.

최종 app.component.ts모습이다.

import { Component } from '@angular/core';

import { AngularFirestore, AngularFirestoreCollection } from '@angular/fire/firestore';
import { BehaviorSubject } from 'rxjs';
import { AngularFireStorage} from '@angular/fire/storage';

import { map } from "rxjs/operators"; // map이다.

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'testFire';
  //데이터 베이스 관련 객체
  private DataBase : AngularFirestore;

  //컬렉션을 담아두는 배열
  private collections = new Array();

  //나중에 쓸 저장소 관련 객체
  private storage : AngularFireStorage;

  private list : any = new Array();

  //의존성 주입
  constructor(db : AngularFirestore,strg : AngularFireStorage) {   //모듈에서 만들어진 파이어 베이스 접속관련 객체
    this.DataBase = db;
    this.storage = strg;

    //샘플 데이터를 넣어주는 모습
    this.setItem('board', {text:'test1', number:1,idx:this.makeID(30)});
    this.setItem('board', {text:'test2', number:2,idx:this.makeID(30)});
    this.setItem('board', {text:'test3', number:3,idx:this.makeID(30)});
    this.setItem('board', {text:'test4', number:4,idx:this.makeID(30)});

    this.getItem('board').subscribe((res : any)=>{  //리스트를 조회하는 구독행위
      console.log(res);
      this.list = res;
    });
  }  

  //컬렉션이 있는지 조사해서 해당 내용을 리턴하는 함수
  getCollections (db_name : string) : AngularFirestoreCollection<any>{
    var result = this.collections[db_name];  //배열에 값을 확인
    if(result == undefined || result == null){  //배열에 값이 비어있다면
      this.collections[db_name] = this.DataBase.collection<any>(db_name, (ref) =>ref);   //새로 만들어준다.
      result = this.collections[db_name];
    }
    return result;
  }

  getItem(db_name : string){
    var items : BehaviorSubject<any[]> = new BehaviorSubject([]);
    this.getCollections(db_name).valueChanges().forEach((val)=>{  //데이터를 가져오기
      items.next(val); 
    });    
    return items; 
  }

  setItem(db_name : string, param : any){  //데이터 등록
    this.getCollections(db_name).add(param);  
  }


  updatItem(db_name : string, param: any){
    var subscription = this.getCollections(db_name).stateChanges().pipe(map( changes => { //pip 함수는 map 함수를 붙여주는 역할을 한다.
      return changes.map(a=>{  //map 함수는 데이터의 내용을 바꾸는 역할을 한다.
        const data = a.payload.doc.data() as any;
        const ID = a.payload.doc.id; //고유 아이디 값
        this.getCollections(db_name).doc(ID).update(param);  //id 값을 통해 수정한다.
        return data;
      });
    })).subscribe((oo)=>{  //해당 수정행위를 구독한다.
      subscription.unsubscribe();  //getItem 함수를 통해 구독을 하고 있기 때문에 여기서 수정한 구독행위는 바로 종료시킨다.
                                   //만약 해당 구독행위를 종료하지 않으면 구독행위가 다중 발생하여 무한loop 수정 행위가 발생한다.
    });
  }

  deleteItem(db_name : string, key: any){
    console.log(db_name, key);
    var subscription = this.getCollections(db_name).stateChanges().pipe(map( changes => { //pip 함수는 map 함수를 붙여주는 역할을 한다.
      return changes.map(a=>{  //map 함수는 데이터의 내용을 바꾸는 역할을 한다.
        const data = a.payload.doc.data() as any;
        const ID = a.payload.doc.id; //고유 아이디 값
        if(data.idx == key){
          this.getCollections(db_name).doc(ID).delete();  //key를 통해 삭제한다.
          return data;
        }
        return data;
      });
    })).subscribe((oo)=>{  //해당 수정행위를 구독한다.
      subscription.unsubscribe();  //getItem 함수를 통해 구독을 하고 있기 때문에 여기서 수정한 구독행위는 바로 종료시킨다.
    });
  }

  makeID(length) {
    var result = '';
    var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    var charactersLength = characters.length;
    for ( var i = 0; i < length; i++ ) {
       result += characters.charAt(Math.floor(Math.random() * charactersLength));
    }
    return result;
 }

}

그러면 여기서 수정하는 함수도 키를 통해서 1개만 바뀌도록 수정 할 수 있다.

  updatItem(db_name : string, param: any, key:any){   //key 값 추가
    var subscription = this.getCollections(db_name).stateChanges().pipe(map( changes => { //pip 함수는 map 함수를 붙여주는 역할을 한다.
      return changes.map(a=>{  //map 함수는 데이터의 내용을 바꾸는 역할을 한다.
        const data = a.payload.doc.data() as any;
        const ID = a.payload.doc.id; //고유 아이디 값
        if(data.idx == key){  //키 값이 동일하다면,
          this.getCollections(db_name).doc(ID).update(param);  //id 값을 통해 수정한다.
        }
        return data;
      });
    })).subscribe((oo)=>{  //해당 수정행위를 구독한다.
      subscription.unsubscribe();  //getItem 함수를 통해 구독을 하고 있기 때문에 여기서 수정한 구독행위는 바로 종료시킨다.
                                   //만약 해당 구독행위를 종료하지 않으면 구독행위가 다중 발생하여 무한loop 수정 행위가 발생한다.
    });
  }

여기까지 파이어베이스 데이터베이스를 활용하여 기본적인 읽기, 쓰기, 삭제하기에 대해서 살펴 보았다.
컴포넌트 1개에 너무많은 기능이 들어가있으면서 공통적으로 사용되는 기능이 다수 존재한다.
다음시간에는 이러한 공통 기능을 서비스로 분리하여 보도록 하자.

src.zip
0.01MB

반응형
* 위 에니메이션은 Html의 캔버스(canvas)기반으로 동작하는 기능 입니다. Html 캔버스 튜토리얼 도 한번 살펴보세요~ :)
* 직접 만든 Html 캔버스 애니메이션 도 한번 살펴보세요~ :)

댓글