Firebase Firestore는 모바일 및 웹 애플리케이션을 위해 설계된 Google의 클라우드 NoSQL 실시간 데이터베이스입니다. 클라이언트 간 자동 동기화와 함께 컬렉션 및 문서에 데이터를 저장합니다. 문서 Firebase, 2025에 따르면, Firestore는 강력한 일관성을 갖춘 멀티 리전 배포를 지원하며 서버 관리 없이 자동 확장을 제공합니다. 이 데이터베이스는 Firebase Authentication 및 Cloud Functions와 통합되어 자체 서버 인프라 없이 완전한 백엔드를 구축할 수 있습니다.
핵심 요점
Firebase Firestore는 Google이 2019년에 Firebase Realtime Database의 발전된 형태로 출시한 유연하고 확장 가능한 NoSQL 데이터베이스입니다. 문서 컬렉션에 데이터를 저장하며, 각 문서에는 키-값 쌍 집합이 포함됩니다. 기존 관계형 데이터베이스와 달리 Firestore는 사전 정의된 스키마가 필요하지 않으며 데이터 구조는 작성되는 문서에 따라 동적으로 형성됩니다.
Firestore와 기존 클라우드 데이터베이스의 주요 차이점은 내장된 실시간 동기화입니다. 서버의 데이터가 변경되면 연결된 모든 클라이언트가 영구 WebSocket 연결을 통해 업데이트를 받습니다. 이는 수동 서버 폴링의 필요성을 제거하고 채팅, 활동 피드, 협업 편집기 및 모니터링 시스템과 같은 라이브 업데이트가 있는 애플리케이션을 구축할 수 있게 합니다.
이 데이터베이스는 Android, iOS, Web(JavaScript) 및 Admin SDK를 통한 서버 측 언어 등 모든 주요 플랫폼에서 사용할 수 있습니다. Firestore는 Swift, Kotlin, JavaScript, Python, Go, Java 및 Node.js용 SDK를 제공합니다. Google에 따르면 Firestore는 전체 Firebase 생태계에서 하루에 1,000억 개 이상의 요청을 처리하여 프로덕션 애플리케이션의 기반으로서의 신뢰성을 입증하고 있습니다.
Firestore에서 데이터는 계층 구조로 구성됩니다. 컬렉션은 SQL의 테이블과 유사하지만 고정 스키마가 없는 문서의 컨테이너입니다. 문서는 문자열, 숫자, 부울 값, 배열, 중첩 객체 및 지리적 위치 등 다양한 유형의 필드를 포함하는 레코드입니다. 문서는 하위 컬렉션을 포함할 수 있어 모든 깊이의 중첩 데이터 구조를 구축할 수 있습니다.
val db = FirebaseFirestore.getInstance()
val user = hashMapOf(
"name" to "Anna Petrova",
"email" to "anna@example.com",
"age" to 28,
"isActive" to true
)
db.collection("users")
.add(user)
.addOnSuccessListener { docRef ->
Log.d("TAG", "문서가 ID로 추가됨: ${docRef.id}")
}
컬렉션의 각 문서에는 자동 생성되거나 수동으로 설정할 수 있는 고유 식별자가 있습니다. Firestore는 모든 문서 필드를 자동으로 인덱싱하여 수동 인덱스 구성 없이 필터링, 정렬 및 결과 제한이 있는 복잡한 쿼리를 가능하게 합니다.
Firestore와 Firebase Realtime Database는 Google의 두 가지 클라우드 실시간 데이터베이스입니다. 둘 다 실시간 동기화를 제공하지만 데이터 모델, 확장 및 가격 책정에 근본적인 차이점이 있습니다. 특정 프로젝트에 적합한 데이터베이스를 선택할 때 이러한 차이점을 이해하는 것이 중요합니다.
| 특성 | Firestore | Realtime Database |
|---|---|---|
| 데이터 모델 | 컬렉션과 문서 | 단일 JSON 트리 |
| 일관성 | 강력한 일관성 | 최종 일관성 |
| 쿼리 | 필터링 및 정렬이 있는 복합 쿼리 | 하나의 매개변수로만 필터링 |
| 확장 | 자동, 멀티 리전 | 단일 리전, 최대 200k 연결 |
| 가격 | 읽기/쓰기/삭제 작업당 | 전송된 데이터 양 기준 |
주요 아키텍처 차이점은 데이터 모델입니다. Realtime Database는 모든 것을 하나의 큰 JSON 트리에 저장하여 깊은 중첩이 있는 쿼리를 복잡하게 만듭니다. Firestore는 컬렉션과 문서를 사용하여 여러 조건이 있는 복잡한 쿼리를 가능하게 합니다. 또한 Firestore는 강력한 데이터 일관성을 제공합니다. 성공적인 쓰기 후 모든 후속 읽기는 최신 데이터를 반환함을 보장합니다.
Firestore는 멀티 리전 아키텍처 덕분에 수백만 개의 동시 연결로 자동 확장됩니다. Realtime Database는 단일 리전으로 제한되며 최대 200,000개의 동시 연결만 가능합니다. 글로벌 사용자를 대상으로 하는 프로젝트의 경우 Firestore가 더 적합합니다. 데이터가 여러 Google 데이터 센터에 자동으로 복제되기 때문입니다.
Firestore의 데이터 구조는 하위 컬렉션이 있는 복잡한 계층 모델을 구축할 수 있습니다. 예를 들어 사용자에게 "주문" 하위 컬렉션이 있고 각 주문에 "항목" 하위 컬렉션이 있을 수 있습니다. Realtime Database에서 이러한 깊은 중첩은 쿼리 중 성능 문제를 발생시킵니다. 루트에서 필요한 노드까지 전체 경로가 로드되기 때문입니다.
Firestore는 실시간 데이터 동기화를 위해 클라이언트와 서버 간에 영구 WebSocket 연결을 사용합니다. 애플리케이션이 스냅샷 리스너를 통해 문서나 컬렉션의 변경 사항을 구독하면 SDK는 통신 채널을 설정하고 데이터가 변경될 때마다 서버가 업데이트를 전송합니다. 클라이언트는 매번 전체 컬렉션의 전체 스냅샷이 아니라 변경된 문서만 수신합니다.
동기화 메커니즘은 이벤트 스트림을 기반으로 합니다: added(문서 나타남), modified(문서 변경됨), removed(문서 삭제됨). 개발자는 각 이벤트를 개별적으로 처리하고 해당 UI 요소만 업데이트할 수 있습니다. 이는 수천 개의 문서가 있어도 높은 성능을 보장하며 변경된 구성 요소만 다시 렌더링됩니다.
Firestore의 주요 장점 중 하나는 내장된 오프라인 모드 지원입니다. SDK는 자동으로 읽은 모든 데이터를 기기에 캐시하고 네트워크가 없어도 계속 작동합니다. 애플리케이션이 오프라인 모드에서 데이터를 쓰면 로컬 큐에 배치되었다가 연결이 복원될 때 서버로 전송됩니다. 충돌 해결에는 last-write-wins 전략이 사용됩니다.
val docRef = db.collection("cities").document("SF")
docRef.addSnapshotListener { snapshot, error ->
if (error != null) {
Log.w("TAG", "수신 오류", error)
return@addSnapshotListener
}
if (snapshot != null && snapshot.exists()) {
Log.d("TAG", "현재 데이터: ${snapshot.data}")
}
}
캐시 크기는 FirestoreSettings를 통해 구성할 수 있습니다. 기본값은 100MB이지만 데이터 읽기가 많은 애플리케이션의 경우 늘릴 수 있습니다. 애플리케이션 재시작 후에도 유지되는 영구 디스크 캐시 모드도 사용할 수 있습니다. 오프라인 모드 가용성을 관리하기 위해 enableNetwork 및 disableNetwork 메서드를 사용하여 일시적으로 네트워크 상호 작용을 비활성화할 수 있습니다.
Firestore Security Rules는 서버 수준에서 데이터 액세스를 제어하기 위한 선언적 마크업 언어입니다. 규칙은 누가 어떤 조건에서 문서를 읽고 쓸 수 있는지 정의합니다. 이는 쿼리 실행 전에 작동하며 권한 부여를 위해 별도의 서버 로직이 필요하지 않습니다. 각 데이터 읽기 또는 쓰기 전에 Firebase 측에서 규칙이 확인됩니다.
액세스 규칙은 허용(allow) 원칙에 따라 구축됩니다. 기본적으로 모든 액세스가 거부됩니다. 개발자는 특정 조건에서 특정 작업(read, write, create, update, delete)에 대한 액세스를 순차적으로 엽니다. 조건은 request.auth를 통한 사용자 인증, request.resource를 통한 요청 데이터, resource를 통한 기존 데이터를 확인할 수 있습니다.
// Firestore 액세스 규칙
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// 사용자는 자신의 데이터만 읽고 씁니다
match /users/{userId} {
allow read, write: if
request.auth != null &&
request.auth.uid == userId;
}
// 인증된 사용자는 게시물을 읽을 수 있습니다
match /posts/{postId} {
allow read: if request.auth != null;
allow create: if request.auth != null
&& request.resource.data.author == request.auth.uid;
}
}
}
액세스 제어 외에도 Security Rules는 작성되는 데이터의 구조와 유형을 검증할 수 있습니다. 예를 들어 이메일 필드가 정규식과 일치하는지 또는 나이가 120세를 초과하지 않는지 확인할 수 있습니다. 검증은 쓰기 전에 수행되어 잘못된 데이터가 서버에 저장되는 것을 방지합니다. 필드 검증에는 쓰여지고 있는 전체 문서를 포함하는 request.resource.data 객체가 사용됩니다.
Firestore는 클라이언트 액세스 없이 Admin SDK를 통한 서버 측 쓰기만 가능한 컬렉션도 지원합니다. 이는 사용자에게 표시되지 않아야 하는 서비스 정보, API 키 및 구성을 저장하는 데 편리합니다. 이를 위해 규칙에서 해당 컬렉션에 대한 모든 클라이언트 작업을 거부하고 서버 측에서 Admin SDK를 통한 액세스만 허용하면 됩니다.
할 일 목록(todo)을 만드는 Android 애플리케이션에 Firestore를 통합하는 예제를 살펴보겠습니다. 애플리케이션은 실시간으로 작업을 읽고, 새 작업을 추가하고, 완료된 작업을 표시합니다. 비동기 작업을 위해 Firebase 콜백 인터페이스와 Kotlin 코루틴이 사용됩니다.
시작하기 전에 Firebase Console을 통해 프로젝트를 Firebase에 연결하고 google-services.json 파일을 애플리케이션 모듈에 추가해야 합니다. 그런 다음 build.gradle에 firebase-firestore-ktx 종속성과 google-services 플러그인을 추가합니다. 라이브러리 버전은 모든 Firebase 구성 요소의 호환성을 위해 현재 Firebase BoM 버전과 일치해야 합니다.
dependencies {
// Firebase BoM — 버전 관리
implementation platform("com.google.firebase:firebase-bom:33.0.0")
implementation "com.google.firebase:firebase-firestore-ktx"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.9.0"
}
설정 후 Task 데이터 모델과 Firestore 작업을 위한 리포지토리가 생성됩니다. 모델에는 id, title, isCompleted 및 timestamp 필드가 포함됩니다. Firestore는 필드 이름을 키로 사용하여 데이터 클래스를 자동으로 문서로 직렬화합니다. 데이터 읽기에는 snapshotFlow 확장을 통해 Flow를 반환하는 스냅샷 리스너가 사용됩니다.
data class Task(
val id: String = "",
val title: String = "",
val isCompleted: Boolean = false,
val createdAt: Timestamp? = null
)
class TaskRepository {
private val tasksRef = FirebaseFirestore
.getInstance()
.collection("tasks")
fun getTasks(): Flow<List<Task>> = tasksRef
.orderBy("createdAt", Query.Direction.DESCENDING)
.snapshotFlow()
.map { snapshot ->
snapshot?.toObjects(Task::class.java) ?: emptyList()
}
suspend fun addTask(title: String) {
tasksRef.add(Task(title = title))
}
}
ViewModel은 리포지토리에서 Flow를 구독하고 작업 목록을 UI 수준에 전달합니다. 새 작업이 추가되면 코루틴 범위를 통해 리포지토리의 suspend 함수가 호출됩니다. Firestore는 모든 클라이언트 간에 변경 사항을 자동으로 동기화합니다. 한 사용자가 작업을 추가하면 다른 사용자가 화면 새로고침 없이 실시간으로 볼 수 있습니다.
자주 묻는 질문
Firestore는 유연한 스키마, 테이블 없음, JOIN 쿼리 없음의 NoSQL 데이터베이스입니다. 데이터는 테이블 행이 아닌 문서 컬렉션에 저장됩니다. SQL과 달리 Firestore는 사전 정의된 스키마가 필요 없고 마이그레이션 없이 자동으로 확장되지만 컬렉션 간의 복잡한 트랜잭션 쿼리는 지원하지 않습니다.
Firestore는 넉넉한 무료 한도(Spark 요금제)를 제공합니다: 하루 50,000회 읽기, 20,000회 쓰기, 20,000회 삭제. 초과 시 종량제 Blaze 요금제가 사용됩니다: 100,000회 읽기당 $0.06, 100,000회 쓰기당 $0.18. 가격은 리전과 전송되는 데이터 양에 따라 다릅니다.
Firestore는 충돌 해결에 last-write-wins 전략을 사용합니다: 문서에 대한 마지막 쓰기가 이전 쓰기를 완전히 대체합니다. 더 세밀한 제어를 위해 트랜잭션(원자적 읽기-쓰기 작업) 및 배치 쓰기를 사용할 수 있으며, 여러 문서에 대한 작업 시 무결성을 보장합니다.
네, Firestore는 Firebase Console 또는 gcloud CLI를 통한 데이터 내보내기 및 가져오기를 지원합니다. 내보내기는 Cloud Firestore Export 형식으로 수행되며 Google Cloud Storage에 저장됩니다. 데이터는 Firebase 프로젝트 간에 마이그레이션하거나 BigQuery 및 기타 도구에서 분석하기 위해 내보낼 수 있습니다.
Firestore에는 내장된 전문 검색 기능이 없습니다. 이 작업을 위해 Google은 Algolia 또는 Meilisearch와의 통합, 또는 Elasticsearch와 함께 Cloud Functions 사용을 권장합니다. Firestore의 내장 쿼리는 하위 문자열 검색 없이 동등성, 범위 및 필드 존재 확인만 지원합니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.