티스토리 뷰

구글에서 firebase라는 serverless 솔루션을 어느정도 사용까지 무료로 제공하고 있는데요, Application을 만들면서 서버가 필요할때 가장 필수 적인 기능들을 손쉽게 이용할 수 있어서 많은 개발자분들이 이용하고 있습니다.

https://firebase.google.com/

 

Firebase

Firebase는 고품질 앱을 빠르게 개발하고 비즈니스를 성장시키는 데 도움이 되는 Google의 모바일 플랫폼입니다.

firebase.google.com

이 글에서는 파이어베이스 프로젝트 생성 및 연동 보다는 생성과 연동되어 있다고 가정하고, 데이터가 쌓여졌을때 어떻게 golang으로 쿼리를 해서 가져오는가에 대해서 설명하도록 하겠습니다.

콘솔에 로그인을 해서 생성된 프로젝트를 누르면 위와 같은 화면을 좌측에 볼수 있는데요, 저는 Firestore Database를 사용해 DB가 설계되어있으므로 해당 메뉴로 진입해도록 하겠습니다.

컬렉션 안에 문서, 그리고 문서안에 컬렉션들이 있는 형태로 되어 있는데요, 여기서 주의해야 할 것은 컬렉션에 대한 개념과 문서에 대한 개념입니다. 실제 정보를 담는 곳은 컬렉션이 아닌 문서이기 때문에 최종 데이터는 문서에 있고 컬렉션들을 문서를 아우르는 상위개념입니다. 중간 다만 이러한 컬렉션들을 하나의 문서에 포함시킬수도 있기때문에 개발해보면서 살짝 혼동스러운 부분이 있었는데요 API를 읽어서 사용하다보면 어렵지 않게 이해 할 수 있습니다.

다만, 파이어베이스는 희한하게 export 기능이 없습니다. 못찾은 걸 수도 있는데 엑셀이나 csv, txt로 저장된 데이터를 쉽게 export만 할 수 있어도 사용도가 올라갈텐데 아마도 여러가지 정책적인 이슈로 막아놓은것 같습니다. import는 된다고 알고 있는데, 오늘은 export 관련된 이야기를 진행하도록 하겟습니다. export 기능이 콘솔 자체에 없다보니 간단한 프로그램을 통해서 데이터를 쿼리하고 파일로 저장하는 형태를 진행해보도록 하겠습니다. 우선 해당 프로젝트에 접속하기위한 authentication 과정이 필요한데요 이는 프로젝트 세팅에서 key파일을 생성해야 합니다.

프로젝트 개요 옆에 톱니바퀴를 눌러서 작은 팝업메뉴가 뜨는데 프로젝트 설정을 눌러서 들어가면 아래와 같은 화면이 나타납니다.

서비스계정을 눌러서 보면 Firebase Admin SDK를 사용할 수 있는 비공개 키를 생성할 수 있고 스니펫이 있는데 친절하게 어떻게 사용해야하는지 예제처럼 나와 있는 걸 볼수 있고 저는 Go 언어로 진행할 것이니 Go를 선택하고 비공개키를 생성해봅니다. 생성하면 json 파일이 하나 로컬에 저장됩니다.

Authentication을 통해서 DB에 저장한 user들의 email과 id 정보를 얻어 올 수 있는데, 굉장히 심플한 코드입니다.  Context를 하나 생성해주고 Credential에 콘솔에서 생성해서 로컬로 받은 json을 넣어주고 NewApp을 통해 App객체를 얻어와서 Auth 를 통해 인증된 Client 객체를 통해 email과 UID 정보를 읽어 올 수 있습니다.

package main

import (
    "context"
    "flag"
    "log"
    "bufio"
    "fmt"
    "os"
    firebase "firebase.google.com/go"
    "google.golang.org/api/option"
    "google.golang.org/api/iterator"

    "cloud.google.com/go/firestore"
)
func main() {
	ctx := context.Background()
    opt := option.WithCredentialsFile("./XXXX.json")
    app, err := firebase.NewApp(ctx, nil, opt)
    if err != nil {
        log.Fatalf("error connect firebase: %s\n", err)
    }
    
    client, err := app.Auth(ctx)
    if err != nil {
        log.Fatalf("error connect firebase auth: %s\n", err)
    }

    iter := client.Users(ctx, "")
    for {
        user, err := iter.Next()
        if err == iterator.Done {
            break
        }
        if err != nil {
            log.Fatalf("error listing users: %s\n", err)
        }
        log.Printf("read user email: %v\n", user.Email)
        log.Printf("read user UID: %v\n", user.UID)
    }
}

근데, 해당 API는 User 정보에 관한 것이기에 실제 Collection과 Document에 대한 내용은 아니라서 아래 API를 불러서 읽어야 합니다. 자신이 구성한 DB에 맞게 짜여져야 함으로 Dependency가 높은편이라 이부분은 좋은 구조는 아니자만 손쉬게 Auth 기능을 제공하고 DB를 NoSQL로 쌓을 수 있게 구성되다 보니 장단점은 있는 것 같습니다.

func createClient(ctx context.Context) *firestore.Client {

    // Sets your Google Cloud Platform project ID.
    projectID := "XXXXXX"

    // Override with -project flags
    flag.StringVar(&projectID, "project", projectID, "The Google Cloud Platform project ID.")
    flag.Parse()

    client, err := firestore.NewClient(ctx, projectID)
    if err != nil {
        log.Fatalf("Failed to create client: %v", err)
    }
    // Close client when done with
    // defer client.Close()
    return client
}
	client := createClient(ctx)
    defer client.Close()
    iter := client.Collections(ctx)
    for {
        col, err := iter.Next()
        if err == iterator.Done {
            break
        }
        if err != nil {
            log.Fatalf("Failed to iterate: %v", err)
        }
        fmt.Println(col.ID)
        
        iter3 := col.DocumentRefs(ctx)
        for {
            docu, err1 := iter2.Next()
            if err1 == iterator.Done {
                break
            }
            if err1 != nil {
                log.Fatalf("Failed to iterate: %v", err1)
            }
            
            fmt.Println(docu.ID)
        }
    }

위와 같이 작성하면 Collections 를 얻어와서 Collection 별로 문서들을 뽑아냅니다. 물론 제가 쿼리할 DB는 문서안에 또 Collections가 있기에 한번더 저 과정을 거쳐야 겠지만 유사한 구조라서 추가적으로 코딩을 하시면 쉽게 자신의 데이터를 얻을 수 있습니다. 혹시 더 상세하게 궁금하신 분은 답글 남겨주세요!

댓글