되는대로 개발하면 되는거 아니었냐며

김태영·2024년 7월 18일
0

TIL

목록 보기
53/70
post-thumbnail
post-custom-banner

오늘 공부한 것

- 알고리즘 호텔 대실 풀이
- 스탠다드반 과제를 위한 준비의 준비
- 앱개발 숙련 주차 특강 듣기

앱을 설계할 때 원칙이 있었다고?

나는 지금 응애 개발자라 규모가 매우 작은 앱들만 만들고 있지만, 나중에 실제로 앱을 출시하려면 규모가 점점 커지게 될 것이고 그에 따라 화면이나 코드가 굉장히 복잡해질 것이다. 사실 지금도 뇌 대신 감자가 존재하는 것 같은 내가 볼 땐 너무 복잡하긴 하다..

아무튼 코드들이 서로 얽혀있어 하나 수정하면 이거저거 다 수정해야하는 상황을 방지하기 위해 안드로이드에서는 앱 구성요소들간의 관심사 분리를 권장하고 있다. 이 원칙을 지키면 앱을 확장하거나 테스트하기 더 쉬워진다고 한다. 이를 위해서 각 앱 마다 최소한 다음 두 가지 레이어가 꼭 포함되어야 한다.

  • UI (Presentation) Layer: 화면에 데이터를 표시한다.
    • 뷰나 프래그먼트 등이 있다.
  • Data Layer: 비즈니스 로직을 포함하며 앱의 나머지 부분에 데이터를 노출시킨다.
    • 비즈니스 로직은 데이터를 끌어오거나 리스트의 값을 가져오는 등의 함수들을 말한다.
    • 0개 이상의 Data Source들과 Repository들로 구성되어 있다.

오늘 알아볼 Repository Pattern은 이 Data Layer를 앱의 나머지 부분에서 분리하는 패턴이다!

⭐ Domain Layer

복잡한 비즈니스 로직이나 여러 뷰모델에서 재사용되는 간단한 비즈니스 로직의 캡슐화를 담당한다. → 필요한 경우에만 사용하자!

Repository의 역할

다른 레이어와 Data Source 사이를 중재하며 Data Source를 캡슐화하는 역할을 한다.

⭐ 캡슐화

객체 지향의 특징 중 하나이다. 데이터를 외부로부터 보호하고, 내부 동작은 감추고 외부에는 필요한 부분만 노출하는 특징을 말한다.

Repository Pattern을 사용하면…

✅ 추상화된 데이터 접근

Repository는 Data Source(ex. Local Data(Room, SharedPreference, DataStore), File, Network, …)에 접근하는 로직을 캡슐화한다.

데이터 접근 로직을 분리해서 한 곳에 모아두기 때문에 코드 변경 시 영향을 최소화할 수 있다.

앱의 나머지 부분은 이 추상화된 인터페이스를 통해 데이터를 요청하고 사용할 수 있다.

✅ 일관된 데이터 인터페이스

데이터의 출처와 관계없이 일관된 방식으로 데이터에 접근하고 조작할 수 있다.

예를 들어 로컬 데이터에서 서버 데이터로 변경되더라도 앱의 다른 부분에는 영향이 가지 않는다.

즉, 데이터의 출처를 모르고 있어도 Repository가 제공해주는 데이터를 냅다 사용하기만 하면 된다.

✅ 나이스한 테스트

테스트할 때 Mock Repository를 사용해 Data Source를 쉽게 모킹할 수 있다.

테스트는 아직 해보지 않아서 모르겠지만, 실제 데이터와 가짜 데이터가 사용하는 함수들이 추상화 되어 있다면 쉽게 데이터를 바꿔가며 테스트 할 수 있다는 말이 아닐까?

✅ 유연성

Data Source가 변경되더라도 Repository 인터페이스만 유지되면 다른 부분의 코드를 수정하지 않고도 쉽게 데이터를 교체할 수 있다.

구현을 해보자

1. 데이터 소스

싱글톤으로 데이터를 만들어놓자. 싱글톤을 구현하는 방법에는 여러가지가 있다고 한다.

// 보통 어쩌구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()
	}
}

2. Repository 인터페이스

다음으로는 데이터 접근 로직을 추상화해야 한다.

interface MyRepository {
	fun getMyDataList(): List<MyData>
}

3. Repository 구현체

인터페이스를 구현한다. 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()
    }
}

4. 사용하자

val myRepository = MyRepositoryImpl(MyDataSource)
val myDataList: List<MyData> = myRepository.getMyDataList()

마치며

사실 실행 결과는.. 크게 다르지 않음.. 필요성을 느낄 때는 아마 내가 개쩌는 안드로이드 인간이 되었을 때가 아닐까 싶다. 일단 프래그먼트부터 익숙해지고 화이팅해보자고 ^^~

profile
화이팅
post-custom-banner

0개의 댓글