[AAC] Room 지속성 라이브러리

Yodi.Song·2021년 2월 10일
0

안드로이드

목록 보기
3/5
post-thumbnail

🧐 Room은 무엇일까?

Room Persistence Library

  • 구글에서 제공하는 ORM

    • 다른 ORM으로는 Realm이 있다
  • 기기에 데이터를 저장하고자 할 때 사용

  • 구성 요소

    • Room DataBase
      • 기본 SQLite 데이터베이스에 대한 엑세스 포인트 역할
      • 데이터베이스 작업 단순화
      • DAO를 사용해서 SQLite 데이터베이스에 쿼리 실행
    • DAO(Data Access Object)
      • SQL 쿼리들을 함수들로 매핑한다.
      • 사용자가 DAO의 함수를 호출하면 Room Database에서 해당 함수의 작업을 실행
    • Entitity
      • Room으로 작업할때 데이터베이스 테이블을 설명하는 annotated class


덧, 그 전에 용어 정리

ORM

Object Relational Mapping
객체 - 관계 매핑

  • 관계형 데이터베이스는 테이블을 사용하고 객체 지향 프로그래밍은 클래스를 사용하기 때문에 불일치가 발생한다.
  • 이런 문제를 해결하기 위해 객체와 관계를 매핑해주는 ORM을 사용한다.

Persistence(영속성)

  • 데이터를 생성한 프로그램이 종료되더라도 사라지지 않는 데이터의 특성
  • 영속성을 가지지 않는 데이터는 프로그램 종료시 소멸된다.
  • Object Persistence (영구적인 객체)
    • 메모리상의 데이터를 영구적으로 저장하여 영속성을 부여한다.
    • (덧) 데이터를 안드로이드 기기에 저장하는 3가지 방법
      1. SharedPreference
        • key - value로 데이터 저장
        • 환경설정, 화면 구성같은 가벼운 데이터 저장할 때 사용한다.
      2. File 입출력
      3. SQLite
        • DBMS지만 서버가 아니라 응용프로그램에서 사용하는 가벼운 데이터베이스
        • Room은 SQLite에 대한 추상 레이어

annotated class

이미지 출처: https://www.nextree.co.kr/p5864/





🥲 Room은 개체 참조를 허용하지 않는다

보통 ORM에서는 Entity가 서로를 참조할 수 있지만 Room은 명시적으로 금지하고 있다.

왜냐하면 클라이언트 단위의 데이터베이스이기 때문에 개체를 참조한다면 쿼리 처리에 오랜 시간이 걸리고 결과적으로 UI 스레드의 성능에 지장을 줄 수 있기 때문이다.

이런 이유로 개체 참조가 아니라 Entity간의 관계를 정의하는 형태로 사용해야한다.

예시 코드

@Entity 
data class Character ( 
  @PrimaryKey @ColumnInfo(name = "character_id") val characterId: Int, 
  @ColumnInfo(name = "place_belong_id") val placeBelongId: Int, 
  @ColumnInfo(name = "character_name") val characterName: String 
) 

data class PlaceWithCharacter(
  @Embedded val place: Place, 
  @Relation(
    parentColumn = "placeId", 
    entityColumn = "placeBelongId"
  ) 
  val characters: List<Character> 
)

출처: https://dunchi.tistory.com/94 [둔치의 개발이야기]

위 코드는 placecharacters:List<Character> 속성을 가지므로 palce와 character가 1:n 매핑이다.

만약 placecharacter: Character 였으면 1:1 매핑이었겠지.

더 정확하고 자세한건 안드로이드 공식문서를 참고하자




😎 Type Converter

위와 같은 이유로

Room은 Primitive Type과 Boxed Type(원시 자료형의 Wrapper Class)을 변환하는 기능은 제공하지만 항목 간 개체 참조는 허용하지 않는다.

(Room provides functionality for converting between primitive and boxed types but doesn't allow for object references between entities.)

때문에 사용자가 정의한 data type의 값을 단일 데이터베이스의 column에 저장하려고 할 때 TypeConverter를 사용해야 한다.

TypeConverter를 통해 Room에서 처리할 수 있는 데이터 타입으로 상호변환할 수 있다.

예시 코드

아래 코드는 Date 인스턴스를 유지하려는 상황이다.

아래와 같이 TypeConverter를 작성하여 데이터베이스에 동등한 Unix 타임스탬프를 저장할 수 있다.

class Converters {
        @TypeConverter
        fun fromTimestamp(value: Long?): Date? {
            return value?.let { Date(it) }
        }

        @TypeConverter
        fun dateToTimestamp(date: Date?): Long? {
            return date?.time?.toLong()
        }
    }
    
출처: https://developer.android.com/training/data-storage/room/referencing-data [Room을 사용하여 복잡한 데이터 참조]

활용 사례

뽀모도로 어플 프로젝트를 진행하다 빌드 에러가 났다.

error: Cannot figure out how to save tags field into database. You can consider adding a type converter for it.


Entity 역할을 하는 Data Class에 tags: ArrayList<String> 변수가 있었는데 이걸 처리하는 과정에서 발생한 오류였다.

아래와 같이 List을 Gson으로 변환하는 Converter를 만들어서 에러를 해결했다.

class Converters {
    @TypeConverter
    fun listToJson(value: List<String>?) = Gson().toJson(value)

    @TypeConverter
    fun jsonToList(value: String) = Gson().fromJson(value, Array<String>::class.java).toList()
}

0개의 댓글