안녕하세요?
이번 숙제 타자 임정현입니다. ㅎㅅㅎ
사실 이 논의... 꽤 오래전에 했는데 이제 서야 쓰네요...핳....(죄송합니다..)

그럼 거두절미하고 바로 가시죠.
문제 상황
- 프로젝트 아키텍처를 무엇을 적용할 것인가?
- 특정 아키텍처가 아니어도 좋으니 Layer를 어떻게 나눌 것인지 논의
- Presentation Layer
- MVC vs MVP vs MVVM
- 전체 Architecture
- Clean vs Butterfly vs TCA vs RIBs vs VIPER
우리의 상황 / 배경
프로젝트 인원: 4명
배경지식: MVC, MVVM, Butterfly Architecture(aka. Clean Architecture)
프로젝트 목표 중: 테스트 가능해야 함 (Swift Testing 등 도입 목표)
문제 해결
MVVM을 결정한 이유
협업을 위한 디자인 패턴을 도입해야 했다.
MVC의 경우 작은 프로젝트에 적용하기 쉽고, 초반에 빠르게 기능 구현이 가능하다는 장점이 있다.
다만, 협업을 하기에는 뷰와 컨트롤러간의 의존성이 강했다.
강한 의존성은 뷰와 컨트롤러를 별도로 작성하기 힘들게 한다.
또한, 이러한 강한 의존성은 테스트하기 힘들고 이는 우리의 목표와 부합하지 않는다.
따라서, 이를 대체할 별도의 패턴이 요구되었다.
우리가 조사한 디자인 패턴 / 아키텍처들은 여러가지가 있었지만,(Clean, Butterfly, RIBs, MVP, MVVM 등)
새로운 아키텍처를 배우기에는 5주라는 시간에 기술적인 학습과 더불어 학습하기에는 힘들 것으로 판단했다.
이에 기존에 알고 있던, MVVM패턴을 활용하기로 했다.
그 이유는 다음과 같다.
- 가용성: 학습스프린트 기간동안 활용한 경험이 있어서 상대적으로 러닝커브가 적다.
- 확장성: 향후 클린 아키텍처등을 도입할 시에 적용할 수 있다.
- 적합성: 우리가 설정한 목표(협업 / 테스트가능) 달성에 용이하다.
⇒ 이를 통해 안정적인 협업과 프로젝트 목표를 달성하기 용이할 것으로 예상한다.
Repository Pattern을 적용한 이유
우리는 멀티미디어(사진, 비디오, 음성 등) 데이터를 로컬에 저장하려고 했다. 하지만, 디스크를 너무 많이 차지하는 등의 문제로 상황에 따라 데이터를 서버에 저장해야할 필요성도 느끼고 있다.
이러한 이유로, 우리는 프로젝트에서 Repository를 여러 개 사용할 계획이고 그 과정에서 다음과 같은 문제가 있었다.
- 유지보수 어려움: Repository를 여러 개 두고 코드를 작성하면, 데이터의 저장소가 변경되었을 때, Repository 자체를 수정해줘야하는 문제점이 있다.
- 테스트 어려움: Repository가 저장소에 종속되어 있어서 테스트를 저장소 별로 해줘야한다는 문제점이 있다.
그래서, 우리는 Repository 패턴을 적용해 두 문제를 해결하고자 했다.
- 유지보수 용이: Repository Protocol을 두어 Repository가 수정되었을 때, Repository를 사용하는 객체의 변화가 없도록 했다.
- 테스트 용이: Repository Protocol을 두어 Repository의 종류의 관계없이 테스트를 가능하도록 했다.
우리는 위와 같은 이유로 다양한 종류의 저장소에 접근해 통일된 형태로 데이터를 가져 올 수 있는 Repository Pattern을 적용했다.
Interface 모듈을 분리한 이유
위에서처럼 여러 모듈로 분리하는 등, 레이어를 나누다보니 의존성이라는 새로운 과제가 발생했다.
지금과 같은 상태로 모듈을 분리하는 것은 좋지만, 서로간의 의존성을 직접적으로 구성하면 다음과 같은 문제가 생긴다.
- 확장성: 추후 새로운 아키텍처(Clean Architecture 등)을 도입하기 힘들다.
- 테스트: 테스트를 위해서 Mock객체 등을 만들어야 하는데 강하게 의존하면 테스트용 객체 사용이 힘들다.
따라서, 중간에 Interface모듈을 별도로 생성하여 각각의 레이어에서 해당 Interface에 의존하도록 했다.
(대충 영현/효준 님의 사진)
위와 같이 구성하여 의존성을 분리할 수 있었고 추후 확장성 측면의 이점과 테스트 가능하다는 우리의 목표를 달성하기 용이할 것으로 보인다.
라고 생각했지만.. 방심은 금물..결국에는...
돌고 돌아 Clean Architecture가 된 이유
Repository만 나누니 비즈니스 로직이 어떤 계층에 들어가야할지 애매해졌다.
위와 같이 계층을 나누게되면 Data Layer에 비즈니스 로직이 들어가게 될텐데, 그럼 대략적으로 Data Layer 즉, Model 계층이 아래와 같이 분리될 것이다.

이렇게 분리할 시, 문제가 되는 지점은 각각의 Repository Implement 부분에 같은 비즈니스 로직이 중복해서 들어가게 된다는 점이다. (위에서 테스트를 위한 MockRepo는 빼더라도 Web, Store Repo에 같은 로직이 생긴다.)
이에 대한 해결방법으로 비즈니스 로직만을 담당하는 Repository를 하나 더 두는 방법이 있는데, 그렇게 하면 Repository 내부에서 상속관계가 생기는 괴랄한 형태가 된다..
// DataLayer
protocol BusinessLogicRepository { ... }
final class BusinessLogicRepositoryImpl: BusinessLogicRepository {
let dataRepo: DataRepository
init() { }
}
protocol DataRepository { ... }
final class WebRepositoryImpl: DataRepository { ... }
final class StoreRepositoryImpl: DataRepository { ... }
이렇게 이상한 형태의 아키텍처를 만드느니, Clean Architecture와 동일하게 Domain Layer를 하나 더 두고 Domain Layer는 비즈니스 로직을 담당하는 계층으로, Data Layer를 데이터를 로컬 혹은 서버로부터 가져오는 역할만을 하는 계층으로 확실하게 역할 분담을 해주는 것이 더 나은 형태의 아키텍처라는 생각이 들었다.
따라서 우리 Memorial House는 Clean Architecture와 MVVM 패턴이 결합된 형태의 아키텍처를 프로젝트에 적용하기로 했다.
결론

- 팀원 모두가 MVC에서 Controller가 View와 비즈니스 로직을 모두 처리하게 되어 코드가 비대해지는 문제를 경험했습니다. 이를 해결하고자 MVVM을 통해 View와 비즈니스 로직을 분리했습니다.
- 멀티미디어를 관리하는 초기 버전에서는 로컬 DB를 활용하고 있지만, 추후 서버 도입 가능성을 고려해 Repository Pattern을 통해 Data Layer를 도입했습니다.
- Presentation Layer가 Data Layer에 직접 의존하면 ViewModel 테스트가 어려워지고 서버 및 로컬 환경에 종속되기 때문에, 두 계층 간에 Repository Interface를 관리하는 모듈을 두기로 결정했습니다.
- 카테고리 및 즐겨찾기 필터링과 부가적인 비즈니스 로직은 ViewModel이 처리하게 되며, 이로 인해 ViewModel의 복잡도가 증가할 것을 예상하여 Domain Layer를 두어 Use Case에서 처리하도록 했습니다.
요약
- MVVM, Repository pattern을 적용한다.
- Presentation과 Data 모듈 사이에 Interface 모듈을 둔다.
→ 돌돌 Clean Architecture.. (돌고돌아 Clean..)
참조 링크
'Architecture, Design Pattern' 카테고리의 다른 글
DIContainer 도입 논의 과정 (0) | 2024.12.04 |
---|---|
MVVM 아키텍처 도입, 우리의 ViewModel 사용법 (1) | 2024.12.04 |