[코드 리팩토링] '미주정복' 2일차 - Room 설정

이윤진·2024년 1월 15일
0

코드 리팩토링

목록 보기
3/12

1일차에 설명한 바와 같이 로컬 데이터베이스를 사용해야할 일이 생겼다.

Room 설정

1. build.gridle에 설정 변경

implementation("androidx.room:room-runtime:2.6.1")
annotationProcessor("androidx.room:room-compiler:2.6.1")

2. DatabaseModule 파일 생성

@Module
@InstallIn(SingletonComponent::class)
class DatabaseModule {
    @Provides
    @Singleton
    fun provideDatabase(
        @ApplicationContext context: Context
    )=
        Room.databaseBuilder(
            context,
            AppDatabase::class.java,
            context.getString(R.string.app_name)
        ).build()

    @Provides
    @Singleton
    fun provideUserDAO(database: AppDatabase) : UserDao = database.userDao()
}

같은 데이터베이스 인스턴트를 보낼 수 있도록 싱글톤으로 설정하였다.

3. Entity 만들기

UserData라는 Entity를 만들었다.

@Entity
data class UserData(
    @ColumnInfo(name = "user_id") val userId : Int,
    @ColumnInfo(name = "user_name") val userName : String,
    @ColumnInfo(name = "user_email") val email : String,
    @ColumnInfo(name = "holding_dollar") val holdingDollar : Float
){
    @PrimaryKey(autoGenerate = true)
    var idx : Int = 0
}

primaryKey는 필요가 없어서, 일단 자동으로 설정되도록 하였다.

4. DAO 만들기

데이터 액세스 객체는 데이터를 삽입, 삭제, 변경할 수 있는 메서드들이 있는 객체를 말한다.

@Dao
interface UserDao {
    @Insert
    suspend fun insertAll(vararg userData: UserData)

    // 전체 삭제
    @Query("DELETE FROM USERDATA")
    suspend fun deleteAll()

    // 전체 데이터 개수
    @Query("SELECT COUNT(user_id) FROM USERDATA")
    suspend fun countAll() : Int

    //user_name으로 user_email 찾기
    @Query("SELECT user_email FROM userdata WHERE user_name = :userName")
    suspend fun findEmailByName(userName : String) : String

    //user_email
    @Query("SELECT user_name FROM userdata WHERE user_email = :userEmail")
    suspend fun getNameByEmail(userEmail : String) : String
}

5. AppDatabase 작성

abstract class AppDatabase : RoomDatabase() {
    abstract fun userDao(): UserDao
}

6. 적용하기

viewModelScope.launch {
    // 저장된 내용이 없는 경우
    if (appDatabase.userDao().countAll() < 1) {
        appDatabase.userDao().insertAll(
            UserData(
                0,
                nickname!!,
                email!!,
                "0".toFloat()
            )
        )
    }
    // 저장된 내용이 다른 경우
    else if (appDatabase.userDao().getNameByEmail(email!!) != nickname) {
        UserData(
            0,
            nickname!!,
            email!!,
            "0".toFloat()
        )
    }
}

에러 발생

위와 같이 설정하고, 앱을 실행시키니 아래와 같은 에러가 떴다.

[Dagger/MissingBinding] @dagger.hilt.android.qualifiers.ActivityContext android.content.Context cannot be provided without an @Provides-annotated method.

이는 내가 LoginViewModel에서 context를 사용할 때, @ActivityContext 주석을 사용했기 때문이다. 이를 @ApplicationContext로 바꿔주면 해당 에러는 나지 않는다.

viewModel 의 생명주기가 Activity 보다 길기 때문에 Application Context 사용

error: [dagger.hilt.android.processor.internal.viewmodel.ViewModelValidationPlugin]
public abstract static class SingletonC implements Application_GeneratedInjector,
Injection of an @HiltViewModel class is prohibited since it does not create a ViewModel instance correctly.

고치고 다시 실행하니 새로운 에러가 나왔다.
이는 내가 LoginActivity에서 LoginView에 LoginViewModel을 넘겨주는 방식이 잘못되었기 때문인데,
나는 원래
@Inject lateinit var viewModel: LoginViewModel
이렇게 LoginViewModel을 받아오게 했었다.
그러나
private val viewModel : LoginViewModel by viewModels()
이렇게 ViewModel을 불러와야 한다고 한다.

[참고문헌]
https://developer.android.com/training/dependency-injection/hilt-jetpack?hl=ko

수정

데이터를 데이터베이스에 저장하는 과정에서 시간이 너무 오래 걸린다는 에러를 받았다.
이는 ViewScoped.lauch를 사용하였기 때문이다.
위의 파트를 CoroutineScope(Dispatchers.IO).lauch로 변경해주었다.

CoroutineScope(Dispatchers.IO).launch {
    // 저장된 내용이 없는 경우
    if (userDao.countAll() < 1) {
        userDao.insertAll(
            UserData(
                0,
                nickname!!,
                email!!,
                "0".toFloat()
            )
        )
        Log.d("Room", "UserData 저장")
    }
    // 저장된 내용이 다른 경우
    else if (userDao.findNameByEmail(email!!) != nickname) {
        userDao.insertAll(
            UserData(
                0,
                nickname!!,
                email!!,
                "0".toFloat()
            )
        )
        Log.d("Room", "UserData 저장")
    }
    else{
        Log.d("Room", "이미 저장된 내용")
    }
}
profile
Android/Flutter 개발

0개의 댓글