CleanArchitecture 와 모듈화 적용 회고
이 글은 제가 혼자서 학습한 이후에 느낀 점들에 대한 회고 글입니다. 전문적이거나 공신력이 있는 기관, 인물로부터 학습한 것이 아니기 때문에 잘못된 내용이 포함 되어져 있을 수 있습니다. 혹시나 이 글을 읽으시는 분이 계시다면 다양한 의견을 코멘트로 남겨주시면 감사하겠습니다.
헬스를 좋아하는 나는 어느 순간부터 운동에 정체기가 왔다는 생각이 들었다. 내 운동을 기록하고, 다음 운동에 이전 운동 기록을 참고해서 이전보다 더 강한 부하를 근육에 걸어주고 있는지를 확인해야 한다고 생각했다. 그래서 운동 기록 앱을 개인 프로젝트로 만들어 보기로 했다. 프로젝트는 잘 진행되었고 현재는 AppStore에서 설치 및 이용이 가능하다.
앱에 대한 간단한 소개
기존 구조
Model
VO, DTO, DAO 구분 없이 앱에서 사용할 모든 구조체들의 집합
Ex)
- Exercise, User, History…
Repository
데이터 저장소
Ex)
- ExerciseRepository, HistoryRepository, UserRepository
Store
전역 상태 저장소. 앱 전역에서 store를 바라보고 상태값을 구독 할 수 있다.
Ex)
- UserStore, HistoryStore, ExerciseStore
Subscriber
특정 이벤트 구독을 위한 싱글톤 객체.
SwiftUI 로 처음 프로젝트를 시작하면서 어떻게 구현하면 좋을지 아직도 헷갈리는 부분이 있다. 한 화면에서 다른 화면으로 이벤트를 전달하는 것.
SwiftUI 에서는 UIKit 과는 다르게 각 View 들의 참조값을 참조하면서 개발 할 수 없다는 특징이 있다. 때문에 화면간 특정 이벤트 위임이 더 어렵다고 느꼈다. Delegate Pattern 을 활용하기 어렵다는 구조라고 생각했다. 그래서 이벤트를 Subject 로 방출시키고 다른 화면들끼리 이벤트를 방출/구독 할 수 있도록 Subscriber 라는 개념을 만들어서 사용했다.
Util, View
설명 불필요
회고
계층 분리가 안되어져 있다. 모든 파일에서 서로 모두 참조가 가능하였다. 어떤 파일을 수정하여도 기술적으로 앱 전체에 영향을 끼칠 수 있는 구조이다. 때문에 유지 보수성이 나쁘다고 볼 수 있다.
하나의 계층으로 이루어져 있기 때문에 앱 전체의 윤곽을 파악하기는 쉽다. 작업 속도가 비교적 빠르다.
Clean Architecture 적용
크게 앱을 3개의 계층으로 나누어서 개발하는 소프트웨어 개발 철학이다.
Domain
앱의 코어 비즈니스 로직을 담고 있는 계층. 추상화 Repository를 들고 있고 이를 통해 Data 에 접근가능하다. UseCase 에서 비즈니스 로직을 구현한다.
다른 계층에 대해서는 알지 못하게 하여 다른 계층의 변화로부터 완벽하게 격리시키는 것이 핵심이다.
Data
실제 서버나 로컬디비에 접근하여 데이터를 추가, 삭제, 업데이트, 호출해준다.
Domain의 Repository를 실제로 구현해주는 계층이다. 여기서의 모델은 DTO, DAO 역할을 한다.
Presentation
UI를 구현하고, 유저의 인터랙션과 상호작용하는 계층이다. 필요시 Domain 계층의 UseCase를 이용하여 비즈니스 로직을 실행시킨다.
회고
적용 시키는 과정이 재미있었다. 코드 작업시 내가 어느 계층에서 작업하고 있는지를 인지하고 작업을 할 수 있게 되어서 좋았다.
Domain 영역에서 작업 시 -> 앱 전체에 영향을 끼칠 수 있는 작업 중이다. 알 필요가 없는 내용이 코드에 들어가지 않도록 집중하고 앱의 코어 비즈니스 로직에만 집중해서 작업하자.
Data 영역에서 작업 시 -> 데이터 관리에만 집중하자.
Presentation 영역에서 작업 시 -> View 작업과 사용자의 상호작용에만 집중하자.
SOLID 원칙이 실제로 적용되어지는 것을 직접 확인 할 수 있었다. 이전엔 ViewModel 에서 Repository 를 주입받고, ViewModel 에서 비즈니스 로직을 수행하고, Repository 에 접근하여 데이터를 관리해주는 방식이었다. 이때 ViewModel에서는 Repository 에 선언된 모든 함수들에 대해서 직접적으로 접근이 가능하다. 이는 인터페이스 분리 원칙(ISP, interface segregation principle) 을 위배하는 일이다. 이를 UseCase 라는 개념이 추가되면서 자연스럽게 ISP 를 준수하면서 코드를 짤 수 있게 되었다.
의존성 개념이 비교적 구체화 될 수 있어서 좋았다. 의존성은 생기면 안좋은 것이다. 라는 인식이 있었었다. 하지만 Clean Architecture를 적용하고 나서 의존성은 무조건 안좋은 것이 아니라(실제로 의존성이 아예 없는 프로그램을 설계하는 것은 기술적으로도 불가하다) 방향성을 똑바로 잘 잡아주는 것이 중요하다. 라는 배움이 있었다.
개발자들의 용어에 조금 더 익숙해진 느낌을 받았다. 간혹 다른 개발자분들과 대화를 할 때면 용어에 대한 개념이 익숙하지 않아서 대화를 잘 따라가지 못한다는 느낌을 많이 받았었다. Clean Architecture 는 범용적인 아키텍쳐이기 때문에 많은 상황에서 Clean Architecture 의 용어나 개념을 들어서 설명할 때가 많다. 대화를 이해하거나, 내 개념을 전달시킬때에 이전보다 성장했다는 느낌을 받았다.
Modular Architecture 적용
Tuist를 학습하고 이를 프로젝트에 적용시켰다. Domain, Data, Presentation 계층으로 크게 3개의 모듈로 앱을 분리하였고, Presentation 내부에서도 화면들에 따라 History, Exercise, My, Friend, Common 등 세부적으로 모듈화 시켜주었다.
회고
모듈화 이전에 클린 아키텍쳐를 미리 학습하고 앱에 적용 시켜두었다는점이 다행이었다는 생각이 들었다.
증분 빌드와 기술적인 의존성 관리가 모듈화 작업에서 핵심 키워드인 것 같았다.
실제로 빌드 단위를 나누어서 의존성을 내 머리 안에서 개념적으로만 관리해주는 것이 아니라 기술적으로 나누어준다. 의존성에 대해서 확실히 이해도가 높아진다. 특정 모듈을 작업하게 되면, 기술적으로 어느어느 모듈까지 영향을 끼칠 수 있는지에 대해서 정확하게 판단이 가능하다.
빌드 속도가 대폭 향상되어진다. 캐싱 없이 앱 전체에 대한 빌드속도만 카운트하게 된다면 비슷할수도 있다. 하지만, 실제 앱을 개발할 때에 모듈 단위로 빌드를 나누어서 개발을 하게 되면서 앱 전체를 빌드하는 횟수 자체가 많이 줄어든다. Preview 활용도가 높아진다.
테스트 코드 작성이 더 수월해진다.