본문 바로가기

클린아키텍쳐

[클린아키텍쳐] BestPractice로 공부하는 클린아키텍쳐 2편: Data

 

안녕하세요 주니오스입니다~! ✋

 

Domain편에 이어서 이번에는 Data영역을 분석해 보겠습니다.

 

지난 포스팅을 못보신 분들은 1편 Domain을 먼저 보시는 것을 추천합니다~!

 

 

우선 데이터 영은 다음과 같이 구성되어 있습니다.

Data layer

Repositories

아래코드를 보시면, 지난번에 Domain레이어에서 확인할 수 있었던 레포지토리 인터페이스가 사용되는 것을 확인할 수 있습니다.

 

Repositories/DefaultMovieRepository

 

레포지토리 인터페이스에 따라 구현된 매서드가 어떻게 정의되어 있는지 확인해 봅시다.

 

fetchMovieList매서드

 

구분을 쉽게하기위해 주석으로 번호를 마킹하였습니다.

 

1번

 

1번을 보시면 Domain레이어로부터 전달받은 엔티티를 DTO로 변환하는 것을 확인할 수 있습니다.

 

2번

2번의 Task는 레포지토리에서 진행중인 작업의 상태를 표시하기위한 객체로 보입니다.

 

RepositoryTask

 

이런 식으로 레포지토리에서 진행되는 작업의 상태(취소됬는 가?)를 저정하고 있습니다.

2번과 3번사이

 

2번과 3번사이에 cache라는 변수의 매서드를 호출하는 것을 확인할 수 있습니다.

 

cache는 첫번째 사진에서 MoviesResponseStorage타입임을 확인할 수 있는데요.

 

해당 타입은 프로토콜로 추상화되어 있습니다. 

 

어떤식으로 구성되어 있는지 알기위해 객체가 주입되는 곳으로 이동해 보았습니다.

 

 

내 바로 코어데이터 저장소가 주입되는 코드를 찾을 수 있었습니다.

 

내부적으로 getResponse매서드가 어떤 동작을 수행하는 지 알아보니

 

요청 정보가 특정 저장소에 이미 저장되어 있는지 확인하는 매서드였습니다.

 

이 경우엔 코어데이터에 확인하는 과정이라고 해석할 수 있습니다.

 

레포지터리도 특정 동작 "이전에 요청됬던 쿼리인지 확인" 에 대해 저장소를 추상화하고 있내요, 흥미롭습니다.

 

 

UseCase: 검색어로 부터 영화리스트 가져와!, 검색기록은 따로 저장해

 

영화 레포지토리: 캐싱된 데이터 확인 후(저장소 추상화), 새로운 데이터 요청

 

영화 쿼리 레포지토리: 검색어를 저장(저장소 추상화)

 

 

이렇게 기능을 바탕으로 각코드를 분류하니 깔끔하고 코드의 위치를 파악하기가 쉽다고 느껴졌습니다.

 

언제 어디를 고쳐야하는지 파악하기 쉬운것 같습니다.

 

3번을 살펴보겠습니다.

 

responseDTO는 result가 success인 경우 전달받은 캐싱된 데이터 입니다.

 

데이터가 이미 있는 쿼리인 경우를 의미하는데요.

 

cached라는 함수의 인수로 전달되는 것을 확인할 수 있습니다.

 

cached는 무엇일까요? 🤔

 

코드를 밟고 올라가보니 결국 Presentation영역까지 닿았는데요.

Presentation레이어의 ViewModel의코드

 

위코드를 보시면 page라는 것을 전달받아 appendPage매서드 호출시 인수로 사용합니다.

 

 appendPage는 옵저버블을 업데이트 하는 매서드입니다.

 

UI에 직접적으로 사용되는 데이터가 Data영역에서 부터 업데이트되는 것을 확인할 수 있습니다.

 

해당 프로젝트에서는 Rx의 옵저버블을 사용하는 것이 아닌 자체 옵저버블을 사용하는데요.

 

이후에 있을 Presentation영역 분석을 통해 자세하게 알아보도록 하겠습니다.

 

 

다음으로는 4번을 보겠습니다.

 

4번

 

APIEndPoint라는 타입으로부터 리소스의 엔드포인트(종착점)를 생성하는 것을 볼 수 있습니다.

 

아래코드는 엔드포인트 코드인데요, 엔드포인트 도달에 필요한 모든 리소스가 준비되어 있습니다.

EndPoint

 

이렇게 만들고 전달받은 값들을 조합하여 URL, URLRequest를 만들어 냅니다.

 

BaseEndpoint -> ResponseRequestable -> Requestable

 

마지막 5번입니다.

 

생성한 엔드포인트와 함께,

 

5번은 DataTransferService로 추상화된 객체를 사용하여 엔드포인트의 리소스를 가져옵니다.

 

해당작업은 앞서 언급했었던 래포지토리테스크에 저장합니다.

 

네트워크 작업이 성공하면

5번

 

데이터를 cache(코어데이터 저장소)에 저장하는 것을 확인할 수 있습니다.

 

그 후 Domain으로부터 전달받은 컴플션핸들러에 데이터를 전달하는 것을 확인할 수 있습니다.

 

주목할점은 DTO가 toDomain매서드를 통해 엔티티로 다시 변경되는 것입니다! 

 

toDomain

 

Network

다음으로 네트워크 폴더를 살펴보겠습니다.

 

해당 디렉토리니 내부에는 앞장에서 보여드린 APIEndpoint타입이 정의되어 있습니다.

 

그리고 다양한 DTO들이 존재했는데요,

 

주목할점은 모든 DTO가 toDomain이라는 매서드를 가져 내부적으로 사용될 준비를 하고 있습니다.

 

DTO들

 

DTO는 Data레이어에 위치한다 정도를 확인할 수 있었습니다.

 

PersistentStorages

레포지토리에서 사용한 인터페이스들과 구체타입이 선어되어 있습니다.

 

주목할 점은 폴더구조였습니다.

PresistentStorage 디렉토리 구조

 

목적에 따라 같은 저장소를 사용하여도 각기다른 class로 구현되어 있는 것을 확인할 수 있었습니다.

(폴더마다 각기다른 DTO가 존재)

 

기존의 로컬데이터를 사용하는 경우는 이처럼 기능에따라 각기다른 폴더구조를 가지기보단,

 

하나의 코어데이터 스토리지타입에 다때려밖는 구조로 구현을 하였습니다.

 

해당 앱의 경우 여러 class가 CoreDataStorage타입을 공유하기만 하도록 하는 구현방법을 사용했습니다.

CoreDataStorage

 

레포지토리에 주입되는 Storage구체타입

 

레포지토리에 주입되는 Storage구체타입

 

회고

Data레이어를 뜯어보며 느낀점은 아래와 같습니다.

1. 레포지토리는 저장소 추상화를 통해, 니즈에 맞게 특정 저장소를 주입받을 수 있다.
2. UseCase에서는 레포지토리간 상화작용만 담당한다. (UseCase는 레포지토리만 추상화함)
3. UI업데이트와 관련된 로직과, 데이터 저장소와 관련된 로직이 별개로 동작함을 확인할 있고 효율적이라고 생각된다.

 

감사합니다~!