데이터 퍼시스턴스는 모바일 애플리케이션 세션 간에 사용자 정보가 보존되도록 해줍니다. 이 기술이 없으면, 매 번의 실행이 참여에서 시작되어 설정, 히스토리 및 다운로드된 파일이 닫히면 손실됩니다. Google Developers, 2024에 따르면, 90% 이상의 모바일 애플리케이션이 사용자 데이터와 인터페이스 상태를 보존하기 위해 적어도 하나의 퍼시스턴스 메커니즘을 사용합니다.
핵심 요점
Data Persistence는 애플리케이션이 기기의 비후수성 메모리에 데이터를 저장할 수 있는 능력입니다. 모바일 개발에서 퍼시스턴스는 데이터베이스, 파일 시스템, 설정 및 캐시를 포근합니다. 각 메커니즘은 성능, 보안 및 저장 용량에 관한 고유한 특징이 있습니다.
일시 데이터는 RAM에만 존재하며 프로세스 종료 시 손실됩니다. 이에는 화면 상태, 일시 계산 및 이미지 캐시가 포함됩니다. 지속 데이터는 파일 시스템이나 데이터베이스에 쓰여지고 애플리케이션이 다시 시작된 후에도 사용 가능합니다. 이에는 사용자 설정, 인증 토큰, 작업 기록 및 다운로드된 콘텐츠가 포함됩니다.
저장 방법을 선택할 때, 개발자는 여러 요소를 평가합니다. 데이터 유형이 저장 구조를 결정합니다: 간단한 설정은 SharedPreferences 또는 DataStore, 구조화된 레코드는 SQLite 또는 Room, 파일은 File Storage입니다. 데이터 양은 성능에 영향을 미칭니다: 데이터베이스는 수출 개의 레코드에 최적화되어 있고, 파일은 큰 바이너리 객체에 적합합니다. 보안을 위해서는 EncryptedSharedPreferences 또는 SQLCipher를 통한 민감 정보 암호화가 필요합니다.
data class StorageOption(
name: String,
dataType: StorageType,
capacity: Long,
secure: Boolean
)
enum class StorageType {
KEY_VALUE,
RELATIONAL,
FILE
}
SharedPreferences는 Android에서 키-값 쌍을 저장하는 고전적인 방법입니다. 이 API는 플랫폼의 초기 버전부터 사용 가능하며 기본 타입(문자열, 숫자, 봈리언 값)을 지원합니다. 데이터는 애플리케이션의 개인 디렉토리에 있는 XML 파일에 저장되며 해당 프로세스만 액세스할 수 있습니다.
Jetpack DataStore는 Kotlin Coroutines와 Flow 기반의 SharedPreferences의 모더니 대체재입니다. DataStore는 두 가지 변형을 제공합니다: 간단한 값을 위한 Preferences DataStore와 타입화된 객체를 위한 Proto DataStore입니다. SharedPreferences와 달리, DataStore는 동시 접근 시 데이터 일관성을 보증하고 메인 스레드를 차단하지 않고 비동기 작업을 지원합니다.
// SharedPreferences — 전통적인 방식
val prefs = context
.getSharedPreferences("settings", Context.MODE_PRIVATE)
with (prefs.edit()) {
putString("username", "john_doe")
putInt("score", 1500)
apply()
}
// DataStore — 비동기 접근 방식
val settingsDataStore = context
.createDataStore("settings.pb")
val usernameFlow: Flow<String> = settingsDataStore
.data
.map { it[USERNAME_KEY] ?: "" }
iOS에서 SharedPreferences의 역할을 하는 것이 UserDefaults입니다. Property List 형식으로 간단한 값을 저장하는 시스템입니다. UserDefaults는 동기 액세스를 사용하며 작은 양의 구성 데이터에 적합하지만, 민감 정보 저장에는 권장되지 않습니다.
SQLite는 별도의 서버 없이 애플리케이션 프로세스 내에서 실행되는 내장 관계형 데이터베이스입니다. 모바일 개발에서 가장 널리 사용되는 DBMS로, 두 플랫폼 모두에서 기본으로 사용됩니다. Android는 SDK에 SQLite를 포함하고, iOS는 libsqlite3 라이브러리에 포함합니다. SQLite는 표준 SQL, 트랜잭션, 인덱스 및 트리거를 지원합니다.
SQLite를 사용하려면 데이터베이스 스키마를 정의하는 것부터 시작합니다. 개발자는 테이블, 필드 및 타입을 정의한 후, 삽입, 읽기, 갱신, 삭제 작업을 수행합니다. Android의 SQLiteOpenHelper가 데이터베이스 생성 및 마이그레이션을 관리하는 반면, iOS는 C 인터페이스나 FMDB 래퍼를 사용합니다.
// Android에서 SQLiteOpenHelper
class DBHelper(context: Context) :
SQLiteOpenHelper(context, "app.db", null, 1) {
override fun onCreate(db: SQLiteDatabase) {
db.execSQL("""
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT UNIQUE
)
""")
}
fun insertUser(name: String, email: String) {
val db = writableDatabase
val values = ContentValues().apply {
put("name", name)
put("email", email)
}
db.insert("users", null, values)
}
}
| 데이터 유형 | SharedPreferences | SQLite | 파일 시스템 |
|---|---|---|---|
| 유형 | 키-값 | 관계형 DB | 바이너리 파일 |
| 양 | 수백 개 | 수천 개 | 사용 가능 공간 |
| 성능 | 높음 | 중간 | 크기에 따름 |
| 일반적인 사용 | 설정 | 구조화된 데이터 | 이미지, 비디오 |
Room은 Jetpack에서 제공하는 SQLite 상의 ORM 계층입니다. Room은 SQL 쿼리와 ContentValues를 작성하는 반복적인 작업을 제거하고, 이를 어노테이션과 Kotlin 함수로 대체합니다. Room 컴파일러는 빌드 시점에 DAO(Data Access Object) 구현을 생성하여 SQL 구문 오류를 제거합니다.
Room을 통합하려면 kapt 의존성을 추가하고 엔티티 클래스, DAO 인터페이스 및 데이터베이스 클래스에 어노테이션을 적용해야 합니다. RoomDatabase가 임구 접점으로 작동하여 DAO를 획득하고 데이터베이스 작업을 수행합니다. Room은 리앵티브 쿼리를 위한 Flow, 스키마 마이그레이션 및 컴파일 시점 쿼리 검증을 지원합니다.
@Entity(tableName = "users")
data class User(
@PrimaryKey val id: Int,
@ColumnInfo(name = "full_name") val name: String,
@ColumnInfo val email: String
)
@Dao
interface UserDao {
@Query("SELECT * FROM users")
fun getAll(): Flow<List<User>>
@Insert
suspend fun insert(user: User)
@Delete
suspend fun delete(user: User)
}
@Database(
entities = [User::class],
version = 1
)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
Core Data는 객체 그래프를 관리하고 디스크에 저장하는 Apple의 프레임웍크입니다. Room과 달리, Core Data는 테이블이 아니라 관계의 계층을 형성하는 관리된 객체(NSManagedObject)를 다룹니다. Core Data는 지연 로딩, 변경 실행 쳞기 및 NSFetchRequest를 통한 복잡한 쿼리를 지원합니다.
Core Data의 핵심은 세 가지 컴포넌트로 구성됩니다: 관리된 객체 컨텍스트(NSManagedObjectContext), 퍼시스턴트 스토어 코디네이터(NSPersistentStoreCoordinator), 데이터 모델(NSManagedObjectModel)입니다. NSPersistentContainer는 모든 컴포넌트를 하나의 임구 접점으로 통합하여 모더니 Swift 애플리케이션의 설정을 간소화합니다.
import CoreData
class PersistenceController {
static let shared = PersistenceController()
let container: NSPersistentContainer
init() {
container = NSPersistentContainer(name: "AppModel")
container.loadPersistentStores { _, error in
if let error = error {
fatalError("Failed: \(error)")
}
}
}
func saveUser(name: String, email: String) {
let context = container.viewContext
let user = User(context: context)
user.name = name
user.email = email
do {
try context.save()
} catch let error {
print("Save error: \(error)")
}
}
}
파일 저장(File Storage)는 이미지, 비디오 및 문서를 저장하는 데 사용됩니다. Android는 내부 저장소(context.filesDir) — 애플리케이션 전용, 그리고 외부 저장소(Environment.getExternalStorageDirectory) — 다른 애플리케이션이 액세스 가능한 공간을 제공합니다. iOS에서는 파일이 Documents와 Library 디렉토리에 저장되며, Library/Caches는 iCloud에 벡업되지 않는 캐시용입니다. 파일 작업을 위해 두 플랫폼 모두 File API와 스트리밍 읽기/쓰기 작업을 제공합니다. Coil과 SDWebImage 같은 모더니 라이브러리는 파일 저장과 RAM을 결합하여 최적의 성능을 위한 캐싱 계층을 추가합니다.
Android에서 Core Data의 복잡성과 기능성에 대한 대안은 Realm입니다. SQL 계층 없이 모델과 직접 작동하는 객체 데이터베이스입니다. Realm은 읽기 작업에서 SQLite보다 빠르고, 데이터가 변경될 때 UI를 자동으로 업데이트하는 라이브 객체를 지원합니다.
자주 묻는 질문
Data Persistence는 기기의 비후수성 메모리에 데이터를 저장하는 메커니즘을 말하며, 애플리케이션 재시작 후에도 사용이 가능합니다. 이에는 데이터베이스, 파일 저장 및 설정 시스템이 포함됩니다.
Room은 SQL 쿼리와 ContentValues를 직접 작성하지 않도록 해주는 SQLite 상의 ORM 래퍼입니다. Room은 컴파일 시점에 SQL 쿼리를 검증하고, Kotlin Coroutines와 Flow를 지원하며, 데이터 액세스 코드를 자동으로 생성합니다.
SharedPreferences는 작은 양의 간단한 데이터(애플리케이션 설정, 플래그, 식별자, 사용자 선호) 저장에 적합합니다. 복잡하거나 구조화된 데이터에는 Room이나 DataStore를 사용하는 것이 더 좋습니다.
Core Data는 객체 그래프를 관리하는 Apple의 프레임웍크입니다. 관리된 객체, 변경 추적, 지연 로딩 및 스토어(SQLite, XML 또는 바이너리 형식)에 자동 저장을 제공합니다.
선택은 데이터 복잡성에 따라 달라집니다: 설정에는 DataStore 또는 UserDefaults, 구조화된 레코드에는 Room(Android) 또는 Core Data(iOS), 파일에는 File Storage입니다. 기준에는 데이터 양, 성능 요구사항 및 암호화 필요성이 포함됩니다. 하나의 애플리케이션에서 여러 메커니즘을 결합하는 것은 각 접근 방식의 장점을 최적으로 활용할 수 있는 표준 실천입니다.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.