
💜 Room의 이점
- SQL 쿼리의 컴파일 시간 확인
- 반복적이고 오류가 발생하기 쉬운 상용구 코드를 최소화하는 편의 주석
- 간소화된 데이터베이스 이전 경로
build.gradle (:app)
plugins {
	id 'kotlin-kapt'
}
dependencies {
	// ROOM Database 사용
    implementation("androidx.room:room-runtime:2.4.3")
    annotationProcessor("androidx.room:room-compiler:2.4.3")
    kapt("androidx.room:room-compiler:2.4.3")
    implementation("androidx.room:room-ktx:2.4.3")
}
@Entity(tableName = "table_bookmark")
data class ParkBookmarkEntity(
    @PrimaryKey
    val parkingCode: String = "",
    val parkingName: String = "",
    val addr: String = "",
    val lat: Double = 0.0,
    val lng: Double = 0.0,
    val tel: String = "",
    val operation_rule_nm: String = "",
    var isSelected:Boolean = false
)@Dao
interface ParkDAO {
    @Query("SELECT * FROM table_bookmark")
    fun getAll(): List<ParkBookmarkEntity>
    // parkingCode 에 해당하는 selected 값 가져오기
    @Query("SELECT isSelected FROM table_bookmark WHERE parkingCode = :parkingCode")
    fun getBookmark(parkingCode: String) : Boolean
    // bookmark 저장 - 중복 값 충돌 발생 시 새로 들어온 데이터로 교체.
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun saveBookmark(bookmarkEntity: ParkBookmarkEntity)
    // bookmark 삭제
    @Delete
    fun deleteBookmark(bookmarkEntity: ParkBookmarkEntity)
}@Database(entities = [ParkBookmarkEntity::class], version = 1)
abstract class ParkDatabase : RoomDatabase() {
    abstract fun parkDao(): ParkDAO
    // 데이터 베이스 객체를 싱글톤으로 인스턴스.
    companion object {
        private var instance: ParkDatabase? = null
        @Synchronized
        fun getInstance(context: Context): ParkDatabase? {
            if (instance == null)
                synchronized(ParkDatabase::class) {
                    instance = Room.databaseBuilder(
                        context.applicationContext,
                        ParkDatabase::class.java,
                        "park.db"
                    )
                        .build()
                }
            return instance
        }
        fun destroyInstance() {
            instance = null
        }
    }
}class ParkBottomSheetFragment(val mContext : Context) : BottomSheetDialogFragment() {
	private var db: ParkDatabase? = null
	override fun onCreateView(...): View? {
        super.onCreateView(inflater, container, savedInstanceState)
        
        // 생략
        
        db = ParkDatabase.getInstance(mContext)
}

// 버튼 xml
<androidx.appcompat.widget.AppCompatButton
            android:id="@+id/btn_bookmark"
            android:layout_width="wrap_content"
            android:layout_height="36dp"
            android:background="@drawable/selector_btn_bookmark"
            android:drawableLeft="@drawable/ic_heart"
            android:paddingHorizontal="@dimen/margin_20"
            android:layout_marginEnd="10dp"
            android:text="즐겨찾기"
            android:textColor="@color/white"
            android:textSize="16dp"
            android:textStyle="bold" />
            
// selector
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_selected="false">
        <shape android:shape="rectangle">
            <solid android:color="@color/main_color" />
        </shape>
    </item>
    <item android:state_selected="true">
        <shape android:shape="rectangle">
            <solid android:color="@color/main_purple1" />
        </shape>
    </item>
</selector>// 즐겨찾기 저장하기
btnBookmark.setOnClickListener {
	// UI 와 상호작용하기 위해 사용.
	CoroutineScope(Dispatchers.Main).launch {
		val temp: Deferred<Boolean> = async(Dispatchers.IO) { // async 로 결과를 반환
			val isBookmarked = db!!.parkDao().getBookmark(data.PARKING_CODE)
			if (isBookmarked) { // 이미 즐겨찾기 되어있으면 삭제
				db!!.parkDao().deleteBookmark(bookmarkData)
				false
                
                
			} else { // 없으면 즐겨찾기 저장
				db!!.parkDao().saveBookmark(bookmarkData)
				true
			}
		}
        			// UI 변경
                    val selected = temp.await() // async 작업이 완료 되고 난 후 호출.
                    it.isSelected = selected
                }
            }
// 즐겨찾기 된 데이터 가져오기
btnGetData.setOnClickListener {
		CoroutineScope(Dispatchers.IO).launch { // 코루틴 사용 비동기로 실행.
			val parkData = db!!.parkDao().getAll()
			Utils.Log("db 조회 -> $parkData")
		}
}// 즐겨찾기 삭제하기
btnDeleteData.setOnClickListener {
	CoroutineScope(Dispatchers.IO).launch { // 코루틴 사용 비동기로 실행.
		db!!.parkDao().deleteBookmark(bookmarkData)
			Utils.Log("즐겨찾기 삭제 완료")
		}
}순서대로 클릭해서 즐겨찾기를 하고, 리스트를 가져오고, 삭제한 후, 잘 찍히는 로그들을 확인할 수 있다.

공식 문서 : https://developer.android.com/training/data-storage/room?hl=ko