Android Room

기존에 종목의 팔로잉 여부를 저장하기 위해 SQLite를 사용하였는데, 이번에 MVVM 구조를 공부하면서 Room이라는 라이브러리를 알게되었다. 공식 문서에서도 다음과 같은 이점 때문에 SQLite API 대신 Room을 사용하는 것을 추천하고 있다.

1. SQL 쿼리의 컴파일 시간 확인
2. 반복적이고 오류가 발생하기 쉬운 상용구 코드를 최소화하는 편의 주석
3. 간소화된 데이터베이스 이전 경로

1. Room의 구성요소

  • 위 그림은 공식문서에서 Room 라이브러리의 아키텍처를 설명한 그림이다. 크게 3가지로 나누어 설명을 하고 있는데,

    1. 데이터베이스 클래스(Room Database) : 데이터베이스를 보유하고 데이터 베이스와 연결을 위한 기본 액세스 포인트 역할을 함.
    2. 데이터 항목(Entities) : 데이터베이스의 테이블
    3. 데이터 액세스 객체 (DAO) : 데이터베이스의 데이터를 쿼리, 업데이트, 삽입, 삭제하는데 사용하는 메서드 제공
  • 추가로 공식문서에서 앱이 단일 프로세스에서 실행되는 경우에는 RoomDatabase가 굉장히 많은 리소스를 소모하므로 싱글톤 디자인 패턴으로 인스턴스화하라 명시되어있다.

1-1. Entity

  • 데이터베이스의 테이블로, 관련 필드의 집합체를 Entity로 정의한다.
    @Entity(tableName = "userName")
    data class User(
        @PrimaryKey val uid: Int,
        @ColumnInfo(name = "first_name") val firstName: String?,
        @ColumnInfo(name = "last_name") val lastName: String?
    )
  • 데이터 클래스를 정의시 @Entity의 애노테이션으로 클래스를 정의해주어야 한다.
  • 각 Entity는 테이블의 각 행을 고유하게 식별하는 하나 이상의 기본 키를 정의해야한다.
    현재 @PrimaryKey를 통해 uid값이 기본 키임을 명시하였다.
  • tableName 속성으로 테이블이름을 데이터클래스명이 아닌 다른 이름으로 지정할 수 있다.
  • 컬럼의 이름은 @ColumInfo로 지정이 가능하다.
  • @PrimaryKey(autoGenerate = true)를 통해 자동으로 키값 생성이 가능하다.

1-2. DAO

  • 데이터베이스에 액세스하는데 사용하는 쿼리를 메서드로 만들 수 있는데 이 때, 이 메서드를 정의하는 인터페이스가 DAO다.

    @Dao
    interface UserDao {
        @Insert
        fun insert(user: User)
        
        // 충돌시 발생하는 경우를 대비할 수 있다.
        @Insert(onConflict = OnConflictStrategy.REPLACE)
        fun insert1(...)
          
        @Update
        fun update(user: User)
    
        @Delete
        fun delete(user: User)
    }
    • Insert : 단일 트랜잭션의 데이터베이스에 모든 매개변수를 삽입하는 구현을 생성한다.
      • onConflict 속성을 통해 테이블에 Entitiy 삽입시 같은 값인 경우 충돌이 발생하는데, 어떻게 해결할 지 정의할 수 있다.
        1. OnConflictStrategy.REPLACE : 충돌 발생시 새로 들어온 데이터로 교체
        2. OnConflictStrategy.IGNORE : 충돌 발생시 새로 들어온 데이터 무시
    • Update : 데이터베이스에서 매개변수로 지정된 Entity 집합을 수정하는데, 이 때 Entity의 기본 키와 일치하는 조회를 사용한다.
    • Delete : Update와 마찬가지로 매개변수로 지정된 Entity 집합의 기본키를 활용하여 엔티티를 삭제한다.
  • 위에서 말한 3가지 외에도 직접 @Query을 작성하여 원하는 기능의 메서드를 작성할 수 있다.

    @Dao
    interface UserDao {
        @Query("SELECT * FROM User")
        fun getAll(): List<User>
        @Query("DELETE FROM User WHERE name = :name")
        fun deleteUserByName(name: String)
    }
    • 위의 예시처럼 어떤 동작을 할 것인지 sql 문법을 통해 @Query와 함께 작성해주면 된다.
  • Observable query

    • 쿼리 수행으로 인한 데이터 변경시 해당 내용이 앱의 UI에 반영되어야 하는 경우 때문에 쿼리 메서드 description에 LiveData 유형의 반환 값을 사용할 수 있다.

    • Room은 데이터베이스 업데이트시 LiveData를 업데이트하는데 필요한 모든 코드를 생성한다.

      interface UserBookDao {
        @Query(
            "SELECT user.name AS userName, book.name AS bookName " +
            "FROM user, book " +
            "WHERE user.id = book.user_id"
        )
        fun loadUserAndBookNames(): LiveData<List<UserBook>>
      
        // You can also define this class in a separate file.
        data class UserBook(val userName: String?, val bookName: String?)
      }

좋은 웹페이지 즐겨찾기