기록
[GPT가 알려주는] DAO, Entity, Repository, Room, Impl, Viewmodel, Hilt 본문
[GPT가 알려주는] DAO, Entity, Repository, Room, Impl, Viewmodel, Hilt
heylo 2025. 6. 2. 11:49좋습니다! Android 앱에서 Room DB를 활용할 때의 핵심 구성요소인 다음 6가지를 이해하셔야 전체 흐름이 잡힙니다:
✅ Entity
✅ DAO
✅ Repository
✅ RepositoryImpl (선택적)
✅ Room Database
✅ ViewModel
아래에 그림 없이 글로 깔끔하게 전체 구조와 흐름을 설명드릴게요.
✅ 1. Entity – 데이터베이스의 테이블 정의
- @Entity는 Room이 테이블을 생성하기 위한 **데이터 구조(클래스)**를 정의하는 것
- 각 필드는 **컬럼(column)**이 됨
@Entity(tableName = "devices")
data class Device(
@PrimaryKey val nodeId: String,
val fabricId: String,
val commissionerName: String,
val timestamp: Long
)
Room은 이 data class를 기반으로 SQLite 테이블을 자동 생성합니다.
✅ 2. DAO – 데이터베이스 접근 메서드 정의
- DAO(Data Access Object)
- DB에 읽기/쓰기/삭제/업데이트하는 쿼리 메서드를 정의
@Dao
interface DeviceDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertDevice(device: Device)
@Query("SELECT * FROM devices")
fun getAllDevices(): Flow<List<Device>>
@Query("DELETE FROM devices WHERE nodeId = :nodeId")
suspend fun deleteDevice(nodeId: String)
}
Room이 이 인터페이스를 자동 구현해서 DB와 연결시켜줍니다.
✅ 3. RoomDatabase – 전체 DB 설정 클래스
- Room의 진짜 DB 객체를 생성하는 곳
- DAO들을 포함하며 앱 전체에서 하나만 존재해야 함 (Singleton)
@Database(entities = [Device::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun deviceDao(): DeviceDao
}
Room.databaseBuilder()로 생성함. Hilt나 싱글톤으로 주입하기도 함.
✅ 4. Repository – 데이터 소스를 추상화한 계층
- ViewModel이 DB에 직접 접근하지 않도록 중간에서 데이터 흐름을 추상화
- 필요 시 Web API + DB + Cache 등을 통합하는 역할
interface DeviceRepository {
suspend fun addDevice(device: Device)
fun getAllDevices(): Flow<List<Device>>
suspend fun deleteDevice(nodeId: String)
}
✅ 5. RepositoryImpl – Repository의 실제 구현체
- DAO를 호출하여 실제 동작을 수행
- ViewModel과의 사이를 연결
class DeviceRepositoryImpl(private val deviceDao: DeviceDao) : DeviceRepository {
override suspend fun addDevice(device: Device) {
deviceDao.insertDevice(device)
}
override fun getAllDevices(): Flow<List<Device>> {
return deviceDao.getAllDevices()
}
override suspend fun deleteDevice(nodeId: String) {
deviceDao.deleteDevice(nodeId)
}
}
테스트 시에는 Repository만 Mock 처리하고 실제 구현체를 바꿔치기할 수 있음\
✅ 6. ViewModel – UI와 데이터 사이의 연결 담당
- Repository로부터 데이터를 받아서 UI에 전달
- 상태 관리, 생명주기 관리, Flow/LiveData 관리
class DeviceViewModel(private val repository: DeviceRepository) : ViewModel() {
val allDevices: StateFlow<List<Device>> = repository.getAllDevices()
.stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
fun addDevice(device: Device) {
viewModelScope.launch {
repository.addDevice(device)
}
}
fun deleteDevice(nodeId: String) {
viewModelScope.launch {
repository.deleteDevice(nodeId)
}
}
}
🎯 전체 흐름 요약
[ UI (Fragment, Activity) ]
↓
[ ViewModel ]
↓
[ Repository Interface ]
↓
[ RepositoryImpl ]
↓
[ DAO ]
↓
[ Room Database ]
✅ 예: "기기 등록하기"의 흐름
- 사용자가 등록 버튼 클릭 → ViewModel의 addDevice() 호출
- ViewModel → Repository의 addDevice() 호출
- RepositoryImpl → DAO의 insertDevice() 실행
- DAO → Room이 SQLite에 insert 쿼리 실행
필요하시면 Hilt 주입 구조, @Singleton, @Provides, Room 모듈 세팅까지 확장해드릴 수 있어요.
원하시는 방식(예: Hilt 사용 여부) 알려주시면 코드로 도와드릴게요!
Hilt란?
Hilt는 Android에서 **의존성 주입(Dependency Injection, DI)**을 쉽게 해주는 도구입니다.
한마디로 말하면:
✅ 클래스 간의 연결을 자동으로 관리해주는 도우미 프레임워크
🔧 왜 Hilt를 쓰는가?
기존 코드:
val repository = DeviceRepository(DeviceDao())
val viewModel = DeviceViewModel(repository)
Hilt 코드:
@Inject lateinit var viewModel: DeviceViewModel
➡ 클래스 간의 객체 생성 / 연결 / 수명 주기 관리를 자동으로 해줘서
코드가 간결해지고, 테스트도 쉬워집니다.
🧩 Hilt는 어떤 역할을 하나요?
구성요소 | 설명 |
@Inject | 필요한 객체를 자동으로 넣어줌 |
@Module | 어떻게 만들지 정의 (생성자 X인 경우) |
@Provides | 특정 방식으로 객체를 만들어주는 함수 |
@Singleton | 앱 전역에서 하나만 사용함 |
@HiltAndroidApp | 애플리케이션 클래스에 붙여서 DI 시작점으로 지정 |
@AndroidEntryPoint | ViewModel, Fragment, Activity 등에서 Hilt 사용 가능하게 함 |
🔄 의존성 흐름 예시
ViewModel → Repository → DAO 구조를 Hilt로 만들면:
1. @Inject로 생성자 연결
class DeviceRepository @Inject constructor(private val dao: DeviceDao)
class DeviceViewModel @Inject constructor(private val repository: DeviceRepository)
2. DAO와 Room은 @Module에서 제공
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
fun provideDatabase(@ApplicationContext context: Context): AppDatabase {
return Room.databaseBuilder(context, AppDatabase::class.java, "app.db").build()
}
@Provides
fun provideDeviceDao(db: AppDatabase): DeviceDao = db.deviceDao()
}
3. Application 클래스에 @HiltAndroidApp
@HiltAndroidApp
class MyApp : Application()
4. Activity나 Fragment에서 Hilt 사용
@AndroidEntryPoint
class DeviceFragment : Fragment() {
private val viewModel: DeviceViewModel by viewModels()
}
✅ 장점 요약
장점 | 설명 |
코드 간결 | 객체 생성/연결 코드 필요 없음 |
테스트 용이 | 주입만 바꿔서 가짜(Mock) 사용 가능 |
수명 주기 자동 관리 | ViewModel, Singleton 등 알아서 관리 |
Room, Retrofit, ViewModel 모두 DI 가능 | 통합 관리 가능 |
❗ 안 쓰면 생기는 불편
- 각 클래스마다 생성자 연결 직접 해줘야 함
- 테스트 시 객체 바꿔끼우기 어려움
- 수동으로 싱글톤 관리 (Static 객체 남발)
✅ 요약 한 줄
Hilt는 안드로이드 앱에서 객체들 간 연결을 자동화해주는 DI 프레임워크이며,
코드 간결성 + 테스트 효율성 + 생명주기 관리를 한꺼번에 해결해줍니다.
필요하다면 Hilt 기반 전체 구조 예제도 제공드릴게요! (ViewModel, Repository, Room까지 연결)
좋습니다! 아래는 Hilt 기반으로 구성된 Android 앱 구조 예제입니다.
Room + Repository + ViewModel 전부 Hilt로 연결합니다.
🎯 전체 흐름
[ UI (Fragment) ]
↓
[ ViewModel ] ← @Inject
↓
[ Repository ] ← @Inject
↓
[ DAO (Room) ] ← @Provides
↓
[ RoomDatabase ] ← @Provides
✅ 1. Entity – Device.kt
@Entity(tableName = "devices")
data class Device(
@PrimaryKey val nodeId: String,
val fabricId: String,
val commissionerName: String,
val timestamp: Long
)
✅ 2. DAO – DeviceDao.kt
@Dao
interface DeviceDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertDevice(device: Device)
@Query("SELECT * FROM devices")
fun getAllDevices(): Flow<List<Device>>
@Query("DELETE FROM devices WHERE nodeId = :nodeId")
suspend fun deleteDevice(nodeId: String)
}
✅ 3. RoomDatabase – AppDatabase.kt
@Database(entities = [Device::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun deviceDao(): DeviceDao
}
✅ 4. Repository – DeviceRepository.kt
interface DeviceRepository {
suspend fun addDevice(device: Device)
fun getDevices(): Flow<List<Device>>
suspend fun deleteDevice(nodeId: String)
}
✅ 5. RepositoryImpl – DeviceRepositoryImpl.kt
class DeviceRepositoryImpl @Inject constructor(
private val dao: DeviceDao
) : DeviceRepository {
override suspend fun addDevice(device: Device) {
dao.insertDevice(device)
}
override fun getDevices(): Flow<List<Device>> = dao.getAllDevices()
override suspend fun deleteDevice(nodeId: String) {
dao.deleteDevice(nodeId)
}
}
✅ 6. ViewModel – DeviceViewModel.kt
@HiltViewModel
class DeviceViewModel @Inject constructor(
private val repository: DeviceRepository
) : ViewModel() {
val devices = repository.getDevices()
.stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
fun addDevice(device: Device) {
viewModelScope.launch {
repository.addDevice(device)
}
}
fun deleteDevice(nodeId: String) {
viewModelScope.launch {
repository.deleteDevice(nodeId)
}
}
}
✅ 7. Hilt Module – AppModule.kt
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideDatabase(@ApplicationContext context: Context): AppDatabase {
return Room.databaseBuilder(
context,
AppDatabase::class.java,
"device_db"
).build()
}
@Provides
fun provideDeviceDao(db: AppDatabase): DeviceDao = db.deviceDao()
@Provides
fun provideDeviceRepository(dao: DeviceDao): DeviceRepository {
return DeviceRepositoryImpl(dao)
}
}
✅ 8. Application 클래스 – MyApp.kt
@HiltAndroidApp
class MyApp : Application()
✅ 9. Fragment 사용 – DeviceFragment.kt
@AndroidEntryPoint
class DeviceFragment : Fragment() {
private val viewModel: DeviceViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewModel.devices.onEach { devices ->
// RecyclerView에 표시
}.launchIn(viewLifecycleOwner.lifecycleScope)
}
}
✨ 추가 설정
build.gradle (모듈)
plugins {
id 'kotlin-kapt'
id 'dagger.hilt.android.plugin'
}
dependencies {
implementation "com.google.dagger:hilt-android:2.48"
kapt "com.google.dagger:hilt-compiler:2.48"
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03"
kapt "androidx.hilt:hilt-compiler:1.0.0"
}
build.gradle (프로젝트)
buildscript {
dependencies {
classpath "com.google.dagger:hilt-android-gradle-plugin:2.48"
}
}
✅ 결과 요약
계층 | 구성 | DI 방식 |
ViewModel | @HiltViewModel, @Inject constructor | 자동 주입 |
Repository | @Inject constructor | 자동 주입 |
DAO, DB | @Provides | 수동 등록 |
Fragment | @AndroidEntryPoint + by viewModels() | 자동 주입 |
'잡동사니 > GPT가 알려주는' 카테고리의 다른 글
[GPT가 알려주는] 안드로이드 앱 복제 및 프로젝트 이름 변경 (1) | 2025.06.02 |
---|---|
[GPT가 알려주는] Node란? (0) | 2025.06.02 |
[GPT가 알려주는] Matter의 Cluster와 Endpoint (0) | 2025.05.27 |
[GPT가 알려주는] 안드로이드 di, dao, model, repository (0) | 2025.05.20 |
[GPT가 알려주는] Beyond Compare로 비교하여 로컬/원격 소스 분리하기 (0) | 2025.05.20 |