기록

[GPT가 알려주는] DAO, Entity, Repository, Room, Impl, Viewmodel, Hilt 본문

잡동사니/GPT가 알려주는

[GPT가 알려주는] DAO, Entity, Repository, Room, Impl, Viewmodel, Hilt

heylo 2025. 6. 2. 11:49
DAO, Entity, Repository, ROOM, IMpl , Viewmodel 설명좀해줘
 

좋습니다! 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 ]

 

✅ 예: "기기 등록하기"의 흐름

  1. 사용자가 등록 버튼 클릭 → ViewModel의 addDevice() 호출
  2. ViewModel → Repository의 addDevice() 호출
  3. RepositoryImpl → DAO의 insertDevice() 실행
  4. DAO → Room이 SQLite에 insert 쿼리 실행
  5.  

필요하시면 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() 자동 주입