데이터베이스 구조화

시작하기 전에

Firebase 실시간 데이터베이스를 사용하려면 우선 Firebase 프로젝트를 만들고 Unity 프로젝트에 Firebase Unity SDK 패키지를 추가해야 합니다.

설정:

기본 요건

Android

iOS

  • Unity 5.0 이상
  • Xcode 8.0 이상

Unity 프로젝트를 준비하지 않았다면 빠른 시작 샘플 중 하나를 다운로드하여 특정 Firebase 기능을 시험해 볼 수 있습니다. 빠른 시작을 사용한다면 프로젝트 설정에서 번들 식별자를 확인해야 합니다. 다음 단계에서 이 식별자가 필요합니다.

Firebase 콘솔에서 앱 설정

앱에 Firebase를 추가하려면 Firebase 프로젝트 및 앱의 Firebase 구성 파일이 필요합니다.

Firebase 프로젝트를 만드는 방법은 다음과 같습니다.

  1. Firebase 콘솔로 이동합니다.

  2. 프로젝트 추가를 클릭한 다음, 프로젝트 이름을 선택하거나 입력합니다.

    • 앱에 연결된 기존 Google 프로젝트가 있으면 프로젝트 이름 드롭다운 메뉴에서 프로젝트를 선택합니다.
    • 기존 Google 프로젝트가 없으면 새 프로젝트 이름을 입력합니다.
  3. (선택사항) 프로젝트 ID를 수정합니다.

    Firebase는 Firebase 프로젝트에 자동으로 고유한 ID를 할당합니다. 이 식별자는 공개적으로 표시되는 Firebase 서비스에 나타납니다. 예를 들면 다음과 같습니다.

    • 기본 데이터베이스 URL — your-project-id.firebaseio.com
    • 기본 호스팅 하위 도메인 — your-project-id.firebaseapp.com
  4. 나머지 설정 단계를 따른 다음 프로젝트 만들기(또는 기존 Google 프로젝트를 사용하는 경우 Firebase 추가)를 클릭합니다.

Firebase에서 Firebase 프로젝트용 리소스를 자동으로 프로비저닝합니다. 이 프로세스는 일반적으로 몇 분 정도 걸립니다. 프로세스가 완료되면 Firebase 콘솔에서 Firebase 프로젝트의 개요 페이지로 이동됩니다.

Android

  1. Android 앱에 Firebase 추가를 클릭하고 설정 단계를 따릅니다. 기존 Google 프로젝트를 가져오면 이 단계가 자동으로 이루어지므로 구성 파일만 다운로드하면 됩니다.
  2. 메시지가 표시되면 앱의 패키지 이름을 입력합니다. 앱에서 사용하는 패키지 이름을 입력해야 합니다. 이 설정은 Firebase 프로젝트에 앱을 추가할 때만 가능합니다.
  3. 이 과정에서 google-services.json 파일을 다운로드하게 됩니다. 언제든지 다시 이 파일을 다운로드할 수 있습니다.
  4. 초기화 코드를 추가한 후 앱을 실행하여 Firebase를 성공적으로 설치했다는 확인을 Firebase 콘솔에 보냅니다.

iOS

  1. iOS 앱에 Firebase 추가를 클릭하고 설정 단계를 따릅니다. 기존 Google 프로젝트를 가져오면 이 단계가 자동으로 이루어지므로 구성 파일만 다운로드하면 됩니다.
  2. 메시지가 표시되면 앱의 번들 ID를 입력합니다. 앱에서 사용하는 번들 ID를 입력해야 합니다. 이 설정은 Firebase 프로젝트에 앱을 추가할 때만 가능합니다.
  3. 이 과정에서 GoogleService-Info.plist 파일을 다운로드하게 됩니다. 언제든지 다시 이 파일을 다운로드할 수 있습니다.
  4. 초기화 코드를 추가한 후 앱을 실행하여 Firebase를 성공적으로 설치했다는 확인을 Firebase 콘솔에 보냅니다.
  5. Firebase 콘솔에서 다운로드한 GoogleService-Info.plist를 드래그하여 Unity 프로젝트의 원하는 폴더에 넣습니다.

앱에 Firebase Unity SDK 추가

  1. Firebase Unity SDK를 다운로드합니다.
  2. 애셋 > 패키지 가져오기 > 맞춤 패키지 메뉴 항목을 선택합니다.
  3. 사용하는 Unity 버전과 일치하는 디렉토리에서 FirebaseDatabase.unitypackage 를 가져옵니다.
    • Unity 5.x 이전 버전에서는 .NET 3.x 프레임워크를 사용하므로 dotnet3/FirebaseDatabase.unitypackage 패키지를 가져와야 합니다.
    • Unity 2017.x 이상 버전에서는 .NET 4.x 프레임워크를 사용할 수 있습니다. 프로젝트가 .NET 4.x를 사용하도록 구성된 경우 dotnet4/FirebaseDatabase.unitypackage 패키지를 가져옵니다.
  4. Unity 패키지 가져오기 창이 나타나면 가져오기 버튼을 클릭합니다.

SDK 초기화

Google Play 서비스가 최신 상태여야 Android의 Firebase Unity SDK를 사용할 수 있습니다. Firebase Unity SDK에서 다른 메소드를 호출하기 전에 애플리케이션의 시작 부분에 다음 코드를 추가하여 Google Play 서비스를 확인하고 필요한 경우 SDK에 필요한 버전으로 업데이트해야 합니다.

Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task => {
  var dependencyStatus = task.Result;
  if (dependencyStatus == Firebase.DependencyStatus.Available) {
    // Create and hold a reference to your FirebaseApp, i.e.
    //   app = Firebase.FirebaseApp.DefaultInstance;
    // where app is a Firebase.FirebaseApp property of your application class.

    // Set a flag here indicating that Firebase is ready to use by your
    // application.
  } else {
    UnityEngine.Debug.LogError(System.String.Format(
      "Could not resolve all Firebase dependencies: {0}", dependencyStatus));
    // Firebase Unity SDK is not safe to use here.
  }
});

앱 빌드

Android

  1. File(파일) > Build Settings(빌드 설정) 메뉴 옵션을 선택합니다.
  2. Platform(플랫폼) 목록에서 Android를 선택합니다.
  3. Switch Platform(플랫폼 전환)을 클릭하여 타겟 플랫폼으로 Android를 선택합니다.
  4. Unity 상태 표시줄 오른쪽 하단의 스피너(컴파일) 아이콘이 멈추기를 기다립니다.
  5. Build and Run(빌드 및 실행)을 클릭합니다.

iOS

  1. File(파일) > Build Settings(빌드 설정) 메뉴 옵션을 선택합니다.
  2. Platform(플랫폼) 목록에서 iOS를 선택합니다.
  3. Switch Platform(플랫폼 전환)을 클릭하여 타겟 플랫폼으로 iOS를 선택합니다.
  4. Unity 상태 표시줄 오른쪽 하단의 스피너(컴파일) 아이콘이 멈추기를 기다립니다.
  5. Build and Run(빌드 및 실행)을 클릭합니다.

데이터 구조화

이 가이드에서는 Firebase 실시간 데이터베이스의 JSON 데이터 구조화와 관련된 주요 데이터 아키텍처 개념 및 몇 가지 권장사항을 설명합니다.

데이터베이스를 적절히 구조화하려면 철저한 사전 준비가 필요합니다. 가장 중요한 것은 최대한 쉽게 데이터를 저장하고 이후에 검색할 방법에 대해 계획을 세우는 것입니다.

데이터를 구조화하는 방법: JSON 트리

모든 Firebase 실시간 데이터베이스 데이터는 JSON 객체로 저장됩니다. 데이터베이스를 클라우드 호스팅 JSON 트리라고 생각하면 됩니다. SQL 데이터베이스와 달리 테이블이나 레코드가 없으며, JSON 트리에 추가된 데이터는 연결된 키를 갖는 기존 JSON 구조의 노드가 됩니다. 사용자 ID 또는 의미 있는 이름을 키로 직접 지정할 수도 있고, Push() 메소드를 사용하여 자동으로 지정할 수도 있습니다.

키를 직접 만드는 경우 키는 UTF-8로 인코딩되어야 하고 768바이트 이하여야 하며 ., $, #, [, ], /, ASCII 제어문자 0~31이나 127을 포함할 수 없습니다. 값 자체에도 ASCII 제어문자를 사용할 수 없습니다.

예를 들어 사용자가 기본적인 프로필과 연락처 목록을 저장할 수 있는 채팅 애플리케이션을 가정해 보겠습니다. 사용자 프로필은 일반적으로 /users/$uid와 같은 경로에 위치합니다. 사용자 alovelace의 데이터베이스 항목은 다음과 같이 표시됩니다.

{
  "users": {
    "alovelace": {
      "name": "Ada Lovelace",
      "contacts": { "ghopper": true },
    },
    "ghopper": { ... },
    "eclarke": { ... }
  }
}

데이터베이스는 JSON 트리를 사용하지만 데이터베이스에 저장되는 데이터는 사용 가능한 JSON 유형에 대응하는 특정한 기본 유형으로 표현하여 코드를 보다 쉽게 관리할 수 있습니다.

데이터 구조 권장사항

데이터 중첩 배제

Firebase 실시간 데이터베이스는 최대 32단계의 데이터 중첩을 허용하므로 중첩을 기본 구조로 도입해도 괜찮다고 생각할 수도 있습니다. 그러나 데이터베이스의 특정 위치에서 데이터를 가져오면 모든 하위 노드가 함께 검색됩니다. 또한 사용자에게 데이터베이스의 특정 노드에 대한 읽기 또는 쓰기 권한을 부여하면 해당 노드에 속한 모든 데이터에 대한 권한이 함께 부여됩니다. 따라서 실제 구현에서는 데이터 구조를 최대한 평면화하는 것이 좋습니다.

다음은 데이터 중첩을 배제해야 하는 이유를 보여 주는 다중첩 구조의 예입니다.

{
  // This is a poorly nested data architecture, because iterating the children
  // of the "chats" node to get a list of conversation titles requires
  // potentially downloading hundreds of megabytes of messages
  "chats": {
    "one": {
      "title": "Historical Tech Pioneers",
      "messages": {
        "m1": { "sender": "ghopper", "message": "Relay malfunction found. Cause: moth." },
        "m2": { ... },
        // a very long list of messages
      }
    },
    "two": { ... }
  }
}

이 중첩 설계에서는 전체 데이터를 반복하는 것이 어렵습니다. 예를 들어 채팅 대화 제목을 나열하려면 모든 멤버와 메시지를 포함한 전체 chats 트리를 클라이언트에 다운로드해야 합니다.

데이터 구조 평면화

비정규화를 통해 데이터를 서로 다른 경로로 분할하면 필요에 따라 별도의 호출을 통해 효율적으로 다운로드할 수 있습니다. 아래의 평면화된 구조를 살펴보세요.

{
  // Chats contains only meta info about each conversation
  // stored under the chats's unique ID
  "chats": {
    "one": {
      "title": "Historical Tech Pioneers",
      "lastMessage": "ghopper: Relay malfunction found. Cause: moth.",
      "timestamp": 1459361875666
    },
    "two": { ... },
    "three": { ... }
  },

  // Conversation members are easily accessible
  // and stored by chat conversation ID
  "members": {
    // we'll talk about indices like this below
    "one": {
      "ghopper": true,
      "alovelace": true,
      "eclarke": true
    },
    "two": { ... },
    "three": { ... }
  },

  // Messages are separate from data we may want to iterate quickly
  // but still easily paginated and queried, and organized by chat
  // conversation ID
  "messages": {
    "one": {
      "m1": {
        "name": "eclarke",
        "message": "The relay seems to be malfunctioning.",
        "timestamp": 1459361875337
      },
      "m2": { ... },
      "m3": { ... }
    },
    "two": { ... },
    "three": { ... }
  }
}

이제 대화당 몇 바이트만 다운로드하여 대화방 목록 전체를 반복하면서 메타데이터를 빠르게 가져와서 UI에 대화방을 나열하거나 표시할 수 있습니다. 메시지가 도착하면 별도로 메시지를 가져와서 표시할 수 있으므로 UI가 빠른 반응 속도를 유지합니다.

확장 가능한 데이터 만들기

앱을 개발할 때는 목록의 일부만 다운로드하는 것이 나을 때가 많습니다. 목록에 수천 개의 레코드가 포함된 경우에 특히 그러합니다. 이 관계가 정적이며 일방적인 경우에는 상위 객체 아래에 하위 객체를 중첩하면 간단히 해결됩니다.

경우에 따라서는 이 관계가 동적이거나 데이터를 비정규화해야 할 수 있습니다. 대체적으로 쿼리를 사용하여 데이터의 일부를 검색하면 데이터를 비정규화할 수 있습니다. 자세한 내용은 데이터 검색을 참조하세요.

이 방법도 충분하지 않을 수 있습니다. 사용자와 그룹 간의 양방향 관계를 예로 들 수 있습니다. 사용자는 그룹에 속할 수 있으며, 그룹은 사용자의 목록으로 구성됩니다. 이때 사용자가 어떠한 그룹에 속할지를 결정하려면 문제가 다소 복잡해집니다.

이러한 경우 특정 사용자가 속하는 그룹을 나열하고 해당 그룹의 데이터만 가져오는 깔끔한 방법이 필요합니다. 이때 그룹 색인이 상당한 도움이 될 수 있습니다.

// An index to track Ada's memberships
{
  "users": {
    "alovelace": {
      "name": "Ada Lovelace",
      // Index Ada's groups in her profile
      "groups": {
         // the value here doesn't matter, just that the key exists
         "techpioneers": true,
         "womentechmakers": true
      }
    },
    ...
  },
  "groups": {
    "techpioneers": {
      "name": "Historical Tech Pioneers",
      "members": {
        "alovelace": true,
        "ghopper": true,
        "eclarke": true
      }
    },
    ...
  }
}

위와 같이 Ada의 레코드와 그룹 모두에 관계를 저장하면 일부 데이터가 중복됨을 알 수 있습니다. alovelace는 그룹 아래에 색인화되었고 techpioneers는 Ada의 프로필에 나열되었습니다. 따라서 Ada를 그룹에서 삭제하려면 두 위치에서 업데이트가 이루어져야 합니다.

이러한 중복성은 양방향 관계에서 불가피합니다. 사용자 또는 그룹 목록이 수백만 개로 늘어나거나 실시간 데이터베이스 보안 규칙으로 인해 일부 레코드에 액세스할 수 없더라도 이 중복성 덕분에 Ada의 소속 그룹을 빠르고 효율적으로 확인할 수 있습니다.

이와 같이 ID를 키로 나열하고 값을 true로 설정하여 데이터를 반전하는 방식을 사용하면 키를 확인할 때 /users/$uid/groups/$group_id를 읽어서 null인지만 확인하면 되니 간단합니다. 색인은 데이터 쿼리 또는 검색보다 속도가 빠르고 훨씬 효율적입니다.

다음 단계

다음에 대한 의견 보내기...

도움이 필요하시나요? 지원 페이지를 방문하세요.