[ Angular 2:Http ] Firebase 파이어베이스 데이타베이스

Angular 2 는 Observable 을 이용해서 데이타베이스에 접근한다. Firebase 는 실시간으로 업데이트 되는 데이타베이스로 Angular 2의 Observable 과 궁합이 잘 맞는 듯 하다.

혹자는 복잡하고 세밀한 기능이 필요한 경우에 Firebase 데이타베이스를 사용하지 말 것을 권하기도 하니, 아직 Firebase 데이타베이스가 보완이 필요한 단계일 수도 있다. 편의성 때문에 Firebase 데이타베이스를 사용했다가 나중에 특정 기능의 구현이 어려워서 다른 데이타베이스로 갈아타야하는 일을 겪었다고 하니 편하다고 무조건 사용할 것은 아닌 것 같다. digitalocean droplet 등에서 mongodb를 활용하는 비용과 비교했을때, 파이어베이스는 정해진 쿼타가 넘어가면 쓸데없이 비용이 제법 많이 발생하게되는 단점도 있다.  그럼에도 불구하고 사용량이 무료 범위 안에 있는 간단한 앱이나 웹 앱을 위한 데이타베이스라면 Firebase 도 좋다.

참고: Reasons Not To Use Firebase

이 글에서는 Angular 2의 Http 모듈을 활용해서 Firebase 와 연동하는 과정을 적어본다.

Firebase Project 생성

링크를 방문해서 사인업/로그인 하고 콘솔로 이동해서 ‘새 프로젝트 만들기’ 버튼을 클릭한다. Input 박스 팝업에서 프로젝트 이름을 치고 생성하면 프로젝트 콘솔창으로 이동한다.

 

데이타베이스를 클릭해서 열어보면 mlab 에서 보던 것과 흡사한 데이타베이스 콘솔이 출력된다.

null 옆에 마우스를 가져가면 + 버튼이 표시된다. + 버튼을 누르면 데이타를 입력할 수 있는 Input field가 열린다.

key/value 형식으로 데이타를 입력한다. 시험삼아 이름에 ‘title’을 값에 ‘Hello There’를 입력하고 추가버튼을 눌러보자.

‘규칙’ 탭을 눌러서 다음과 같이 접근권한을 변경한다. 우리는 테스트 삼아 해보는 것이니, 외부에서 접근 가능하도록 설정해보자. 물론, 프로덕션 환경에서는 접근권한을 필요에 맞게 제대로 설정해주어야한다.

이제 데이타를 Angular 2에서 불러와보자.

Angular2 프로젝트 셋업

먼저 ‘http’라는 이름으로 Angular2 프로젝트를 생성한다.

$ ng new http
$ cd http

http.service.ts 를 생성한다.

$ ng generate service http
//OR
$ ng g s http

http.service.ts 에 Angular 2 Http 를 injection 한다.

import { Injectable } from '@angular/core';
import { Http } from "@angular/http";

@Injectable()
export class HttpService {

  constructor(private http:Http) { }

}

app.module.ts 에서 방금 생성한 HttpService 를 등록해준다.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

import { AppComponent } from './app.component';
import { HttpService } from './http.service';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule
  ],
  providers: [HttpService],
  bootstrap: [AppComponent]
})
export class AppModule { }

 

Angular 2 에서 파이어베이스 데이타 읽어오기: 간단한 맛보기

http.service.ts 를 다음과 같이 수정한다. 10번째 줄에 있는 주소는 위 이미지의 노란색 표시된 부분에 ‘title.json’을 붙인 것이다.

import { Injectable } from '@angular/core';
import { Http } from "@angular/http";

@Injectable()
export class HttpService {

  constructor(private http:Http) { }

  getData() {
    return this.http.get('https://angular2-course-635c4.firebaseio.com/title.json');
  }
}

this.http.get(”)은 Observable을 반환한다. 따라서, subscribe 해주어야 내용을 출력할 수 있다. app.component.ts 에서 HttpService 를 Injection 하고 getData() 를 통해 Observable 을 불러온 후 subscribe 하자.

app.component.ts

import { Component, OnInit } from '@angular/core';
import { HttpService } from './http.service';
import { Response } from "@angular/http";

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  constructor(private httpService:HttpService){}

  ngOnInit(){
    this.httpService.getData()
        .subscribe(
          (data:Response)=>console.log(data)
        );
  }
}

이렇게 하고 ng serve 명령으로 브라우저 창에 실행시키면 브라우저 콘솔에 다음과 같이 Response 가 출력된다.

Response Object -> json

우리가 필요로하는 데이타는 response 의 body 부분이다. 따라서, 다음과 같이 필요한 부분만 받아올 수 있다.

app.component.ts

import { Component, OnInit } from '@angular/core';
import { HttpService } from './http.service';
import { Response } from "@angular/http";

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  constructor(private httpService:HttpService){}

  ngOnInit(){
    this.httpService.getData()
        .subscribe(
          (data:Response)=>console.log(data.json())
        );
  }
}

data에 json()을 붙이면 필요한 데이타가 json 형식으로 넘어온다. 아래 브라우저 콘솔에서 Hello There 만 넘어와 있는 것을 알 수 있다.

그런데, 이렇게 하면 HttpService 에서 데이타를 불러올 때마다 json()을 붙여 변환을 해주어야한다. 아예 HttpService에서 json으로 바꾸어서 보내주면 훨씬 경제적인 코딩이 될 것이다. 이를 위해 map 오퍼레이터를 사용한다. 3번째 줄은 map 오퍼레이터를 사용하기 위한 import 이다.

http.service.ts

import { Injectable } from '@angular/core';
import { Http, Response } from "@angular/http";
import 'rxjs/Rx';

@Injectable()
export class HttpService {

  constructor(private http:Http) { }

  getData() {
    return this.http.get('https://angular2-course-635c4.firebaseio.com/title.json')
    .map((response:Response)=>response.json());
  }
}

app.component.ts 에서는 Response와 json() 부분을 제거해준다.

import { Component, OnInit } from '@angular/core';
import { HttpService } from './http.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  constructor(private httpService:HttpService){}

  ngOnInit(){
    this.httpService.getData()
        .subscribe(
          (data:any)=>console.log(data)
        );
  }
}

 

Async Pipe

위에서는 브라우저 콘솔에 읽어온 값을 표시했다. 브라우저에 제대로 출력하려면 어떻게 할까? 데이타베이스에서 읽어와서 출력하는 과정을 비동기식(asynchronous)으로 처리할 수 있다. 다음과 같이 async Pipe을 사용하면 된다.

app.component.html

<p>{{ asyncString | async }}</p>

app.component.ts

import { Component, OnInit } from '@angular/core';
import { HttpService } from './http.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  asyncString = this.httpService.getData();
  constructor(private httpService:HttpService){}

  ngOnInit(){
    this.httpService.getData()
        .subscribe(
          (data:any)=>console.log(data)
        );
  }
}

여기서 주목할 것은 asyncString 으로 넘어온 값이 string 이 아닌 Observable 이라는 것이다. async 파이프가 이 Observable이 string으로 처리될 수 있도록 처리해준다.

Post Data

파이어베이스에 데이타를 저장하려면, http의 post 메서드를 사용한다. 먼저 간단한 Input field 를 만들어보자. app.component.html 에서 사용자이름과 이메일을 입력할 수 있도록 한다.

app.component.html

<label for="username">Username</label>
<input type="text" id="username" #username>
<label for="email">E-Mail</label>
<input type="text" id="email" #email>
<button (click)="onSubmit(username.value,email.value)">Submit</button>

app.component.ts 에서 onSubmit 메서드를 정의해주어야한다.

app.component.ts

import { Component, } from '@angular/core';
import { HttpService } from './http.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  constructor(private httpService:HttpService){}

  onSubmit(username:string,email:string){
    this.httpService.sendData({username:username,email:email}).subscribe(
      data=>console.log(data)
    );
  }
}

http.service.ts 에서 sendData 메서드를 정의해준다.

import { Injectable } from '@angular/core';
import { Http, Response, Headers } from "@angular/http";
import 'rxjs/Rx';

@Injectable()
export class HttpService {

  constructor(private http:Http) { }

  sendData(user:any){
    const body = JSON.stringify(user);
    const headers = new Headers();
    headers.append('Content-Type','application/json');
    return this.http.post('https://angular2-course-635c4.firebaseio.com/data.json',body,{
      headers:headers
    }).map((data:Response)=>data.json());
  }
}

브라우저를 열고 빈칸을 채워본다.

Submit 하고나서 파이어베이스에서 확인해본다.

data 아래에 입력한 내용이 들어가 있으면 성공이다.

입력한 데이타 읽어오기

위 그림에서 보듯이 data 는 mysql 에서의 table과 비슷하다. data 안에 key들이 나열된다. 현재는 하나의 입력만 있으므로 한개의 key 만 존재한다.

HttpService 에 getData() 메서드를 다음과 같이 수정하자. 11번째 줄 마지막 title.json을 data.json으로 바꾸었다. Response에 json()메서드를 적용시켰기 때문에, data에서 Object를 읽어오게 된다.

http.service.ts

import { Injectable } from '@angular/core';
import { Http, Response, Headers } from "@angular/http";
import 'rxjs/Rx';

@Injectable()
export class HttpService {

  constructor(private http: Http) { }

  getData() {
    return this.http.get('https://angular2-course-635c4.firebaseio.com/data.json')
      .map((response: Response) => response.json());
  }

  sendData(user: any) {
    const body = JSON.stringify(user);
    const headers = new Headers();
    headers.append('Content-Type', 'application/json');
    return this.http.post('https://angular2-course-635c4.firebaseio.com/data.json', body, {
      headers: headers
    }).map((data: Response) => data.json());
  }

}

이제 html 상에서 읽어들이는 버턴을 만들어보자.

app.component.html

<label for="username">Username</label>
<input type="text" id="username" #username>
<label for="email">E-Mail</label>
<input type="text" id="email" #email>
<button (click)="onSubmit(username.value,email.value)">Submit</button>
<hr>
<button (click)="onGetData()">Get Data</button>
<ul>
    <li *ngFor="let item of items">{{item.username}}</li>
</ul>

app.component.ts 에 items를 정의해주고 onGetData() 메서드를 추가해주어야한다.

app.component.ts

import { Component, } from '@angular/core';
import { HttpService } from './http.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  items: any[] = [];
  constructor(private httpService:HttpService){}

  onSubmit(username:string,email:string){
    this.httpService.sendData({username:username,email:email}).subscribe(
      data=>console.log(data)
    );
  }
  onGetData(){
    this.httpService.getData()
      .subscribe(
        data => {
          const myArray = [];
          for (let key in data ){
            myArray.push(data[key]);
          }
          this.items = myArray;
        }
      );
  }
}

data 에서 Object 로 받아왔기 때문에 subscribe 안에서 Object 를 Array로 변환시켜주었다. 이렇게해야 html 상에 제대로 목록을 보여주게 된다.

Error Handling

데이타베이스에서 접근권한 등의 이유로 에러를 발생했을 때, 우리는 그것을 표시해주어야 한다. HttpService 의 sendData() 메서드에 에러 핸들링을 구현해보자.

http.service.ts

import { Injectable } from '@angular/core';
import { Http, Response, Headers } from "@angular/http";
import 'rxjs/Rx';
import { Observable } from "rxjs/Rx";

@Injectable()
export class HttpService {

  constructor(private http: Http) { }

  getData() {
    return this.http.get('https://angular2-course-635c4.firebaseio.com/data.json')
      .map((response: Response) => response.json());
  }

  sendData(user: any) {
    const body = JSON.stringify(user);
    const headers = new Headers();
    headers.append('Content-Type', 'application/json');
    return this.http.post('https://angular2-course-635c4.firebaseio.com/data.json', body, {
      headers: headers
    })
      .map((data: Response) => data.json())
      .catch(this.handleError);
  }

  private handleError (error:any){
    console.log(error);
    return Observable.throw(error.json());
  }
}

app.component.ts 에서 data를 subscribe 할 때 다시 에러를 보여줄 수 있다.

app.component.ts

import { Component, } from '@angular/core';
import { HttpService } from './http.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  items: any[] = [];

  constructor(private httpService:HttpService){}

  onSubmit(username:string,email:string){
    this.httpService.sendData({username:username,email:email}).subscribe(
      data=>console.log(data),
      error => console.log(error)
    );
  }
  onGetData(){
    this.httpService.getData()
      .subscribe(
        data => {
          const myArray = [];
          for (let key in data ){
            myArray.push(data[key]);
          }
          this.items = myArray;
        }
      );
  }
}

 

댓글 남기기