개발 정보 공유/Kotlin

안드로이드 앱에서 데이터 처리 및 저장, 정복해 보자!

언제나 예외처리...ㅡ.ㅡ) b 2024. 12. 30. 09:12
반응형

안녕하세요, 안드로이드 앱 개발의 세계에 푹 빠져 있는 여러분들을 위한 꿀팁 가득한 블로그 포스팅으로 돌아왔습니다! 🎉 오늘은 앱 개발에서 빼놓을 수 없는 중요한 주제, 바로 데이터 처리 및 저장에 대해 파헤쳐 보려고 합니다.

앱을 만들 때 사용자의 정보, 설정 값, 또는 앱에서 생성되는 데이터 등 다양한 데이터를 다루게 되는데요, 이 데이터를 어떻게 효율적으로 처리하고 안전하게 저장하는지가 앱의 성능과 사용자 경험에 큰 영향을 미칩니다. 그래서 오늘은 안드로이드에서 데이터를 처리하고 저장하는 여러 가지 방법들을 알아보고, 각각의 장단점과 어떤 상황에 적합한지, 그리고 실제 코드 예제까지 곁들여서 꼼꼼하게 설명해 드리겠습니다.

자, 그럼 이제 본격적으로 데이터의 바다로 함께 떠나볼까요? 🌊

목차

  1. 데이터 처리 및 저장이 왜 중요할까요?
  2. 안드로이드에서 데이터를 저장하는 다양한 방법들
    • 2.1. SharedPreferences: 간단한 키-값 쌍 저장에 최적화!
      • 2.1.1. SharedPreferences란?
      • 2.1.2. 언제 사용하면 좋을까요?
      • 2.1.3. 장단점
      • 2.1.4. 사용 예제
    • 2.2. 내부 저장소(Internal Storage): 앱 전용 파일 저장 공간
      • 2.2.1. 내부 저장소란?
      • 2.2.2. 언제 사용하면 좋을까요?
      • 2.2.3. 장단점
      • 2.2.4. 사용 예제
    • 2.3. 외부 저장소(External Storage): 공용 파일 저장, SD 카드도 OK!
      • 2.3.1. 외부 저장소란?
      • 2.3.2. 언제 사용하면 좋을까요?
      • 2.3.3. 장단점
      • 2.3.4. 사용 예제
    • 2.4. SQLite 데이터베이스: 구조화된 데이터 저장의 정석
      • 2.4.1. SQLite란?
      • 2.4.2. 언제 사용하면 좋을까요?
      • 2.4.3. 장단점
      • 2.4.4. 사용 예제
    • 2.5. Room Persistence Library: SQLite를 더 쉽게, 객체 지향적으로!
      • 2.5.1. Room이란?
      • 2.5.2. Room의 주요 구성 요소
      • 2.5.3. 언제 사용하면 좋을까요?
      • 2.5.4. 장단점
      • 2.5.5. 사용 예제
    • 2.6. 데이터 바인딩 (Data Binding): UI와 데이터를 깔끔하게 연결!
      • 2.6.1. 데이터 바인딩이란?
      • 2.6.2. 언제 사용하면 좋을까요?
      • 2.6.3. 장단점
      • 2.6.4. 사용 예제
  3. 상황별 데이터 저장 방법 추천
  4. 마무리하며: 데이터 처리 및 저장, 마스터하기!

1. 데이터 처리 및 저장이 왜 중요할까요?

앱에서 발생하는 모든 정보, 즉 사용자가 입력한 정보, 앱의 설정, 다운로드 받은 파일 등은 모두 데이터입니다. 이 데이터를 어떻게 처리하고 저장하느냐에 따라 앱의 성능, 안정성, 사용자 경험이 크게 달라집니다.

  • 사용자 경험 향상: 데이터를 적절히 저장하면 앱을 다시 실행했을 때 이전 상태를 복원하여 사용자에게 끊김 없는 경험을 제공할 수 있습니다. 예를 들어, 사용자가 입력한 텍스트를 저장해 두었다가 다시 보여주거나, 마지막으로 본 화면을 기억하여 다시 실행했을 때 해당 화면부터 시작하도록 할 수 있습니다.
  • 오프라인 기능 제공: 네트워크 연결이 없는 오프라인 상태에서도 앱이 작동하도록 하려면 데이터를 기기에 저장해야 합니다. 예를 들어, 뉴스 앱에서 기사를 미리 다운로드 받아 저장해 두면 인터넷 연결 없이도 기사를 읽을 수 있습니다.
  • 앱 성능 향상: 필요한 데이터를 매번 서버에서 가져오지 않고 로컬에 저장해 두면, 데이터를 불러오는 시간을 단축하여 앱의 성능을 향상시킬 수 있습니다.
  • 데이터 보안: 민감한 사용자 정보를 안전하게 저장하려면 적절한 보안 조치를 취해야 합니다. 안드로이드는 데이터를 안전하게 저장하기 위한 여러 가지 방법을 제공합니다.

2. 안드로이드에서 데이터를 저장하는 다양한 방법들

안드로이드는 데이터를 저장하기 위한 다양한 방법을 제공합니다. 각각의 방법은 장단점이 있으므로, 앱의 특성과 저장하려는 데이터의 종류에 따라 적절한 방법을 선택해야 합니다.

2.1. SharedPreferences: 간단한 키-값 쌍 저장에 최적화!

2.1.1. SharedPreferences란?

SharedPreferences 간단한 데이터를 키-값(key-value) 쌍으로 저장하는 데 사용되는 인터페이스입니다. 데이터는 앱의 내부 저장소에 XML 파일 형태로 저장되며, 앱이 삭제되기 전까지 유지됩니다.

2.1.2. 언제 사용하면 좋을까요?
  • 앱의 설정 값 (예: 자동 로그인 여부, 알림 설정 등)
  • 사용자의 기본 설정 (예: 언어 설정, 테마 설정 등)
  • 간단한 데이터 (예: 사용자가 마지막으로 입력한 텍스트, 게임 점수 등)
2.1.3. 장단점
  • 장점:
    • 사용법이 매우 간단합니다.
    • 작은 양의 데이터를 저장하는 데 효율적입니다.
  • 단점:
    • 보안에 취약할 수 있습니다. (루팅된 기기에서는 데이터에 접근 가능)
    • 복잡한 데이터 구조를 저장하기 어렵습니다.
    • 많은 양의 데이터를 저장하기에는 적합하지 않습니다.
2.1.4. 사용 예제

데이터 저장:

 

Kotlin
 
val sharedPref = getSharedPreferences("my_prefs", Context.MODE_PRIVATE) // "my_prefs"는 파일 이름과 비슷합니다. with (sharedPref.edit()) {
    putBoolean("auto_login", true) // 자동 로그인 여부 (boolean)     putString("user_name", "John Doe") // 사용자 이름 (String)     putInt("high_score", 12345) // 최고 점수 (Int)     apply() // 변경 사항을 비동기적으로 저장합니다. }

 

데이터 불러오기:

 

Kotlin
 
val sharedPref = getSharedPreferences("my_prefs", Context.MODE_PRIVATE)
val autoLogin = sharedPref.getBoolean("auto_login", false) // 기본값은 false val userName = sharedPref.getString("user_name", null) // 기본값은 null val highScore = sharedPref.getInt("high_score", 0) // 기본값은 0 

 

2.2. 내부 저장소(Internal Storage): 앱 전용 파일 저장 공간

2.2.1. 내부 저장소란?

내부 저장소는 앱만이 접근할 수 있는 비공개 저장 공간입니다. 여기에 저장된 파일은 해당 앱에서만 읽고 쓸 수 있으며, 사용자가 앱을 삭제하면 함께 삭제됩니다.

2.2.2. 언제 사용하면 좋을까요?
  • 앱에서만 사용하는 임시 파일
  • 캐시 데이터
  • 사용자에게 공개되지 않아야 할 민감한 데이터 (단, 보안에 더 신경 써야 합니다.)
2.2.3. 장단점
  • 장점:
    • 앱 전용 공간이므로 다른 앱에서 접근할 수 없습니다.
    • 앱 삭제 시 파일이 함께 삭제됩니다.
  • 단점:
    • 사용자가 직접 파일에 접근하거나 관리할 수 없습니다.
    • 저장 공간이 제한적일 수 있습니다.
2.2.4. 사용 예제

파일에 데이터 쓰기:

 

Kotlin
 
val fileName = "my_file.txt" val fileContents = "Hello, Internal Storage!" openFileOutput(fileName, Context.MODE_PRIVATE).use {
    it.write(fileContents.toByteArray())
}

 

파일에서 데이터 읽기:

 

Kotlin
 
val fileName = "my_file.txt" val fileContents = openFileInput(fileName).bufferedReader().useLines { lines ->
    lines.fold("") { some, text ->
        "$some\n$text"     }
}

 

 

반응형

 

2.3. 외부 저장소(External Storage): 공용 파일 저장, SD 카드도 OK!

2.3.1. 외부 저장소란?

외부 저장소는 모든 앱이 접근할 수 있는 공용 저장 공간입니다. SD 카드와 같이 탈착 가능한 저장소일 수도 있고, 내장된 공용 저장소일 수도 있습니다. 외부 저장소에 파일을 저장하려면 READ_EXTERNAL_STORAGE 또는 WRITE_EXTERNAL_STORAGE 권한을 AndroidManifest.xml 파일에 추가해야 합니다. (Android 10 (API 레벨 29) 이상부터는 범위 지정 저장소라는 것이 도입되어 권한에 대해서 더 신경써야 합니다.)

2.3.2. 언제 사용하면 좋을까요?
  • 사진, 음악, 동영상과 같이 사용자가 다른 앱과 공유하거나 직접 관리할 수 있는 파일
  • 다운로드 받은 파일
  • 크기가 큰 파일
2.3.3. 장단점
  • 장점:
    • 저장 공간이 넉넉합니다.
    • 사용자가 파일에 직접 접근하고 관리할 수 있습니다.
    • 다른 앱과 파일을 공유할 수 있습니다.
  • 단점:
    • 보안에 취약할 수 있습니다. (다른 앱에서 파일에 접근 가능)
    • 사용자가 언제든지 파일을 삭제하거나 수정할 수 있습니다.
    • 외부 저장소가 없을 수도 있습니다. (예: SD 카드가 없는 기기)
    • 권한이 필요합니다.
2.3.4. 사용 예제

외부 저장소 사용 가능 여부 확인:

 

Kotlin
 
fun isExternalStorageWritable(): Boolean {
    return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
}

fun isExternalStorageReadable(): Boolean {
    return Environment.getExternalStorageState() in
            setOf(Environment.MEDIA_MOUNTED, Environment.MEDIA_MOUNTED_READ_ONLY)
}

 

파일에 데이터 쓰기 (예: Downloads 디렉토리에):

 

Kotlin
 
if (isExternalStorageWritable()) {
    val file = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "my_file.txt")
    file.writeText("Hello, External Storage!")
}

 

파일에서 데이터 읽기:

 

Kotlin
 
if (isExternalStorageReadable()) {
    val file = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "my_file.txt")
    val text = file.readText()
}

 

2.4. SQLite 데이터베이스: 구조화된 데이터 저장의 정석

2.4.1. SQLite란?

SQLite는 **경량의 관계형 데이터베이스 관리 시스템(RDBMS)**입니다. 서버가 필요 없고, 파일 기반으로 작동하며, 안드로이드에 내장되어 있어 별도의 설치 없이 사용할 수 있습니다. 구조화된 데이터를 저장하고 관리하는 데 적합합니다.

2.4.2. 언제 사용하면 좋을까요?
  • 테이블 형태로 저장해야 하는 데이터 (예: 사용자 목록, 제품 목록, 할 일 목록 등)
  • 데이터 간의 관계를 정의해야 하는 경우 (예: 주문 정보와 주문에 포함된 제품 정보)
  • 데이터를 검색, 정렬, 필터링해야 하는 경우
2.4.3. 장단점
  • 장점:
    • 데이터를 체계적으로 저장하고 관리할 수 있습니다.
    • SQL 쿼리를 사용하여 데이터를 효율적으로 검색, 수정, 삭제할 수 있습니다.
    • 트랜잭션을 지원하여 데이터의 무결성을 보장할 수 있습니다.
  • 단점:
    • SharedPreferences나 파일 저장에 비해 사용법이 복잡합니다.
    • 데이터베이스 스키마를 설계하고 관리해야 합니다.
2.4.4. 사용 예제

SQLiteOpenHelper를 상속받아 데이터베이스 생성 및 관리:

 

Kotlin
 
class MyDatabaseHelper(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) {

    companion object {
        private const val DATABASE_VERSION = 1
        private const val DATABASE_NAME = "MyDatabase.db"
        private const val TABLE_NAME = "contacts"
        private const val COLUMN_ID = "_id"
        private const val COLUMN_NAME = "name"
        private const val COLUMN_EMAIL = "email"
    }

    override fun onCreate(db: SQLiteDatabase) {
        val CREATE_CONTACTS_TABLE = ("CREATE TABLE " + TABLE_NAME + "("
                + COLUMN_ID + " INTEGER PRIMARY KEY," + COLUMN_NAME + " TEXT,"
                + COLUMN_EMAIL + " TEXT" + ")")
        db.execSQL(CREATE_CONTACTS_TABLE)
    }

    override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
        db.execSQL("DROP TABLE IF EXISTS $TABLE_NAME")
        onCreate(db)
    }

    // 데이터 추가, 삭제, 조회, 수정 등의 메서드 구현 }

 

데이터 추가:

 

Kotlin
 
fun addContact(name: String, email: String) {
    val db = this.writableDatabase
    val values = ContentValues().apply {
        put(COLUMN_NAME, name)
        put(COLUMN_EMAIL, email)
    }
    db.insert(TABLE_NAME, null, values)
    db.close()
}

 

데이터 조회:

 

Kotlin
 
fun getAllContacts(): List<Contact> {
    val contacts = mutableListOf<Contact>()
    val selectQuery = "SELECT  * FROM $TABLE_NAME"
    val db = this.readableDatabase
    val cursor = db.rawQuery(selectQuery, null)
    if (cursor.moveToFirst()) {
        do {
            val contact = Contact(
                cursor.getInt(cursor.getColumnIndex(COLUMN_ID)),
                cursor.getString(cursor.getColumnIndex(COLUMN_NAME)),
                cursor.getString(cursor.getColumnIndex(COLUMN_EMAIL))
            )
            contacts.add(contact)
        } while (cursor.moveToNext())
    }
    cursor.close()
    return contacts
}

2.5. Room Persistence Library: SQLite를 더 쉽게, 객체 지향적으로!

2.5.1. Room이란?

Room은 SQLite를 더 쉽게 사용할 수 있도록 도와주는 추상화 레이어입니다. Room을 사용하면 반복적인 코드(boilerplate code)를 줄이고, 객체 지향적으로 데이터베이스를 다룰 수 있습니다. 또한, 컴파일 타임에 SQL 쿼리 검증을 제공하여 런타임 에러를 줄이는 데 도움이 됩니다.

2.5.2. Room의 주요 구성 요소
  • Entity: 데이터베이스 테이블을 나타내는 클래스입니다. @Entity 어노테이션을 사용합니다.
  • DAO (Data Access Object): 데이터베이스에 접근하는 메서드를 정의하는 인터페이스입니다. @Dao 어노테이션을 사용합니다.
  • Database: 데이터베이스를 생성하고 관리하는 클래스입니다. @Database 어노테이션을 사용하며, RoomDatabase를 상속받습니다.
2.5.3. 언제 사용하면 좋을까요?
  • SQLite를 사용해야 하지만, 직접 SQL 쿼리를 작성하고 싶지 않을 때
  • 객체 지향적인 방법으로 데이터베이스를 다루고 싶을 때
  • 컴파일 타임에 SQL 쿼리 오류를 확인하고 싶을 때
2.5.4. 장단점
  • 장점:
    • SQLite 사용을 간소화합니다.
    • 객체 지향적으로 데이터베이스를 다룰 수 있습니다.
    • 컴파일 타임에 SQL 쿼리 검증을 제공합니다.
    • LiveData, RxJava 등과 쉽게 통합할 수 있습니다.
  • 단점:
    • 학습 곡선이 있습니다. (SQLite에 비해)
    • 간단한 데이터베이스 작업에는 오버헤드가 될 수 있습니다.
2.5.5. 사용 예제

Entity:

 

Kotlin
 
@Entity(tableName = "contacts")
data class Contact(
    @PrimaryKey(autoGenerate = true) val id: Int = 0,
    @ColumnInfo(name = "name") val name: String?,
    @ColumnInfo(name = "email") val email: String?
)

 

DA1O:

 

Kotlin
 
@Dao interface ContactDao {
    @Query("SELECT * FROM contacts")
    fun getAllContacts(): List<Contact>

    @Insert
    fun insertContact(contact: Contact)

    @Update
    fun updateContact(contact: Contact)

    @Delete
    fun deleteContact(contact: Contact)
}

 

Database2:

 

Kotlin
 
@Database(entities = [Contact::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun contactDao(): ContactDao

    companion object {
        private var INSTANCE: AppDatabase? = null

        fun getInstance(context: Context): AppDatabase {

 

출처

 

반응형