Repository Pattern 이란?

쓰리원·2023년 1월 14일
1
post-thumbnail

들어가기 전에 아래의 사전지식이 없으면 이해에 문제가 있을 수 있습니다.

선수 지식

1. 객체 지향 프로그래밍
2. 개념-의존성-주입-DI-Dependency-Injection
3. 안드로이드 AAC ViewModel이란?

1. Repository Pattern 이란?

Repository Pattern 은 데이터 레이어를 앱의 나머지 부분에서 분리하는 디자인 패턴입니다. 데이터 레이어는 UI와는 별도로 앱의 데이터와 비즈니스 로직을 처리하는 앱 레이어 부분 이며, 다른 레이어에서 데이터 레이어에서 제공하는 API를 통해서 이 데이터에 액세스 할 수 있습니다. UI가 사용자에게 정보를 제공하는 동안 데이터 레이어에는 네트워킹 코드, Room 데이터베이스, 데이터 관련 오류 및 예외 처리, 데이터를 읽거나 조작하는 코드 등이 포함됩니다.

아래 그림은 공식문서의 그림으로 Activity와 Fragment와 같은 앱 구성요소가 ViewModel로 Repositories를 통해 데이터 소스에 접근하는 계층을 보여줍니다.

안드로이드 공식문서의 위 그림을 보면, 데이터가 있는 여러 저장소(Local, Remote)를 필요한 공통 특성들을 추상화하여 중앙 집중처리 방식을 구성합니다. 즉, Data의 출처에 관계 없이 동일한 인터페이스로 데이터에 접근할 수 있도록 하는 패턴입니다. 그래서 Repositories는 데이터 소스(ex: 로컬 데이터, 웹 서비스 API로 부터 데이터, 캐시 데이터) 간의 충돌을 해결하고 이 데이터의 변경사항을 중앙 집중화할 수 있습니다. 그리고 DataSource들 Local 이면 (Room, SQlite), Remote면 서버와 api 통신하는 특정한 목적들을 알 수 없이 우리는 필요한 데이터만 요청하게 되므로, 캡슐화 되게 됩니다.

2. Repository Pattern 사용의 이점 이란?

Repository 모듈은 데이터 작업을 처리하고 여러 백엔드 API 사용을 한곳에서 가능하게 합니다. 일반적인 앱에서 저장소[Repository 가 아니라도 data 계층에 접근하는 것들]는 네트워크에서 데이터를 가져올지 아니면 로컬 데이터베이스에 캐시된 결과를 사용할지 결정하는 로직을 구현합니다. MVVM의 경우 뷰 모델이 위와 같은 역할을 하게 됩니다.

  1. MVVM 에서 Repository 와 DI 라이브러리를 같이 사용하게 된다면 뷰 모델에서 Data 관련한 구현 세부정보를 교체할 수 있습니다. 이는 코드를 모듈식으로, 테스트를 가능하게 만들 수 있습니다. 쉽게 Repository에 Test 코드를 작성해서 Data를 활용하는 코드의 나머지 부분을 테스트할 수 있습니다.
  1. Repository는 앱 데이터의 특정 부분에 관한 단일 정보 소스 역할을 해야 합니다. 네트워크 리소스와 오프라인 캐시 등 여러 데이터 소스로 작업할 때 Repository 는 앱이 오프라인 상태일 때도 받아놓은 데이터를 사용을 할 수 있습니다. 캐싱의 경우 Room 라이브러리를 통해서 저장했다가 꺼내서 쓰기도 합니다.

3. Repository Pattern 구현 (with hilt)

개발 해서 product에 적용했던 코드를 바탕으로 예제를 작성하겠습니다.

1. RestaurantRepository interface와 구현체인 RestaurantRepositoryImpl를 만들기.

interface RestaurantRepository 

@Singleton
class RestaurantRepositoryImpl @Inject constructor(
    private val restaurantDataSource : RestaurantDataSource,
    private val mapDataSource: MapDataSource
) : RestaurantRepository {

여기서 RestaurantDataSource 와 MapDataSource 가 DI 되고 있는 것을 확인 할 수 있습니다. DI가 잘 될 수 있게 hilt 적용을 어떻게 하는지 알아보겠습니다.

2. hilt 적용하기

@Module
@InstallIn(SingletonComponent::class)
abstract class RepositoryBindModule {
    @Binds
    abstract fun bindFoodApiRepository(
        restaurantRepositoryImpl: RestaurantRepositoryImpl
    ): RestaurantRepository
}
  • 1. 의존성 주입 구성

@Module 어노테이션을 사용하여 Hilt 모듈을 만들고, @InstallIn 어노테이션을 사용하여 해당 모듈을 구성할 구성 요소를 지정합니다. 이렇게 모듈을 구성하면 의존성 주입을 구성할 수 있습니다.

  • 2. 앱 전역에서 사용 가능한 의존성 주입

SingletonComponent::class는 앱의 전역 컴포넌트 중 하나입니다. 이렇게 구성하면 모듈이 앱 전반에 걸쳐 사용 가능합니다. 모듈이 SingletonComponent::class에 구성되어 있으면 앱에서 필요로 하는 어느 곳에서나 해당 모듈의 의존성 주입을 사용할 수 있습니다.

위의 코드 처럼 추상클래스로 모듈을 만들어주고, 생성자에 RestaurantRepositoryImpl를 파라미터로 받고 RestaurantRepository로 반환을 해줍니다. 이렇게 hilt 모듈을 설정하게 되면 @Inject 를 해서 DI를 한 DataSource 클래스가 뷰모델 인스턴스 생성시 생성자 주입이 되게 됩니다.

3. ViewModel에 Repository 를 DI 하기.

@HiltViewModel
class MapViewModel @Inject constructor(
	private val repository: RestaurantRepository
) : ViewModel() {

RestaurantRepositoryImpl 이 @Binds가 되어서, MapViewModel에 제공되게 됩니다.

4. 위와 같은 ViewModel 을 쓰는 View에서 Instance 생성하기.

@AndroidEntryPoint
class MapFragment : BaseFragment<FragmentMapBinding>() {

	private val viewModel: MapViewModel by viewModels()
}

MapViewModel 인스턴스는 viewModel 호출이 되면 그 때 인스턴스가 초기화 되게 됩니다. 초기화 된 뷰모델 인스턴스는 DI 작업이 이루어진 레포지토리가 주입된 상태로 사용할 수 있게 됩니다.

4. Repository Pattern과 caching

추후에 관련 코드와 함께 작성 예정 입니다.

5. reference

https://developer.android.com/codelabs/basic-android-kotlin-training-repository-pattern?hl=ko#3
https://developer.android.com/jetpack/guide/data-layer?hl=ko

profile
가장 아름다운 정답은 서로의 협업안에 있다.

0개의 댓글