✨ 오늘 공부한 것
- 알고리즘 호텔 대실 풀이 - 스탠다드반 과제를 위한 준비의 준비 - 앱개발 숙련 주차 특강 듣기
나는 지금 응애 개발자라 규모가 매우 작은 앱들만 만들고 있지만, 나중에 실제로 앱을 출시하려면 규모가 점점 커지게 될 것이고 그에 따라 화면이나 코드가 굉장히 복잡해질 것이다. 사실 지금도 뇌 대신 감자가 존재하는 것 같은 내가 볼 땐 너무 복잡하긴 하다..
아무튼 코드들이 서로 얽혀있어 하나 수정하면 이거저거 다 수정해야하는 상황을 방지하기 위해 안드로이드에서는 앱 구성요소들간의 관심사 분리를 권장하고 있다. 이 원칙을 지키면 앱을 확장하거나 테스트하기 더 쉬워진다고 한다. 이를 위해서 각 앱 마다 최소한 다음 두 가지 레이어가 꼭 포함되어야 한다.
오늘 알아볼 Repository Pattern은 이 Data Layer를 앱의 나머지 부분에서 분리하는 패턴이다!
⭐ Domain Layer
복잡한 비즈니스 로직이나 여러 뷰모델에서 재사용되는 간단한 비즈니스 로직의 캡슐화를 담당한다. → 필요한 경우에만 사용하자!
다른 레이어와 Data Source 사이를 중재하며 Data Source를 캡슐화하는 역할을 한다.
⭐ 캡슐화
객체 지향의 특징 중 하나이다. 데이터를 외부로부터 보호하고, 내부 동작은 감추고 외부에는 필요한 부분만 노출하는 특징을 말한다.
Repository는 Data Source(ex. Local Data(Room, SharedPreference, DataStore), File, Network, …)에 접근하는 로직을 캡슐화한다.
데이터 접근 로직을 분리해서 한 곳에 모아두기 때문에 코드 변경 시 영향을 최소화할 수 있다.
앱의 나머지 부분은 이 추상화된 인터페이스를 통해 데이터를 요청하고 사용할 수 있다.
데이터의 출처와 관계없이 일관된 방식으로 데이터에 접근하고 조작할 수 있다.
예를 들어 로컬 데이터에서 서버 데이터로 변경되더라도 앱의 다른 부분에는 영향이 가지 않는다.
즉, 데이터의 출처를 모르고 있어도 Repository가 제공해주는 데이터를 냅다 사용하기만 하면 된다.
테스트할 때 Mock Repository를 사용해 Data Source를 쉽게 모킹할 수 있다.
테스트는 아직 해보지 않아서 모르겠지만, 실제 데이터와 가짜 데이터가 사용하는 함수들이 추상화 되어 있다면 쉽게 데이터를 바꿔가며 테스트 할 수 있다는 말이 아닐까?
Data Source가 변경되더라도 Repository 인터페이스만 유지되면 다른 부분의 코드를 수정하지 않고도 쉽게 데이터를 교체할 수 있다.
싱글톤으로 데이터를 만들어놓자. 싱글톤을 구현하는 방법에는 여러가지가 있다고 한다.
// 보통 어쩌구Entity로 이름을 짓는다고 한다.
// 여기서는 그냥 MyData로...
data class MyData(val name: String)
// 데이터를 생성해 반환하는 함수
fun myDataList(): List<MyData> {
return listOf(MyData("mimi"), MyData("jiji"))
}
// 코틀린은 object를 사용해 쉽고 간단하게 싱글톤을 구현할 수 있다.
object MyDataSource {
fun getMyDataList(): List<MyData> {
return myDataList()
}
}
다음으로는 데이터 접근 로직을 추상화해야 한다.
interface MyRepository {
fun getMyDataList(): List<MyData>
}
인터페이스를 구현한다. Repository를 통해 Data Source를 캡슐화해주었다! myDataSource
가 어디에서 어떻게 데이터를 가져오든 MyRepositoryImpl은 신경 쓸 필요 없이 getMyDataList
를 통해 데이터를 가져와 쓸 수 있다.
// DataSource를 파라미터로 받아,
// view에다가 myDataSource()를 의존성 주입해줄 것
// 보통 어쩌구Impl로 이름을 짓는다.
class MyRepositoryImpl(private val myDataSource: MyDataSource): MyRepository {
override fun getMyDataList(): List<UserEntity> {
// 1번에서 만들어둔 object의 데이터 반환 함수
return myDataSource.getMyDataList()
}
}
val myRepository = MyRepositoryImpl(MyDataSource)
val myDataList: List<MyData> = myRepository.getMyDataList()
사실 실행 결과는.. 크게 다르지 않음.. 필요성을 느낄 때는 아마 내가 개쩌는 안드로이드 인간이 되었을 때가 아닐까 싶다. 일단 프래그먼트부터 익숙해지고 화이팅해보자고 ^^~