Firebase 에서 사용할 수 있는 DB는 2종류이다.

RealTimeDatabase

Cloud Firestore

둘다 NoSQL DB이며 실시간으로 데이터가 동기화된다.

새 프로젝트를 시작하는 단계의 개발자들은 Firebase DB들 중 Cloud Firestore 를 선택해서 진행하는 것이 좋다. 향후 더욱 강력한 기능을 지원할 예정이기 때문이다. 실제로 Firebase 에서 Cloud Firestore에 더 많은 지원을 해주고 있다.

둘의 자세한 차이점은 데이터베이스 선택: Cloud Firestore 또는 실시간 데이터베이스 에 적혀있다.

 

이 글에서는 Cloud Firestore 만 설명하며

Unity3d <-> Node.js Server -> Cloud Firestore

이런 방식으로 데이터를 저장할 예정이다.

 

서버는 Node.js를 사용한다.


Firebase Setting

 

1. 새로운 Firebase 프로젝트를 만들거나 기존의 Firebase 프로젝트를 사용한다.

 

2. 새로운 앱을 등록하거나 기존의 앱을 사용한다.

클라이언트(Unity3d)에서는 데이터만 서버에 보내주고 서버에서 Database 기능을 사용할 예정으로 Web App과 Firebase Database 세팅을 한다.

[Firebase] Firebase 기본 설정 - 코드저장소 (tistory.com)

 

 

Firebase Cloud Store 구조

진행하기 전에 서버에서 사용할 DB(Cloud Firestore) 의 구조부터 파악하자.

사진 삭제

사진 설명을 입력하세요.

Cloud Firestore

NoSQL 문서 중심의 데이터베이스이며 SQL 데이터베이스와 달리 테이블이나 행이 없으며, 컬렉션으로 정리되는 문서(Document)에 데이터를 저장한다. 각 문서에는 키-값들이 들어 있다.

작은 문서가 많이 모인 컬렉션을 저장하는 데 최적화되어 있다.

콜렉션과 문서는 Cloud Firestore에서 암시적으로 생성한다.

사용자는 컬렉션 내의 문서에 데이터를 할당하기만 되고 컬렉션 또는 문서가 Firestore에 없다면 자동으로 생성한다.

 

Document

문서(Document)는 Cloud Firestore의 저장소 단위이다.

문서는 값이 매핑되는 필드(Field)를 포함하며 각 문서는 이름으로 식별된다.

사용자 alovelace 를 나타내는 문서는 다음과 같다.

사진 삭제

사진 설명을 입력하세요.

하위의 이미지처럼 문서의 복잡한 중첩 개체를 이라고 한다.

사진 삭제

사진 설명을 입력하세요.

JSON과 구조가 비슷해보이는데 사실 기본적으로 JSON을 사용한다. 문서가 추가적인 데이터 형식을 지원하고 크기가 1MB로 제한되는 등의 몇가지 차이점이 있지만 JSON 레코드로 취급해도 무방하다.

 

지원되는 데이터 유형은 https://firebase.google.com/docs/firestore/manage-data/data-types 이 링크를 참조하자.

 

Collection

모든 문서들이 저장되는 위치이며 단순히 문서의 컨테이너이다.

사진 삭제

사진 설명을 입력하세요.

사용자 이라는 컬렉션 아래에 alovelace, aturing 이라는 문서가 존재한다.

 

컬렉션은 값이 있는 원시 필드를 포함하거나 다른 컬렉션을 포함할 수 는 없다. 오직 문서만 포함하며 컬랙션 내의 문서 이름들은 고유하다. 사용자 ID (UID) 와 같은 고유한 키 나 Firestore에서 생성한 무작위 ID를 사용할 수 있다.

 

만약 alovelace 문서에 참조하고 싶다면

let alovelaceDocRef = firebaseAdmin.firestore().collection('users').doc('alovelace');

 

alovelace 문서가 포함된 콜렉션을 참조하고 싶다면

let userColRef = firebaseAdmin.firestore().collection('users');

 

alovelace 문서를 경로로 지정하여 참조하고 싶다면

let alovelaceDocRef = firebaseAdmin.firestore().doc('users/alovelace');

 

계층적 데이터

계층적 데이터를 쉽게 이해하기 위해, 채팅 앱을 예시로 든다.

사진 삭제

사진 설명을 입력하세요.

여러 채팅방을 저장하는 rooms 라는 컬렉션이 있다.

roomA 라는 채팅방이 생겼지만 메시지를 채팅방에 직접 저장하는 것은 좋지 않다. 문서는 가벼워야 하는데, 채팅방에 매우 많은 메시지가 포함될 수 있기 때문이다. 이러한 문제는 채팅방 문서 안에 하위 컬렉션을 추가로 만드는 것으로 해결할 수 있다.

사진 삭제

사진 설명을 입력하세요.

roomA 채팅방에 messages 라는 하위 컬렉션을 만들어 각 메세지를 문서로 저장할 수 있다.

 

message1 에 대한 참조는 아래와 같다.

let messageRef = firebaseAdmin.firestore().collection('rooms').

doc('roomA').collection('messages').doc(message1');

 

위와 같이 컬렉션과 문서가 교대로 나오는 패턴을 따라야한다.

collections('rooms').collections('messages') 이나 doc('roomA').doc('message1') 와 같은 패턴은 참조할 수 없다.

roomA의 모든 메시지를 가져오려면 하위 컬렉션인 messages에 대한 컬렉션 참조를 만들고 다른 컬렉션 참조와 같은 방식으로 상호작용하면 된다.

 

하위 컬렉션의 문서도 하위 컬렉션을 포함할 수 있으며 데이터를 더 중첩할 수 있다. 최대 깊이는 100개 수준이다.

 

문서를 삭제해도 하위 컬렉션은 삭제되지 않으며 하위 컬렉션이 있는 문서를 삭제해도 하위 컬렉션은 삭제되지 않는다.

예를 들어 coll/doc 문서가 더 이상 존재하지 않더라도 coll/doc/subcoll/subdoc에는 문서가 있을 수 있다.

상위 문서를 삭제할 때 하위 컬렉션의 문서도 삭제하려면 컬렉션 삭제에 설명된 대로 직접 삭제해야 합니다.

출처 입력

 

Server

 

인제 직접 서버에 적용하여 사용하는 법을 보자. 하위 컬렉션은 사용하지 않으며

set, get, update 에 대한 사용 예시이다.

 

새로운 node.js 프로젝트를 만들어준다.

 

먼저 node.js 표준 프레임워크라고 불리우는 express 설치를 진행한다.

$ sudo npm install -g express-generator

 

express 설치를 완료했다면 인제 node.js 서버를 만들어보자.

먼저 서버를 만들 임의의 폴더로 이동한다.

글쓴이는 Desktop/NodeTutorial 에 만들 것이다.

$ cd Desktop

$ mkdir NodeTutorial

$ cd NodeTutorial

 

자신이 서버를 만들고자 하는 폴더로 이동했다면 인제 node.js 서버를 제작하자.

이때 express 를 이용하여 서버를 만들 것이다.

$ express --session --view=ejs --css css ApiServer

 

위의 명령어를 실행하면 아래와 같이 NodeTutorial 폴더 안에 ApiServer 폴더가 생긴다.

 

터미널에서 알려준 명령어를 순서대로 실행한다.

$ cd ApiServer

$ npm install

$ npm start

npm start 명령어를 실행하고 인터넷 주소창에 아래 주소를 입력하면 자신의 express node.js 서버가 열려있는 것을 볼 수 있다.

$ localhost:3000

접속 로그

 

실행 중인 서버를 CTRL + C 로 종료해주자.

 

인제 열리기만 하는 node.js 서버에 Firebase Database 와 통신하는 로직을 구현해보자.

설치해둔 vscode 를 실행시켜준다.

vscode 에서 아래와 같이 우리가 만든 서버 폴더를 열어준다.

 

 

 

먼저 Firebase SDK 기능을 사용하기 위해 몇개의 package 를 설치해줘야한다.

$ npm install firebase

 

$ npm install firebase-admin

 

firebase, firebase-admin 의 설치가 끝나고 package.json 을 확인하면 방금 설치한 2개의 패키지가 종속성에 포함된 것을 확인할 수 있다.

 

인제 본격적으로 node.js firebase 프로젝트를 만들어보자.

 

routes 에 db.js 를 만들어준다.

 

app.js 에 새로 선언한 db.js routes 를 추가해준다.

$ var dbRouter = require('./routes/db');

$ app.use('/db', dbRouter);

db.js 에 아래 내용을 넣어준다.

파일 첨부

db.js
0.01MB

// express 프레임워크
const express = require('express');
const router = express.Router();

// firebase Admin 써드파티 미들웨어 
const admin = require('firebase-admin'); 
// firebase Admin 초기화 
const firebaseAdmin = admin.initializeApp({ 
    credential: admin.credential.cert({
        
      }), 
    databaseURL: ,
});

// Test Data Insert
router.get('/TestData', (req, res) => {
    const work = async () => {
        try{
            // 테스트 데이터
            var userData = new Object();
            userData.uid = "ABCCBAABCCBA";
            userData.name = "홍길동";
            userData.age = 27;

            console.log(userData.uid);

            // firestore 에서의 collection은 단순히 문서의 컨테이너이다.
            // db에 'user'라는 컬렉션이 없으면 자동으로 생성 후 사용하며
            // 있다면 그대로 사용한다.
            firebaseAdmin.firestore().collection('user')
            // doc() 이면 firestore가 자동으로 id로 문서가 생성한다.
            // doc('임의의 이름') 이면 사용자가 지정한 이름으로 문서가 생성된다.
            .doc(userData.uid)
            // JSON 데이터를 직접 넣는다.
            .set(userData)
            .then(() => {
                // 데이터 입력 성공 시...
                return res.send(true);
            }, (err) => {
                // 데이터 입력 실패 시...
                throw err;
            });
        } catch(err) {
            console.log(err);
            
            return res.send(false);
        }
    }

    work();
});

// 클라이언트가 보낸 데이터를 받은 후 DB에 저장
router.post('/postData', (req, res) => {
    const work = async () => {
        try{
            // 클라이언트에서 보낸 json 데이터
            var userData = req.body;
            
            console.log(userData.uid);

            // firestore 에서의 collection은 단순히 문서의 컨테이너이다.
            // db에 'user'라는 컬렉션이 없으면 자동으로 생성 후 사용하며
            // 있다면 그대로 사용한다.
            firebaseAdmin.firestore().collection('user')
            // doc() 이면 firestore가 자동으로 id로 문서가 생성한다.
            // doc('임의의 이름') 이면 사용자가 지정한 이름으로 문서가 생성된다.
            .doc(userData.uid)
            // JSON 데이터를 직접 넣는다.
            .set(userData)
            .then(() => {
                // 데이터 입력 성공 시...
                return res.send(true);
            }, (err) => {
                // 데이터 입력 실패 시...
                throw err;
            });
        } catch(err) {
            console.log(err);
            
            return res.send(false);
        }
    }

    work();
});

// 클라이언트가 보낸 UID 로 DB에서 검색 후 클라이언트에 JSON 되돌려준다.
router.get('/getSearchData', (req, res) => {
    const work = async () => {
        try{
            var uid = req.query.uid;

            // 문서 한 개만 찾은 후 클라로 보낸다.
            firebaseAdmin.firestore().collection('user').doc(uid).get()
            .then((snapshot) => {
                // 찾은 문서에서 데이터를 JSON 형식으로 얻는다.
                var userData = snapshot.data();

                return res.json(userData);
            }, (err) => {
                throw err;
            });

            // 만약 
        } catch(err) {
            console.log(err);

            return res.send(false);
        }
    }

    work();
});

// 클라이언트가 보낸 userData 로 DB에서 검색 후 특정 키값을 갱신한다.
router.post('/postRenewData', (req, res) => {
    const work = async () => {
        try{
            var user = req.body;

            firebaseAdmin.firestore().collection('user').doc(user.uid).update({
                name: "유저 이름 변경",
                // 특정 함수를 사용하여 수 타입의 데이터의 양을 지정된 수만큼 늘릴 수 있다.
                // 나이가 3 으로 저장되어 있는데 아래 함수를 사용할 경우 나이가 한살 늘어나
                // 4 로 수정된다.
                age: admin.firestore.FieldValue.increment(1)
            }).then((snapshot) => {
                // 성공 시...
                return res.send(true);
            }, (err) => {
                throw err;
            });
        } catch (err) {
            console.log(err);

            return res.send(false);
        }
    }
});

module.exports = router;

 

위의 코드를 입력하고 보면 initializeApp 부분이 비어있는 것을 볼 수 있다.

admin 키값을 이 곳에 넣어주면된다.

[Firebase] Firebase 기본 설정 - 코드저장소 (tistory.com)

위의 글을 참고하여 admin key 를 다운로드 받는다.

databaseURL 의 경우 admin key 다운로드 받는 위치에 있다.

 

db.js 에 다운로드 받은 firebase admin sdk key 을 넣어준다.

필요한 정보를 다 넣었다면 터미널에서 npm start를 해주고 브라우저 주소창에

localhost:3000/db/TestData

입력해보자.

 

제대로 설정을 했다면 true 라고 나오며 firebase firestore 에 Test 정보가 들어가 있을 것이다.

 

 

 

서버 테스트가 끝났다면 인제 서버와 통신할 Unity3d Client 를 만들어가자.

 

 

Client

아래 링크를 참고하여 기본적인 Unity3d Client Setting을 한다.

[Firebase] Firebase 기본 설정 - 코드저장소 (tistory.com)

 

예제 Unity3d version 은 2020.3.0f1 이다.

실력 향상을 위해 작업 중인 Unity3d 프로젝트가 있다면 이 프로젝트에 예제 프로젝트의 기능을 직접 구현하는 방법도 있다.

 

예제 프로젝트

파일 첨부

FirebaseDatabase.zip
0.06MB

 

예제 프로젝트를 연 후 Scenes 폴더를 열고 Database.scene 을 실행한다.

 

아까 만들어둔 서버를 실행하고 Unity Client도 Play 버튼을 눌러 실행한다.

실행한 후 UID 에 아까 추가한 "ABCCBAABCCBA" 를 입력한 후 유저정보 가져오기 버튼을 눌러본다.

Console창에 아까 test로 입력했던 홍길동의 나이가 찍히는 것을 확인 할 수 있다.

 

 

구동 방식은 유저정보 가져오기를 누르면 DatabaseServer GameObejct 에 있는 DatabaseServer.cs의 GetUserDataDB() 함수를 실행이 되며 우리가 서버에 구현한 아래의 /getSearchData 에 신호를 보낸다.

 

 

유저정보 저장하기, 수정하기 2개 모두 위와 같이 Client 함수를 실행하여 서버로 신호를 보내고 결과값을 돌려받는 방식이다.

 

'Programming > Server' 카테고리의 다른 글

VM에 서버 올리기  (0) 2021.04.03
GitLab 저장소에 서버 올리기  (0) 2021.04.03
Node.js 개발 환경 구축  (0) 2021.04.03
GitLab 가입하기  (0) 2021.04.03
오라클 클라우드로 생성한 VM에 고정 IP 할당하기  (0) 2021.04.03
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기