Firebase란?

파이어베이스

2016 Google I/O에서 발표한 가장 혁신적인 기술로 꼽히는 BAAS(Backend as a service)입니다. 기본적으로 앱(iOS, Android) 및 웹의 백엔드 개발을 도와주기 위해 개발된 서비스로 프론트쪽에 집중하며 빠르게 앱을 개발할 수 있도록 지원해주는 엄청난 툴입니다. 원래 탄탄한 기술에 2014년 구글에 인수되면서 엄청난 시너지 효과를 발휘하게 됩니다. 오늘날 파이어베이스는 Develop, Grow, Earn 이라는 구호에 맞게 실시간 데이터베이스, 사용자 인증(oAuth), 클라우드, 호스팅, 오류 보고, 에드워즈, 에드몹, 애널리틱스와 같은 수많은 서비스를 제공합니다. 즉, 파이어베이스만 사용하면 복잡한 백단 작업을 줄일 수 있고, 호스팅까지 해주며, 각종 Analytics를 통해서 사용자, 버그 통계를 알려주고 짭짤한 광고수익을 얻을 수 있게 해줍니다.

주관적인 생각은, 저와 같이 디비, 백엔드, 상용자 인증과 같은 기능들을 구현할 수 없는 분들에게는 정말 유용한 툴인것 같습니다. 또 서비스를 빠르게 개발해봐야하는 스타트업의 경우에도 ‘무료로’ 제작을 할 수 있으니 더할나위 없이 좋은 툴이겠네요. 이건 개발쪽에 더 투자를 많이하고 호스팅(서버) 비용을 줄일것인가, 아니면 반대로 개발 비용을 낮추고 호스팅(서버)에 투자를 할것인지의 선택인것 같습니다. 파이어베이스와 비슷한 BAAS로는 Heroku 가 있습니다. (재미삼아 보는 Firebase vs Heroku)

튜토리얼

그럼 파이어베이스를 공부해야겠죠? 제가 찾아 본 튜토리얼들을 여기에 모아봤습니다.

  1. 인프런 파이어베이스 강좌(무료)
  2. 파이어베이스 Docs
  3. React & Firebase(영문)
  4. Firecast - React & Firebase(영문 유튜브)
  5. Firebase 유튜브 채널(영문) Firecast라는 개발 관련 재생목록이 있습니다.
  6. Firebase 와 Redux로 채팅앱 만들기(영문)
  7. Firebase blog - React Native & Firebase(영문)
  8. Realm - Firebase 소개
  9. 슬라이드 쉐어 - Firebase 호스팅 기본 가이드 형식
  10. React Native & Firebase & You(영문) 기본 시작 가이드
  11. React - Redux - Firebase(영문) 복잡스 데모

이것 외에도 Vue, Angular와 접목시킨 튜토리얼도 많이 있지만.. 저는 리액트를 사용할 예정이기 때문에 검색해보지 않았습니다.

시작하기

저는 간단한 상담 예약 앱을 제작해보자 합니다. 욕심이지만 리액트를 베이스로 제작할 예정이고요. 뷰는 관리자 로그인 페이지, 달력페이지, 예약 신청, 조회, 수정, 삭제페이지(CRUD)가 있겠습니다. 새로운 예약이 신청될때마다 특정 이메일로 이메일을 보내는 기능도 생각하고 있습니다. 아니면 PWA형식으로 notification 을 받을 수 있게..?? 조금 더 나가면 리엑트 네이티브로 안드로이드 앱을 제작해서 사용해보는것도 좋겠네요. 단순히 생각해 보면 제가 파이어베이스로 구축해야하는것은 실시간 데이터베이스, 사용자 인증, 호스팅 정도가 되겠네요.

리액트 설치

CRA(Create React App)을 이용해서 리액트를 설치합니다. 여기 에서 가이드를 따라가면 쉽게 설치 가능합니다.

npm install -g create-react-app

create-react-app my-app
cd my-app/
npm start (또는 yarn start)

리액트 로고가 나타나면 정상적으로 설치가 되어있는 상태입니다. 서버를 닫고, yarn build 명령어로 스태틱파일로 컴파일합니다. 그럼 프로젝트의 build 폴더가 생성됩니다. 파이어베이스를 설치하며 build폴더를 기본 폴더로 등록해야 한다는 점을 참고하세요.

저는 CRA 베이스로 react-router, ssr, sass, code splitting, hot reloading 등이 적용되어있는 스타터킷을 사용했습니다. 제가 직접 짜집기해서 만든 스타터킷입니다 ㅇㅅㅇ

파이어베이스 CLI 설치

파이어베이스를 프로젝트에 설치하기 위해서는 Command Line Tool 을 사용해야 합니다. npm 플러그인을 설치해주세요.

npm install -g firebase-tools

파이어베이스 등록

파이어 로고!

구글 파이어베이스에 접속해서 ‘프로젝트’를 추가합니다.

해당 프로젝트로 이동해서 Hosting 서비스를 시작합니다. ‘시작하기’를 클릭하면 이런저런 가이드가 뜨는데, 잘 읽고 따라하시면 됩니다.

파이어베이스 리액트 프로젝트에 설치

터미널에서 firebase login 명령어로 파이어베이스를 사용할 구글 계정을 등록합니다. 리액트 프로젝트에 설치하기 위해 리액트 폴더에서 터미널로 이동해서 firebase init을 실행합니다. 그럼 간지나는 F I R E B A S E 로고와 함께 설치가 진행됩니다!

파이어 로고!

이미지의 아래쪽에 보시면 파이어베이스의 어떤 기능을 설치할것인지 옵션이 나옵니다. 위아래 화살표 키보드로 이동이 가능하고, 스페이스바로 선택한 후 (다중 선택 가능) 엔터키로 진행하면 됩니다. 저는 Database 와 Hosting을 선택했습니다.

그럼 차례대로 1)파이어베이스에 등록된 프로젝트 선택, 2)데이터베이스 Rules 선택, 3)deploy 할 폴더 디렉터리 선택, 4)SPA 프로젝트 여부(Y/N)을 선택 하는 옵션이 차례대로 진행됩니다. 본인에게 맞는 옵션들을 선택하면 되지만, 폴더 선택은 build로 수정한 후 등록합니다. 위의 방법을 따라 오셨다면 이미 build 폴더가 생성되어있어서 ‘overwrite’할 것인지 선택지가 나오는데, n을 입력해서 취소합니다.

설치가 완료되었습니다! 그럼 이제 로컬로 띄워보도록 하죠.

firebase serve

localhost:5000 포트에서 파이어베이스 프로젝트가 서빙됩니다! 리액트 프로젝트와 동일한 페이지가 나오면 됩니다. 이제 이 프로젝트를 실제 서비스로 deploy 하는 방법을 알아보겠습니다.

파이어베이스 Deploy

디플로이 톡톡톡

호스팅 기능이 활성화 되어있다면, 이제부터는 firebase deploy명령어로 실제 서버에 프로젝트를 올릴 수 있습니다. deploy 하면 터미널에 사이트 url이 나오는데, 이 url로 접속하시면 우리의 리액트 프로젝트를 볼 수 있습니다.

리액트 파일을 호스팅하기 위해서는 먼저 static 파일로 build 해야합니다. 그러면 yarn build 명령어 후 firebase deploy 를 실행해야하는 번거로움이 있죠. 이를 명령어 한줄로 하기 위해서는 package.json 에 다음과 같이 추가해줍니다.

"scripts": {
    // ... 
    "deploy": "react-scripts build && firebase deploy"
  }

이제 npm run deploy 명령어 한줄만 입력하면 파이어베이스 deploy 까지 자동으로 합니다!

리액트 & 파이어베이스

파이어베이스의 기능들(데이터베이스, 사용자 인증)을 사용하려면 파이어베이스 자바스크립트 코드를 가져와야합니다. npm firebase 를 통해서 NPM 파이어베이스 모듈을 설치해줍니다.

npm install -S firebase

그리고 firebase를 import 해서 관련된 함수를 작성해주시면 됩니다. 설정하는 방법은 두가지가 있습니다. 하나는 index.js 파일에서 바로 import & config 코드를 적어놓는것과, 두번째는 별도의 Firebase.js(예시) 파일을 생성해서 파이어베이스 설정 코드를 담아둔후, import 해서 사용하는 방법입니다. 당연하게도 두번째 방법이 훨씬 스마트하고, 유지보수하기 편리하겠죠?

자, 그럼 두번째 방법으로 리액트를 수정해나가도록 하죠.

파이어베이스 API
파이어베이스 콘솔로 접속을한후, ‘웹 앱에 파이어베이스 추가하기’ 를 클릭해서 코드 스니펫을 복사해와서, 아래의 config 부분을 수정한 후 별도의 js 파일(Firebase.js)에 작성합니다. 저는 Firebase.js를 shared 폴더안에 추가하였습니다.

import * as firebase from 'firebase'
let database;
let config = {
    apiKey: "XXX",
    authDomain: "XXX",
    databaseURL: "XXX",
    projectId: "XXX",
    storageBucket: "XXX",
    messagingSenderId: "XXX",
}

export const fire = () => {
   	firebase.initializeApp(config);
 	database = firebase.database()
}

이제는 Root 파일이나 App 파일에서 Firebase.js 를 import한 후 init()을 실행하면 파이어베이스와 연동이 완료됩니다. 저는 제 스타터 킷을 참고하였으므로 shared 폴더에 있는 App.js에 아래 코드를 추가하였습니다. constructorfirebaseInit()을 추가하였습니다.

//... other imports
import { fire } from 'shared/Firebase'

export default class App extends Component {
  constructor() {
      super();
      fire();
  }
  
  render(){
		// ... codes
  }
}

이 상태에서 코드를 실행하면, 파이어베이스 관련 에러가 뜹니다. 따로 적어놓지 않아서 잘 모르겠지만 파이어베이스가 두번 initialize 되어서 뜨는 에러가 나타나더군요. 급하게 구글링한결과 아래의 코드를 추가해주면 됩니다. initialize 하기 전에 firebase 가 실행되어있는가를 먼저 파악하는 if 문입니다.

// Firebase.js

// ...
export const fire = () => {
    if (!firebase.apps.length) {
        firebase.initializeApp(config);
    }
  database = firebase.database()
}

디비 연동하기

파이어베이스 콘솔로 이동해서, Realtime Database 를 활성화합니다. 파이어베이스 데이터를 읽기 위해서 는 ‘규칙’을 조금 수정해야합니다. 프로젝트 루트 디렉터리에 database.rules.json 이라는 파일을 다음과 같이 수정하세요. 읽기, 쓰기 권한을 해제하는건데, 나중에 oAuth 사용자 인증 기능을 추가하게 되면 다시 수정하면 됩니다.

{
  "rules": {
    ".read": true,
    ".write": true
  }
}

코드를 수정하였으면, 바뀐 규칙을 적용시키기 위해 파이어베이스에 deploy 해줍니다.

파이어베이스 DB

위에 사진처럼 데이터를 추가하였습니다. 저는 일단 READ 기능만 구현을 해볼 예정이기 때문에 공식문서를 참고해서 만들었습니다.

// Firebase.js

// ...
export const getFireDB = () => {
    return database.ref('/').once('value')
}

그리고 불러온 데이터를 state로 저장하기 위해서 App.js 파일에 다음과 같이 작성합니다.

// App.js

// ...
export default class App extends Component {
  constructor() {
      super();
      this.state = {
        memo: []
      };
      fire();
  }

  componentDidMount() {
    getFireDB()
    .then(res =>{
      this.setState({
        memo : res.val().memos
      })
    });
  }
  
  render(){
  	// ...
  }
}

제 스타터킷에는 리액트 라우터가 적용되어있습니다. Firstpage 컴포넌트를 호출하는 태그에 불러온 state값을 props로 넘겨줘서, 뷰단에도 데이터가 나타나도록 설정해보겠습니다.

// App.js

// ...
	render(){
    const {
      memo
    } = this.state;
    
    return (
      <div>
        <Switch>
          <Route exact path="/" render={()=><FirstPage memo={memo}/>}/>
          <Route path="/second" component={SecondPage}/>
          <Route component={NoMatch}/>
        </Switch>
      </div>
    )
  }
// ...
// Firstpage.js

// ...
render() {
	const {
		memo
	} = this.props;
	
    return (
      <div>
        <h1>Schedule</h1>
		{memo.length ? (
			<p>{memo[0].title}</p>
		) : (
			<p className="loading">LOADING</p>
		)}
   		<Link to={'/second'}>Second</Link>
      </div>
    )
  }
// ...

파이어베이스 연동 완료

이제 LOADING이라는 글자가 잠깐 나왔다가 title이 정상적으로 출력되는것을 확인할 수 있습니다. 이렇게 파이어베이스 데이터베이스와 연동을 완료하였습니다. deploy를 하면 서버에 서도 데이터를 정상적으로 불러오는것을 확인할 수 있습니다.


구현하며 어려웠던점

  1. react-router v4.0이 적용되어있는데, App.js에서 받아온 데이터를 <Route/>의 props로 넘겨주는게 어려웠다. 관련된 Docs도 찾기 어려웠는데, 여기있다. 의외로 간단하다.
  2. this.state = {}this.setState() 가 문법적으로 다르다는것. 계속 this.setState = {} 로 작성해서 에러 발생!!
  3. react-router 로 구분되어있는 페이지에서 setState 를 어디에 설정해야하는지 햇갈렸던점. Route로 연결되는 첫 페이지에 state로 지정해놨는데, 로딩될때마다 데이터를 fetch 해오는 문제가 있었다. SPA쓰는 이유가.. ㅠㅠ 결국 state값은 최상위 Parent 컴포넌트에서 불러와야한다는 것을 깨닳음.
  4. Firebase API를 이해해야했다. 공식문서에 관련된 내용을 찾았고, 이를 Firebase.js 라는 독립된 스크립트로 구분하기 위해서 애를 먹었다. 아직 독립된 스크립트로 파이어베이스 관련 스크립트를 분리시키는 이유는 모르겠다.
  5. 파이어베이스의 데이터 구조를 완전히 파악하지 못한 상태로 작업을하니, JSON 데이터가 어떻게 뿌려지는 지 몰랐다. ‘JSON 내보내기’ 기능을 적극 활용해야한다. 참고로, 디비를 작성할때 0, 1, 2 와 같은 숫자로 카테고리?를 만들면, 배열이 된다.
  6. componentWillMount(), componentDidMount() 이 두개의 life cycle에서 언제 데이터를 가져와야하는지 헷갈렸다. 여전히 확실하게 ‘여기다!’ 라고 할 수 없는 부분이기도 하다. DidMount 에서 fetch 해오는게 좋은 practice 라고 하는 블로그를 찾았다. (출처)
  7. 기존에 initialState 와 같은 기능이 this.state = {...} 로 구현이 되는데 이때 기본 값을 넣어놓고 componentWillMount() 에서 this.setState() 할경우 이미 render()가 한번 일어 난 뒤에 setState함수가 먹히는 것을 확인했다. 이 문제에 대해서 구글링을 해보니 비동기 데이터 로딩은 무조건 렌더링이 한번 일어난 후에 처리된다고 한다. 따라서 this.state에 기본 값을 할당하거나, 렌더링 하는 부분에서 empty일경우의 수를 따로 구분해 놓는 방법으로 해결할 수 있다고 한다.(출처) 내 해결법과 비슷하다니, 기분이 좋다.
  8. 파이어베이스를 initialize 하는 함수가 2번 호출이 되는 문제가 있었다. 동일한 프로젝트에 파이어베이스를 2번 이상 적용시키려 하다보니 당연히 에러가 떴다. 무슨 firebase is already defined [DEFAULT]... 라는 에러 메세지였는데, 해결방법은 if문으로 예외처리하는 방법이었다.
  9. 리액트, 앵귤러, 뷰와 같은 프론트엔드에는 다양한 방법론이 있다. 이런 방법론들을 찾아보고, 항상 ‘최선’인 방법을 프로젝트에 적용해야한다.

마치며

기본적인 파이어베이스 세팅 및 리액트와 연동하여 데이터를 가져오는 방법을 알아보았습니다. 앱을 제작하는 과정을, 제가 겪었던 문제들과 함께 단계별로 포스팅해나가겠습니다. 혹시 에러가 있거나 제가 잘못 알고 있는 부분이 있다면, 댓글로 지적해주세요! 해당 포스트가 큰 도움이 되었으면 하네요.