Realm은 iOS 및 Android용 SQLite 및 Core Data의 대안으로 개발된 모바일 객체 지향 NoSQL 데이터베이스입니다. 데이터는 메모리의 객체에 직접 액세스할 수 있는 자체 형식으로 저장되어 높은 읽기 및 쓰기 속도를 제공합니다. 반응형 알림, Realm Sync를 통한 자동 동기화 및 크로스 플랫폼 데이터 모델 덕분에 Realm은 10억 개 이상의 기기에서 사용됩니다(MongoDB 데이터, 2025).
핵심 요점
Realm은 모바일 애플리케이션을 위해 특별히 만들어진 객체 지향 데이터베이스입니다. SQLite와 달리 Realm은 SQL이나 관계형 모델을 사용하지 않습니다. 데이터는 라이브 객체(Live Objects)로 저장되며, 중간 ORM 계층 없이 직접 액세스할 수 있습니다. 개발자는 일반 언어 객체(Swift, Kotlin, Dart)로 작업하며, 이 객체들은 데이터베이스에 자동으로 저장됩니다.
Realm은 2014년 스타트업 Realm Inc.로 등장했으며, 2019년 MongoDB Inc.에 인수되었습니다. 그 이후로 Realm은 모바일 데이터 동기화를 위한 통합 플랫폼인 Atlas Device Sync의 일부인 MongoDB Realm으로 개발되고 있습니다. 기본 로컬 Realm 데이터베이스는 무료이며 오픈 소스(Apache 2.0 라이선스)로 남아 있지만, Realm Sync(클라우드 동기화)는 구독으로 제공됩니다.
MongoDB Developer Report(2025)에 따르면, Realm은 모바일 애플리케이션의 15%에서 기본 로컬 저장소로 사용됩니다. 주요 사용 사례는 오프라인 모드의 애플리케이션, 복잡한 객체 모델 및 기기 간 동기화가 필요한 애플리케이션으로, Realm은 개발 속도와 객체 작업 용이성에서 SQLite를 능가합니다.
Live Objects — 각 Realm 객체는 메모리 매핑 파일(memory-mapped file)을 통해 디스크에 연결됩니다.어떤 스레드에서 객체를 변경하면 수동 알림 없이 다른 모든 스레드에 즉시 반영됩니다. Realm 알림을 통해 데이터가 변경되면 UI가 자동으로 다시 그려집니다. Zero-copy — Realm은 데이터베이스에서 객체로 데이터를 복사하지 않습니다. 객체 자체가 메모리의 데이터입니다.
Realm 코어는 C++로 작성되었으며 데이터 인덱싱에 B+ 트리를 사용합니다. 각 행이 관계형 테이블의 레코드인 SQLite와 달리, Realm은 객체를 객체 간 직접 참조(forward pointers)가 있는 그래프 구조의 노드로 저장합니다. 객체 간의 관계는 외래 키가 아닌 직접 포인터이므로 JOIN 연산이 필요하지 않습니다.
Realm 파일(.realm 확장자)은 메모리 매핑됩니다. OS 자체가 액세스 시 파일 페이지를 메모리로 로드하는 것을 관리합니다. 이를 통해 Realm은 사용 가능한 RAM보다 큰 데이터베이스로 작업할 수 있으며, RAM에 맞는 데이터의 경우 네이티브 언어 데이터 구조(Swift Array 또는 Kotlin List) 수준의 읽기 지연 시간을 제공합니다.
메모리 매핑 파일 — Realm의 핵심 기술입니다. 데이터베이스 파일에서 언어 객체로 데이터를 복사하는 대신(SQLite가 sqlite3_step + sqlite3_column_*을 통해 하는 것처럼), Realm은 데이터베이스 파일을 프로세스의 주소 공간에 직접 매핑합니다. 언어 객체는 매핑된 메모리의 데이터에 대한 단순한 포인터입니다. 객체를 변경하면 별도의 save 호출 없이 디스크의 데이터베이스가 수정됩니다.
| 특성 | Realm (zero-copy) | SQLite (복사) |
|---|---|---|
| 읽기 | 직접 메모리 액세스(나노초) | 객체로 복사(마이크로초) |
| 쓰기 | 메모리 매핑 파일에 즉시 쓰기 | SQL INSERT/UPDATE 트랜잭션의 save() |
| 관계 | 직접 포인터(forward pointers) | 외래 키 + JOIN |
| 메모리 소비 | 파일 + OS 페이지 캐시 | 객체 + SQLite 페이지 캐시 |
| 라이브 객체 | 예(자동 업데이트) | 아니요(새 fetch 필요) |
메모리 소비는 활성 읽기 중 Realm에서 더 높습니다(전체 파일 또는 대부분이 메모리에 매핑됨). 그러나 쓰기 작업 중에는 객체 복사본을 위한 메모리 할당이 필요하지 않으므로 더 낮습니다. 모바일 장치에서 최대 1GB의 데이터베이스의 경우, 메모리 매핑 접근 방식은 중간 복사본이 있는 SQLite보다 전체 성능이 더 우수합니다.
SQLite는 SQL 및 ACID 트랜잭션을 갖춘 관계형 DBMS입니다. Realm은 메모리 매핑 액세스를 갖춘 객체 NoSQL 데이터베이스입니다. Core Data는 SQLite 위의 Apple ORM 계층입니다. 세 가지 접근 방식은 각각 고유한 적용 분야와 강점이 있습니다. Realm은 직접 객체 액세스 속도와 반응성에서 우수하며, SQLite는 다양성과 성숙도에서, Core Data는 Apple 생태계와의 통합에서 우수합니다.
Realm의 읽기 성능은 zero-copy 액세스 덕분에 SQLite보다 10-50% 높습니다. Realm은 SQL 구문 분석이 필요 없고 메모리 매핑 페이지를 직접 수정하기 때문에 쓰기에서도 더 빠릅니다. 그러나 Realm은 대규모 데이터베이스(>500MB)로 작업할 때 파일이 주소 공간에 완전히 매핑되므로 더 많은 RAM을 소비할 수 있습니다.
| 기준 | Realm | SQLite | Core Data |
|---|---|---|---|
| 유형 | NoSQL 객체 DB | 관계형 DBMS (SQL) | SQLite 위의 ORM |
| 읽기 속도 | 높음 (zero-copy) | 중간 | 중간 (ORM 오버헤드) |
| 반응성 | Live Objects (내장) | 래퍼 필요 (Room/GRDB) | NSFetchedResultsController |
| 동기화 | Realm Sync (MongoDB Atlas) | 사용자 정의 구현 필요 | NSPersistentCloudKitContainer |
| 플랫폼 | iOS, Android, Flutter, RN, Node.js | iOS, Android, Web, 데스크톱 | iOS/macOS만 |
| 라이브러리 크기 | ~4 MB | ~600 KB | SDK에 내장 |
Realm과 SQLite 사이의 선택은 종종 우선순위에 달려 있습니다: 개발 속도와 반응성(Realm) 대 제어와 다양성(SQLite). Realm은 데이터 모델이 자주 변경되고 빠른 라이브 UI 업데이트가 중요한 MVP 및 프로토타입에 특히 좋습니다. SQLite/Room은 대용량 데이터와 복잡한 분석 쿼리가 있는 성숙한 제품에 더 적합합니다.
Realm Kotlin SDK는 Android용 공식 라이브러리로, Kotlin으로 작성되었습니다(Java SDK의 래퍼가 아님). 데이터 모델은 RealmObject를 상속하는 클래스를 통해 정의되며, 고유 식별자에는 @PrimaryKey 어노테이션을 사용합니다. Realm Kotlin은 Kotlin Coroutines, Flow 및 컴파일 타임 코드 생성을 위한 KSP(Kotlin Symbol Processing)를 지원합니다.
RealmQuery는 SQL 없이 필터링하기 위한 타입 세이프 API를 제공합니다. 쿼리는 메서드 체인으로 구성됩니다: query → filter → sort → find. 결과는 목록(메모리에 로드) 또는 Flow(데이터 변경 시 업데이트되는 반응형 스트림)로 반환될 수 있습니다. Realm Kotlin은 중첩 객체와 목록(RealmList)도 지원합니다.
데이터 모델 Realm은 RealmObject 인터페이스를 구현하는 데이터 클래스로 정의됩니다. _id 필드는 고유 식별을 위해 @PrimaryKey로 표시됩니다. 일대다 관계는 RealmList(다른 RealmObject 목록)를 통해 구현됩니다. 모든 쓰기 작업(생성, 업데이트, 삭제)은 write 트랜잭션 블록 내에서 수행됩니다.
class Project : RealmObject {
@PrimaryKey
var _id: ObjectId = ObjectId().generate()
var name: String = ""
var tasks: RealmList<Task> = realmListOf()
}
class Task : RealmObject {
@PrimaryKey
var _id: ObjectId = ObjectId().generate()
var title: String = ""
var isComplete: Boolean = false
}
val config = Realm.Configuration.Builder(
schema = setOf(Project::class, Task::class)
).build()
val realm = Realm.open(config)
realm.write { transactionRealm ->
val project = copyToRealm(Project().apply {
name = "Mobile App"
})
project.tasks.add(copyToRealm(Task().apply {
title = "Design UI"
}))
}
val projects: Flow<RealmResults<Project>> =
realm.query<Project>()
.sort(Project::name, Sort.ASCENDING)
.asFlow()
Flow를 반환하는 쿼리는 Project 또는 Task의 추가, 업데이트 또는 삭제와 같이 Realm의 데이터 변경 시 자동으로 새 결과를 내보냅니다. Flow 접근 방식은 Compose와 완벽하게 조화를 이룹니다. ViewModel의 collectAsState()는 목록 어댑터를 수동으로 업데이트하거나 LiveData를 사용하지 않고 데이터 변경 시 UI를 자동으로 재구성합니다.
Realm Swift SDK는 async/await, Combine Publishers 및 Swift Concurrency를 지원하는 네이티브 Swift API를 제공합니다. 데이터 모델은 Object를 상속하는 클래스로 정의되며, 속성에는 @Persisted를 사용합니다. Realm Swift는 변경 시 자동으로 객체를 업데이트하며, 알림(NotificationToken)을 통해 개별 객체 또는 컬렉션의 변경 사항을 구독할 수 있습니다.
@ObservedRealmObject 및 @ObservedResults는 Realm 데이터가 변경될 때 자동으로 View를 다시 그리는 SwiftUI용 Property Wrapper입니다. @ObservedResults는 쿼리 결과와 함께 작동하고, @ObservedRealmObject는 특정 객체와 함께 작동합니다. 두 속성 모두 View가 초기화 해제될 때 구독을 취소합니다.
SwiftUI 통합은 Realm SDK의 강점 중 하나입니다. @ObservedResults Property Wrapper는 Realm 쿼리를 SwiftUI 표시와 연결합니다. 작업이 변경되면 SwiftUI가 자동으로 목록을 재구성합니다. realm.writeAsync는 UI를 차단하지 않고 백그라운드 스레드에서 트랜잭션을 실행합니다.
import RealmSwift
class TaskItem: Object, Identifiable {
@Persisted(primaryKey: true) var _id: ObjectId
@Persisted var title: String = ""
@Persisted var isDone: Bool = false
@Persisted var priority: Int = 0
}
struct TaskListView: View {
@ObservedResults(TaskItem.self,
sortDescriptor: SortDescriptor(["priority"]))
var tasks
var body: some View {
List {
ForEach(tasks) { task in
TaskRow(task: task)
}
}
Button("Add Task") {
let realm = try! Realm()
try! realm.write {
realm.add(TaskItem(value: ["title": "New task"]))
}
}
}
}
@ObservedResults는 Realm 쿼리 수명 주기를 자동으로 관리합니다. View가 생성되면 변경 사항에 대한 구독이 생성되고, 소멸되면 취소됩니다. Property Wrapper는 선택적 조건(NSPredicate)과 정렬 설명자를 허용합니다. 이렇게 하면 수동으로 fetch 요청을 작성하거나, ViewModel을 만들거나, Realm 알림을 구독할 필요가 없어집니다.
오프라인 애플리케이션 — Realm은 인터넷 없이도 데이터를 사용할 수 있어야 하는 시나리오에 이상적입니다. 내장 동기화(MongoDB Atlas와의 Realm Sync)는 네트워크에 연결될 때 충돌을 자동으로 해결합니다. 앱은 로컬 데이터로 계속 작동하며, 백그라운드 동기화는 연결이 가능해지면 데이터를 업데이트합니다.
반응형 인터페이스 — Live Objects 및 Flow/Combine 알림은 Realm을 데이터가 자주 업데이트되는 애플리케이션(채팅, 작업 피드, 공동 문서)에 편리한 선택으로 만듭니다. 수동으로 reloadData 또는 invalidate를 호출하지 않고 데이터가 추가, 변경 또는 삭제될 때 UI가 자동으로 업데이트됩니다.
크로스 플랫폼 프로젝트 — Realm은 Kotlin Multiplatform(KMP), Flutter, React Native 및 Xamarin을 지원합니다. 데이터 모델은 한 번 정의되고 모든 플랫폼에서 사용됩니다. 이는 코드 중복을 줄이고 공유 비즈니스 로직을 가진 iOS 및 Android 애플리케이션 간의 스키마 일관성을 보장합니다.
Realm Sync는 MongoDB Atlas에 내장된 클라우드 서비스입니다. 애플리케이션은 사용자 기기와 서버 간에 데이터를 자동으로 동기화합니다. 충돌은 "last write wins" 전략 또는 서버의 사용자 정의 Conflict Resolution Functions를 통해 해결됩니다. Realm Sync는 트래픽을 줄이기 위해 부분 동기화(데이터 하위 집합)를 지원합니다.
class SyncRepository {
private val app = App(
AppConfiguration(
appId = "my-realm-app-id"
)
)
suspend fun syncData() {
val user = app.login(Credentials.anonymous())
val config = SyncConfiguration.Builder(
user, setOf(Project::class)
).build()
val syncedRealm = Realm.open(config)
// 자동 동기화가 활성화되었습니다
}
}
SyncConfiguration은 MongoDB Atlas에 대한 연결을 구성하고 동기화할 모델 집합을 정의합니다. 사용자 인증 후 Realm은 자동으로 클라우드에서 데이터를 로드하고 로컬 변경 사항과 병합합니다. 동기화는 백그라운드 스레드에서 실행되며 수동 호출이 필요하지 않습니다. SyncConfiguration으로 Realm 인스턴스를 생성하기만 하면 됩니다.
자주 묻는 질문
네, 로컬 Realm 데이터베이스는 완전히 무료입니다(Apache 2.0 라이선스). MongoDB Atlas를 통한 Realm Sync(클라우드 동기화)는 Atlas Device Sync 구독에 포함된 유료 서비스입니다. 무료 Atlas 티어에는 처음 500MB의 데이터와 월 100만 회 작업이 포함됩니다.
네, 마이그레이션은 SQLite에서 JSON으로 데이터를 내보내고 첫 번째 앱 로드 시 Realm으로 가져와서 가능합니다. 대용량의 경우 스트리밍 마이그레이션을 사용하세요. 사용자가 새 앱 버전으로 전환하는 동안 두 데이터베이스에 병렬로 데이터를 쓰고, 그런 다음 SQLite 파일을 삭제합니다.
Realm Sync는 기본적으로 "last write wins" 전략을 사용합니다. 마지막 쓰기가 이전 쓰기를 덮어씁니다. 복잡한 충돌의 경우 애플리케이션의 비즈니스 로직에 따라 다른 기기의 변경 사항을 병합하는 사용자 정의 Conflict Resolution Functions를 MongoDB Atlas 서버에 구성하세요(예: 교체 대신 값 병합).
Realm은 데이터베이스 수준에서 SQL 집계(SUM, AVG, GROUP BY)를 지원하지 않습니다. 분석 쿼리의 경우 Realm Sync를 통해 Realm에서 MongoDB Atlas로 데이터를 내보내고 MongoDB Aggregation Pipeline을 통해 집계를 실행하는 것이 좋습니다. 간단한 계산에는 realm.query<T>().count()를 사용하세요.
Realm은 기본적으로 데이터를 암호화하지 않습니다. 암호화를 위해 구성에 64바이트 키를 제공하세요: Realm.Configuration(encryptionKey: key). 암호화는 AES-256 + SHA-2 HMAC를 사용하며 작업 오버헤드를 10-20% 증가시킵니다. 암호화 없이 .realm 파일은 파일 시스템에 액세스할 수 있는 모든 프로세스에서 읽을 수 있습니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.