Room을 사용하여 복잡한 데이터 참조

Room은 기본 유형과 박싱된 유형 간 변환을 위한 기능을 제공하지만 항목 간 개체 참조는 허용하지 않습니다. 본 문서에서는 유형 변환기를 사용하는 방법 및 Room이 개체 참조를 지원하지 않는 이유를 설명합니다.

유형 변환기 사용

때로 앱은 개발자가 단일 데이터베이스 열에 값을 저장하려고 하는 맞춤 데이터 유형을 사용해야 합니다. 이러한 종류의 맞춤 유형 지원을 추가하려면 TypeConverter를 제공하면 됩니다. 이 유형 변환기는 Room이 유지할 수 있는 알려진 유형과 맞춤 클래스를 상호 변환합니다.

예를 들어 Date 인스턴스를 유지하려는 경우 다음 TypeConverter를 작성하여 데이터베이스에 동등한 Unix 타임스탬프를 저장할 수 있습니다.

Kotlin

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

        @TypeConverter
        fun dateToTimestamp(date: Date?): Long? {
            return date?.time?.toLong()
        }
    }
    

자바

    public class Converters {
        @TypeConverter
        public static Date fromTimestamp(Long value) {
            return value == null ? null : new Date(value);
        }

        @TypeConverter
        public static Long dateToTimestamp(Date date) {
            return date == null ? null : date.getTime();
        }
    }
    

위 예에서는 두 개의 함수를 정의합니다. 하나는 Date 객체를 Long 객체로 변환하고 다른 하나는 Long에서 Date로의 역변환을 실행합니다. Room은 Long 객체를 유지하는 방법을 이미 알고 있으므로 이 변환기를 사용하여 Date 유형의 값을 유지할 수 있습니다.

이제 AppDatabase 클래스에 @TypeConverters 주석을 추가합니다. 그러면 AppDatabase의 각 항목DAO에 관해 정의한 변환기를 Room이 사용할 수 있습니다.

AppDatabase

Kotlin

    @Database(entities = arrayOf(User::class), version = 1)
    @TypeConverters(Converters::class)
    abstract class AppDatabase : RoomDatabase() {
        abstract fun userDao(): UserDao
    }
    

자바

    @Database(entities = {User.class}, version = 1)
    @TypeConverters({Converters.class})
    public abstract class AppDatabase extends RoomDatabase {
        public abstract UserDao userDao();
    }
    

이러한 변환기를 사용하면 다음 코드 스니펫에서와 같이 기본 유형을 사용하는 것처럼 다른 쿼리에서 맞춤 유형을 사용할 수 있습니다.

User

Kotlin

    @Entity
    data class User(private val birthday: Date?)
    

자바

    @Entity
    public class User {
        private Date birthday;
    }
    

UserDao

Kotlin

    @Dao
    interface UserDao {
        @Query("SELECT * FROM user WHERE birthday BETWEEN :from AND :to")
        fun findUsersBornBetweenDates(from: Date, to: Date): List<User>
    }
    

자바

    @Dao
    public interface UserDao {
        @Query("SELECT * FROM user WHERE birthday BETWEEN :from AND :to")
        List<User> findUsersBornBetweenDates(Date from, Date to);
    }
    

개별 항목, DAO 및 DAO 메서드를 포함하여 다양한 범위로 @TypeConverters를 제한할 수도 있습니다. 자세한 내용은 @TypeConverters 주석에 관한 참조 문서에서 확인하세요.

Room에서 개체 참조를 허용하지 않는 이유 이해

주요 요점: Room은 항목 클래스 간의 개체 참조를 허용하지 않습니다. 대신 앱에 필요한 데이터를 명시적으로 요청해야 합니다.

데이터베이스에서 각 개체 모델로 관계를 매핑하는 것은 일반적인 관행이며 이러한 매핑은 서버 측에서 매우 잘 작동합니다. 필드가 액세스될 때 프로그램이 필드를 로드하는 경우에도 서버는 여전히 잘 작동합니다.

그러나 클라이언트 측에서는 이 유형의 지연 로드가 일반적으로 UI 스레드에서 발생하기 때문에 실행 가능하지 않으며 UI 스레드에서 디스크에 관한 정보를 쿼리하면 상당한 성능 문제가 발생합니다. 일반적으로 UI 스레드는 활동의 업데이트된 레이아웃을 계산하고 그리는 데 약 16ms를 소요하므로 쿼리가 5ms밖에 걸리지 않은 경우에도 앱에서 프레임을 그리는 데 여전히 시간이 부족할 가능성이 크며 이에 따라 분명한 시각적 결함이 발생할 수 있습니다. 병렬로 실행 중인 별도의 트랜잭션이 있거나 기기가 다른 디스크 집약적인 작업을 실행 중이면 쿼리가 완료되는 데 훨씬 많은 시간이 걸릴 수 있습니다. 그러나 지연 로드를 사용하지 않으면 앱이 필요한 것보다 더 많은 데이터를 가져오며 이에 따라 메모리 소비 문제가 발생합니다.

개체 관계형 매핑은 일반적으로 개발자가 앱 사용 사례에 가장 적합한 모든 것을 할 수 있도록 이 결정을 개발자에게 맡깁니다. 개발자는 일반적으로 앱과 UI 간에 모델을 공유하려고 합니다. 그러나 이 방법은 확장성이 좋지 않습니다. 시간이 지남에 따라 UI가 변경되므로 공유된 모델이 개발자가 예측 및 디버그하기 어려운 문제를 일으키기 때문입니다.

예를 들어 각 도서에 Author 객체가 있는 Book 객체 목록을 로드하는 UI를 생각해 보세요. 처음에는 지연 로드를 사용하여 Book 인스턴스가 저자를 검색하도록 하는 쿼리를 디자인할 수 있습니다. author 필드의 첫 번째 검색은 데이터베이스를 쿼리합니다. 그리고 얼마 후에 앱 UI에도 저자 이름을 표시해야 한다는 사실을 알게 되었습니다. 다음 코드 스니펫에서와 같이 이 이름에 매우 쉽게 액세스할 수 있습니다.

Kotlin

    authorNameTextView.text = book.author.name
    

자바

    authorNameTextView.setText(book.getAuthor().getName());
    

그러나 외견상 무해한 이 변경사항으로 인해 기본 스레드에서 Author 테이블이 쿼리됩니다.

저자 정보를 미리 쿼리하면 데이터가 더 이상 필요 없는 경우에 데이터가 로드되는 방식을 변경하기가 어려워집니다. 예를 들어 앱의 UI가 더 이상 Author 정보를 표시하지 않아도 되는 경우에도 앱은 더 이상 표시하지 않는 데이터를 사실상 로드하여 소중한 메모리 공간을 낭비합니다. 앱의 효율성은 Author 클래스가 Books와 같은 다른 테이블을 참조하면 훨씬 더 저하됩니다.

Room을 사용하여 여러 항목을 동시에 참조하려면 각 항목이 포함된 POJO를 생성한 후 테이블을 조인하는 쿼리를 작성하세요. Room의 강력한 쿼리 유효성 검사 기능과 결합되어 제대로 구조화된 이 모델을 사용하면 앱이 데이터를 로드할 때 더 적은 리소스를 소비하므로 앱 성능 및 사용자 환경을 향상할 수 있습니다.