🍞 Front-End/Android

[Android] 데이터 저장 방법 (SharedPreferences, RoomDB)

박빵이 2022. 11. 20. 15:22

Android에서 Database를 왜 배울까?

대부분의 정보는 서버에 저장하지만 클라이언트에서만 저장해야 하는 값도 존재한다. 또한 이러한 지식을 알고 있으면 서버 개발자와의 협업에도 유리하다! 그래서 안드로이드를 공부하더라도 DB를 배우는 것이 좋다. 안드로이드에서도 (Key-Value 및 관계형 데이터베이스) 형태 기반으로 값을 저장 가능할 수 있다.

 

대표적인 저장 방법으로는 3가지가 있다.

- File (text...)

- SharedPreferences (Key-Value)

- RoomDB (관계형 DB)

 

📌 SharedPreferences

안드로이드에서 간단하게 값을 저장하고 불러오는 방법이다.

기본적인 자료구조만 가능하기 때문에 하나의 Key에 다중 값을 저장하기는 어렵다!

 

Key-Value 형태로 값을 저장

Key는 String Type을 사용

Value는 기본적인 자료 구조만 가능 (Boolean, Long, String, Int, Double, Set (순서 없이 여러 개 담는))

 

❗하나의 Key에 다중값을 담을 순 없을까?

Set 자료형으로 저장 -> 순서가 구분되지 않는다.

String에 구분자로 저장 -> 구분자가 값에 들어가면 문제가 된다.

String으로 여러 데이터를 표현하는 데이터 포맷인 JSON 사용 ->

- Key-Value 형태로 해석 가능한 문자열 기반 포맷

- ex. {"name":"에릭", "age":23}

- 외부 라이브러리를 사용하여 변환 가능 ex) GSON

- 네트워크 통신에서 값을 주고받는데도 자주 사용된다.

 

http 통신에는 객체 개념이 없기 때문에 주로 문자열로 주고받아야 하는데

JSON 형태로 받으면 서버에서 바로 이해할 수 있다!

package com.example.dbproject

import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import com.example.dbproject.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // packageName or 임의로 name 정해주고 모드 설정해줌
        val sharedPrefs = getSharedPreferences("sharedprefs", Context.MODE_PRIVATE)
        // sharedPrefs 객체를 가져오고 editor 를 가져온다.
        val editor = sharedPrefs.edit()

        // editor 로 값 수정 (key-value)
        editor.putString("yejin", "android")
        // 저장 다 했다는 의미로 apply
        editor.apply()

        // 가져오는 것 (""는 값이 없으면 나오는 것)
        val spvalue = sharedPrefs.getString("yejin", "")
        Log.d("SP", "${spvalue}")
    }
}

android라는 값이 정상적으로 출력되는 것을 볼 수 있다.

 

📌 RoomDB

데이터베이스를 서버 대신 파일 형태로 저장하는 SQLite 기반

관계형 데이터베이스 구조

SQL문 사용 (자주 사용되는 Insert, Delete는 SQL문 작성 없이 가능)

 

Room을 사용하여 로컬 데이터베이스에 데이터 저장  |  Android 개발자  |  Android Developers

Room 라이브러리를 사용하여 더 쉽게 데이터를 유지하는 방법 알아보기

developer.android.com

 

- Database (데이터베이스)

    저장하는 데이터의 집합 단위를 말합니다

 

- Entity (항목)

    데이터베이스 내의 테이블을 의미합니다

 

- DAO (다오)

    데이터베이스에 접근하는 함수(insert,update,delete,...)를 제공합니다

 

 

우선, RoomDB를 사용하기 위해 build.gradle 파일을 수정해준다.

이 코드를 추가해주지 않으면 Sync 오류가 난다. 꼭 추가하기!

 

파일구성

 

AppDatabase, User, UserDao를 만들어주면 RoomDB 초기 세팅은 끝난다!

 

AppDatabase.kt

package com.example.dbproject

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase

// DB 와 객체를 넣는 과정
// 여러 클래스가 들어올 수 있으니 배열, 바뀔 수도 있으니 버전을 줌
@Database(entities = [User::class], version = 1)
abstract class AppDatabase: RoomDatabase() {
    // 자동으로 Dao 값 채워줌
    abstract fun userDao(): UserDao
    // 싱글톤 패턴 (하나의 앱에서 사용될 때 DB 하나를 전역적으로 재활용)
    companion object {
        // database 가 담기는 객체
        private var appDatabase: AppDatabase? = null

        @Synchronized // 여러 스레드에서 하나의 자원에 접근하려고 할 때 그것을 방지
        fun getInstance(context: Context): AppDatabase? {
            if (appDatabase == null) { // null 일 때 초기화를 해줘야함
                synchronized(AppDatabase::class.java){ // 이 클래스가 점유하고 있다는 뜻
                    appDatabase = Room.databaseBuilder(
                        context.applicationContext, // 전역적
                        AppDatabase::class.java,
                        "app-database", // 이름 다 다르게
                    ).allowMainThreadQueries().build()
                }
            }
            return appDatabase
        }
    }
}

 

User.kt

package com.example.dbproject

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity // RoomDB 에서 사용되는 객체라는 것을 알리기 위해
data class User (
    @ColumnInfo(name = "name") val name: String,
    @ColumnInfo(name = "age") val age: Int,
    // 고유키 autoGenerate 자동으로 증가
    @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "userId") val userId: Int = 0
)

 

UserDao.kt

꼭 인터페이스로 설정!

package com.example.dbproject

import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query

@Dao
interface UserDao {
    @Insert
    fun insert(user: User)

    @Delete
    fun delete(user: User)

    // 가져오는 것
    @Query("SELECT * FROM User")
    fun selectAll(): List<User>

    @Query("SELECT * FROM User WHERE userId = :userId")
    fun selectByUserId(userId: Int): User

    @Query("SELECT * FROM User WHERE name = :name")
    fun selectByUserName(name: String): List<User> // 동일 이름 있을 수 있어서

    // 업데이트
    @Query("UPDATE User SET name = :name WHERE userId = :userId")
    fun updateNameByUserId(userId: Int, name: String)
}

 

DB CRUD 시작

데이터 생성 (Create)

val roomDb = AppDatabase.getInstance(this) // nullable 한 DB

if(roomDb != null) {
    val user = User("예진", 22)
    roomDb.userDao().insert(user) // insert 할 때마다 고유 id 자동으로 증가

    val userList = roomDb.userDao().selectAll()
    Log.d("DB", "User List : ${userList}")
}

 

데이터 수정 (Update)

val roomDb = AppDatabase.getInstance(this) // nullable 한 DB

if(roomDb != null) {
    roomDb.userDao().updateNameByUserId(1, "예슬")

    val userList = roomDb.userDao().selectAll()
    Log.d("DB", "User List : ${userList}")
}

 

데이터 삭제 (Delete)

val roomDb = AppDatabase.getInstance(this) // nullable 한 DB

if(roomDb != null) {
    val deletedUser = User("", 0, 1)
    roomDb.userDao().delete(deletedUser)

    val userList = roomDb.userDao().selectAll()
    Log.d("DB", "User List : ${userList}")
}

 

데이터 아이디로 찾기 (Read)

앞에서 다 지워버려서 안드라는 user를 추가해주고 userId 2를 가진 유저를 찾아줬다.

val roomDb = AppDatabase.getInstance(this) // nullable 한 DB

if(roomDb != null) {
    val user = roomDb.userDao().selectByUserId(2)

    Log.d("DB", "User List : ${user}")
}