* 제가 다시 작성한 최신 튜토리얼 수정본 입니다. 아래 주소를 통해서 진행하시는 것을 권장 드립니다. ^^
저번시간을 통해서 파이어베이스 데이터베이스 사용법에 대해 살펴보았다.
이번시간에는 컴포넌트에 존재하는 파이어베이스와 관련된 공통기능을 서비스로 분리하고, 실제 웹에서 등록을 하는 폼에대해서 알아보도록 하겠다.
먼저 서비스를 만들어준다.
src - app 폴더까지 이동한 후에 아래 명령어를 입력한다.
ng g service db-connector
서비스가 훌륭하게 만들어 졌다. 그럼 이제 파이어베이스와 관련된 내용 전부 서비스로 옮기도록 하자.
db-connector.service.ts를 수정하여보자.
import { Injectable, Host, Optional } 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이다.
@Injectable({
providedIn: 'root'
})
export class DbConnectorService {
//데이터 베이스 관련 객체
private DataBase : AngularFirestore;
//컬렉션을 담아두는 배열
private collections = new Array();
//나중에 쓸 저장소 관련 객체
private storage : AngularFireStorage;
constructor(@Host() @Optional() db : AngularFirestore,@Host() @Optional()strg : AngularFireStorage) { //Host와 Optional 데코레이터
this.DataBase = db;
this.storage = strg;
}
//컬렉션이 있는지 조사해서 해당 내용을 리턴하는 함수
private 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, 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 수정 행위가 발생한다.
});
}
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 함수를 통해 구독을 하고 있기 때문에 여기서 수정한 구독행위는 바로 종료시킨다.
});
}
}
기존 컴포넌트에 존재하는 파이어베이스 관련된 내용 전부 옮겨보았다.
그렇다면 해당 서비스가 다른 컴포넌트에 자동으로 내용이 완성되도록 하려면 어떻게 해야 되는가?
모듈에 서비스를 주입하여야 한다. app.module.ts파일을 수정하여 보자.
//..생략
import { DbConnectorService } from './db-connector.service';
const fireEnvironment = {
//..생략
};
@NgModule({
declarations: [
AppComponent
],
imports: [
//..생략
],
providers: [
//..생략
DbConnectorService //데이터베이스 접속관련 서비스
],
bootstrap: [AppComponent]
})
export class AppModule { }
서비스를 주입하여 해당공통내용을 이제 어느 컴포넌트에서던지 사용이 가능하게 변경이 되었다.
app.component.ts를 수정하여 보자.
import { Component } from '@angular/core';
import { DbConnectorService } from './db-connector.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
private list : any = new Array(); //리스트
private name = 'board';
constructor(private service : DbConnectorService) { //서비스 주입
service.getItem(this.name).subscribe((res : any)=>{ //리스트를 조회하는 구독행위
console.log(res);
this.list = res;
});
}
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;
}
}
깔끔하게 동작함을 볼 수 있다.
매번 데이터를 저장하거나 수정하는 것을 우리는 ts파일에서만 하였다. 이번에는 html 파일에 실제 input 테그를 활용해서 데이터를 CRUD해보도록 하자.
app.component.html 파일을 수정하여 보자.
<div>-s-</div>
<div>
<input type="text" value="" id='aaa'/>
<input type="text" value="" id='bbb'/>
<input type="button" value="등록" (click)='insert()'/>
</div>
<table>
<tr *ngFor='let items of list;let i = index' >
<td *ngIf="items" >{{items.text}}</td>
<td *ngIf="items" (click)='update(items.idx)'>
<input type="button" value='수정' />
</td>
<td *ngIf="items" (click)='delete(items.idx)'>
<input type="button" value='삭제' />
</td>
</tr>
</table>
<div>-e-</div>
뭔가 매번 하던데로 id를 부여하였다.
음..이렇게 해놓고 보니까 id에 있는 value값을 어떻게 가져오는지 막막하다.
예전기억을 더듬어 보면 FormsModule을 사용하였던 것 같다.
app.module.ts로 다시돌아가 FormsModule을 추가하여 주자.
import { BrowserModule } from '@angular/platform-browser'; //새로추가!!
//..생략
@NgModule({
declarations: [
AppComponent
],
imports: [
FormsModule, //새로추가!!!
//..생략
],
//..생략
})
export class AppModule { }
html에 저렴하게(?) 주었던 id 값을 제거하고 바인딩할 내용으로 수정하여 보자.
<div>-s-</div>
<div>
<input type="text" [(ngModel)]="text" /> <!--바로 요 부분이다-->
<input type="text" [(ngModel)]="desc" /> <!--바로 요 부분이다-->
<input type="button" value="등록" (click)='insert()'/>
</div>
<table>
<tr *ngFor='let items of list;let i = index' >
<td *ngIf="items" >{{items.text}}</td>
<td *ngIf="items" (click)='update(items.idx)'>
<input type="button" value='수정' />
</td>
<td *ngIf="items" (click)='delete(items.idx)'>
<input type="button" value='삭제' />
</td>
</tr>
</table>
<div>-e-</div>
text와 desc라는 객체로 바인딩을 하였다. 이에따른 컴포넌트에서 변수를 마찬가지로 추가하여 앞선시간에 만든 파이어베이스 데이터베이스로 저장되는 함수를 실행시키면 완성이다.
최종 저장기능이 탑재된 모습이다.
import { Component } from '@angular/core';
import { DbConnectorService } from './db-connector.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
private list : any = new Array(); //리스트
private name = 'newBoard';
private text : any;
private desc : any;
constructor(private service : DbConnectorService) { //서비스 주입
service.getItem(this.name).subscribe((res : any)=>{ //리스트를 조회하는 구독행위
console.log(res);
this.list = res;
});
}
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;
}
insert(){
var param = {
text : this.text,
desc : this.desc,
idx : this.makeID(30)
}
this.service.setItem(this.name,param);
}
}
저장되는 곳을 newBoard로 바꾸었다. text와 desc라는 변수는 html파일에서의 바인딩 효과에 의해서 값이 변하면 자동으로 변동되는 것을 확인 할 수 있다.
이제 app.component.ts에서 수정기능과 삭제 기능도 추가하여보자.
//..생략
update(item){ //나중에 내용이 바뀐다~
if(confirm('수정하시겠습니까?')){
console.log(item);
}
}
delete(idx){
if(confirm('삭제하시겠습니까?')){
this.service.deleteItem(this.name, idx);
}
}
//..생략
오..여기까지 하니까 그럴싸해진 웹 기능이 된 것 같다. 그런데 삭제까지는 뭐 상관없는데..수정은 뭘 어떻게 해야되는지 모르겠다.
text 타입의 input 테그로 바꾸도록하자. 그리고 change라는 이벤트를 추가하여주자.
app.component.html파일의 모습이다.
<td *ngIf="items" >
<input type="text" value="{{items.text}}" (change) = 'items.text = $event.target.value'/> <!-- 발생한 이벤트를 items에 바로 적용시킨다. -->
<input type="text" value="{{items.desc}}" (change) = 'items.desc = $event.target.value' /> <!-- 발생한 이벤트를 items에 바로 적용시킨다. -->
</td>
<td *ngIf="items" (click)='update(items)'> <!-- 수정 내용 자체를 수정함수에 바로 전달하여 준다 -->
<input type="button" value='수정' />
</td>
update 함수에 전달하는 내용은 수정할 그 내용 자체가 전달되게 변경하였다.
해당 내용까지 적용하고 수정버튼을 누르면 아래콘솔 내용이 표시가 된다.
수정할 내용까지 잘 전달 되었으니 update 함수를 고쳐보자.
update(item){
if(confirm('수정하시겠습니까?')){
this.service.updatItem(this.name, {
text : item.text,
desc : item.desc
}, item.idx);
}
}
요..드디어 수정까지 제대로 동작하는 앱이 되었다. 그래도..역시 화면에서 스타일은 좀 있어야 볼만 한 것 같다.
예전에 해 보았던 bootstrap을 활용하여 스타일만 좀 꾸며보자.
app.component.html의 최종 모습이다.
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<div class="container">
<br><br>
<div>
<input type="text" [(ngModel)]="text" class='form-control' style='width:30%; display: inline-block;' />
<input type="text" [(ngModel)]="desc" class='form-control' style='width:30%; display: inline-block;' />
<input type="button" value="등록" (click)='insert()' class='btn btn-primary' style='float: right;' />
</div>
<br>
<table class='table table-striped'>
<tr *ngFor='let items of list;let i = index'>
<td *ngIf="items">
<input type="text" value="{{items.text}}" (change)='items.text = $event.target.value' class='form-control'/>
</td>
<td *ngIf="items">
<input type="text" value="{{items.desc}}" (change)='items.desc = $event.target.value' class='form-control' />
</td>
<td *ngIf="items" (click)='update(items)'>
<input type="button" value='수정' class='btn btn-info' />
</td>
<td *ngIf="items" (click)='delete(items.idx)'>
<input type="button" value='삭제' class='btn btn-warning'/>
</td>
</tr>
</table>
</div>
해당 화면 모습이다.
실제 데이터베이스와 CRUD 기능도 되면서 화면도 나오는 앱이 탄생하였다.
이정도 수준으로 작업이 가능하다면 이제 어플리케이션 만드는 것은 어렵지 않는 듯 하다.
다음시간에는 앵귤러에서의 라우팅 기능에 대해 알아보도록 하자.
'앵귤러, 리엑트, 뷰 > Angular Tutorial(old)' 카테고리의 다른 글
앵귤러 튜토리얼 (Angular tutorial) -24 with 파이어 베이스 (0) | 2019.08.01 |
---|---|
앵귤러 튜토리얼 (Angular tutorial) -23 with 파이어 베이스 (0) | 2019.07.25 |
앵귤러 튜토리얼 (Angular tutorial) -21 with 파이어 베이스 (0) | 2019.07.16 |
앵귤러 튜토리얼 (Angular tutorial) -20 with 파이어 베이스 (0) | 2019.07.12 |
앵귤러 튜토리얼 (Angular tutorial) -19 with 파이어 베이스 (0) | 2019.07.12 |
댓글