[Firebase] Realtime Database - 데이터 구조화

박지훈·2023년 11월 22일

Firebase

목록 보기
5/6
post-thumbnail

Realtime Database 데이터 구조화

💡 데이터베이스를 적절히 구조화하려면 철저한 사전 준비가 필요하다. 무엇보다도 최대한 쉽게 데이터를 저장하고 이후에 검색할 수 있도록 만들 방법을 계획하는 것이 중요하다.

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

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

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

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

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

데이터 구조 권장사항

데이터 중첩 피하기

데이터 구조를 최대한 평면화하는 것이 좋다.
다음은 데이터 중첩이 바람직하지 않은 이유를 보여 주는 다중첩 구조의 예이다.

{
  // 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": { ... }
  }
}

확장 가능한 데이터 만들기

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

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

이 방법도 충분하지 않을 수 있다. 예를 들어 사용자와 그룹 간의 양방향 관계가 그런 경우이다. 사용자는 그룹에 속할 수 있으며, 그룹은 사용자 목록으로 구성된다. 이때 사용자가 어떤 그룹에 속해 있는지를 판단하려면 문제가 다소 복잡해진다.

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

// 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는 그룹 아래에 색인이 생성되어 있고 techpioneersAda의 프로필에 나열되어 있다. 따라서 Ada를 그룹에서 삭제하려면 두 위치에서 업데이트가 이루어져야 한다.

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

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


👀 Reference

profile
Flutter 개발자가 되어보자!

0개의 댓글