View

300x250

“나, 클린 아키텍처를 완벽히 이해했어!” 라는 말을 여기저기에 하고 다녔는데, 다들 막 이것 저것 태클을 걸고싶어서 “그 부분은 좀 있다가 한 번 이야기해보죠” 라는 말을 자주 많이 들었다. ㅋㅋㅋㅋ 아무래도 클린 아키텍처 자체가 넓은 방법론 중 하나이기도 하고 한 번씩은 적용하기를 시도해보니까, 다들 해석하는 방법도 다양하고, 적용하는 데에 있어서 거친 고난과 역경을 겪으며 각자가 스스로 깨달은 바가 많아서 그런게 아닌가 싶다.

 

개념적으로 클린 아키텍처를 통해서 하고싶은게 뭔지는 다 캐치를 했는데, 이걸 코드로 작성하려고 하니 또 다른 난관에 부딪히고 있다. 플러터에서 상태관리를 위해 사용하는 Riverpod 프레임워크에 생각보다 제약이 많았고, 이걸 활용해서 내가 만들고자 하는 클린 아키텍처 스러운 구조를 짤 수 있을지 의심스러웠다. 그치만, 이걸 적용하기 위해서 고민하고 가다듬는 부분에서 또 배우는게 많으니 한 번 죠져보기로 결졍했다. ㅎㅎ


고민의 시작

또 다시 가져왔다. 내가 이해한 클린 아키텍처.

Untitled.png

[링크] 지난 번에 썼던 글에 있는 내용을 간추려서 적으면 이렇게 될 것이다.

  • Domain Layer는 다른 레이어를 모르게 하라!
    • 그저 이런 Usecase가 호출되면 Repository에서 이런 데이터를 받아와 처리하게 만들기만 하면 된다.
  • Presentation Layer는 Domain Layer와 UseCase를 통해서만 접근 가능하다. (API를 호출하듯이)
    • Presentation Layer가 PC든 모바일이든 웹이든 상관없이 API로 로직에 접근할 수 있으니, 플랫폼이나 프레임워크에 상관 없이 프론트를 구현할 수 있어야 한다.
  • Data Layer는 Domain Layer의 Repository의 구현을 담고있다.
    • Data Layer는 데이터의 출처가 Local이든 Server이든 NAS이든 아무런 상관 없이 Domain Layer의 API에 따라 원하는 형식으로 데이터를 넘기기만 하면 된다.

다시 말해서 Presentor는 UseCase를 통해서 Domain Layer와만 연결될 수 있고, Data Layer는 Repository Implementation의 구현을 통해서만 Domain Layer와 연결될 수 있어야 한다.

 

그런데 이번에 클린 아키텍처의 폴더구조와 관련된 유튜브 영상에 있는 샘플코드를 보면서, 나의 생각이랑 너무 다른 코드를 봤다.

https://www.youtube.com/watch?v=SmJB8cy8emU < 이 영상이였다.

Untitled.png

Presentation Layer에 있는 Provider 코드가 Data Layer에 있는 RepositoryImpl의 존재를 알고있는게 아닌가? 심지어 바로 아래에 있는 failureOrPokemon이라는 변수의 타입은 Data Layer에서 사용되는 Entity 모델이였다. ??? 왜지 왜지?

 

Untitled.png

대충 이런 느낌.

클린 아키텍처를 짜는 이유가 Domain Layer, Present Layer, Data Layer를 최대한 분리시켜서 의존성을 제거하고자 함이라고 생각했는데, 이런 방식으로 코드를 작성하면 다시 프론트와 백 영역 간에 강한 의존성이 생겨버려서 유지보수가 힘들게 될 것 같았다.

 

이렇게 짜는게 아닌 것 같아서 친구에게 물어보니, 친구의 친구가 말하기를 “실질적으로 클린 아키텍처를 지키면서 개발하는데에 코스트가 많이 드니, Entity 모델을 그대로 가져와 재사용하기도 하고 어느정도의 의존성이 발생하는건 허용하면서 개발을 진행한다”고 하더라. 흠… 맞는 말이지. 그치만 이왕 클린 아키텍처를 적용하는 걸 이번 프로젝트에서 공부하기로 했으니, 시간이 들더라도 조금 더 철저하게 구조를 지키면서 개발을 진행하여, 아키텍처의 장점을 한 번 느껴보고 싶었다.

 

레이어 간의 분리에 대한 고민

현재 프로젝트에서는 Riverpod 프레임워크를 사용하고 있다. 이 친구도 할 말은 많지만, 아끼도록 하겠다. ㅎㅎ 스위프트가 얼마나 그리운지 모르겠다. 😢 Riverpod를 활용하면 위젯 트리 구조에 상관없이 어디에서나 원하는 데이터에 접근할 수 있게 해주고, 데이터가 변경되었을 때 이를 알려 화면을 다시 그리는 등의 반응을 할 수 있게 해준다.

 

처음으로 플러터 프로젝트에 클린 아키텍처를 기반으로 코드를 집어넣으면서 Riverpod을 사용하는 예제 코드를 봤는데, 거의 모든게 Provider를 통해 전달되고 있었다. 이거를 그림으로 그리면 이렇게 생겼다.

Untitled.png

 

흠 좋아. 이런식으로 메서드를 호출하고 데이터를 주고받는 구조로 Provider를 작성해주면 되겠군! 이라고 생각했지만, 생각치 못한 곳에서 또 고민거리를 마주쳤다.

Untitled.png

 

요런 녀석들이 있는데, 왜 authRepositoryProvider에서 remoteDataSource에 대해서 알아야하지?? 라는 고민이 생겼다. 물론 코드상으로는 ReposityroImpl 클래스의 객체를 생성할 때 이 상태를 인자로 전달하는게 자연스럽기도 하고, 결국 “구조가 아닌 Provider이니까” 괜찮지 않을까? 라고 생각이 들기는 했다. 그러나! 그럼에도! 클린 아키텍처스럽게 코드를 작성하려면 RepositoryRepositoryImpl와만 connection이 있어야 하고, 이걸 분리해야 한다고 생각했다.

 

Repo의 Provider에서 Init 시점에 DataSourceProvider를 알아야하니깐 지금은 이런 상태이고

Untitled.png

 

요걸 이런식으로 개선해서, Repo의 Provider가 DataSourceProvider를 모르도록 만들고자 하였다.

Untitled.png

 

기존의 코드는 이런 방식이였다. AuthRepositoryImpl 클래스에서는 AuthRemoteDataSource 를 필요로 하고(이건 당연함) 요 녀석을 생성자의 인자를 통해 넣어주고 있었고, 이 때문에 Domain Layer인 authRepositoryProvider에서 AuthRepositoryImpl을 생성할 때 DatasourceProvider에 접근해 Datasource를 인자로 넣어주어야 했다. Domain Layer에서 Datasource의 형태를 알아야 한다는게 너무나도 어색했다.

Untitled.pngUntitled.png

 

그래서, 아래 코드처럼 변경해줬다. AuthRepositoryProviderAuthRepositoryImpl을 전달한다. 이때 ProviderRef ref를 인자로 전달해서, AuthRepositoryImpl에서 DatasourceProvider를 찾을 수 있게 만들었다.

Untitled.pngUntitled.png

 

이렇게 코드를 적용하면, Data Layer인 AuthRemoteDataSourceProvider 측에서 코드를 수정하더라도 Domain Layer에는 아무런 영향이 없으니, 의존성을 거의 제거한 코드라고 볼 수 있을 것 같다.

 

내가 생각할 때는 코드가 훨씬 분리되고 깔끔해진 것 같은데, 이런 방식으로 코드를 작성하다가 문제가 발생하면 다시 글을 남겨야겠다. 후.

 


개인적으로 생각할 때는, 아직도 분리가 좀 더 가능하다고 생각이 들긴 한다. 기술적으로 가능할 지는 모르겠지만, 여전히 Domain Layer에 있는 Provider가 Impl 클래스의 존재를 알고있다는게 좀 거슬리기는 하지만, 여기에 어떻게 Impl 클래스를 전달해줄 수 있을지는 고민해봐야 할 것 같다.

Untitled.png

혹시나 찾게되면 또 글을 한 번 남겨야겠다. (찡-긋)

320x100
Share Link
reply
반응형
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31