본문 바로가기

iOS공통

[iOS] 케어밋 프로젝트 기술회고1: 클린아키텍처

 

근 3개월간 진행한 케어밋 프로젝트에 대한 기술적 회고를 진행해 보려고 한다.

 

기술의 진정한 의의를 알지 못한 채로 사용한 기술이 많아 회고를 통해 해당기술에 대한 장단점을 논해보려고한다.

 

우선 프로젝트의 깃허브 링크는 아래와 같다.

 

 

 

GitHub - 3IDLES/idle-iOS: SW Maestro 15th | 재가요양 시장의 새로운 구인구직 플랫폼 | 케어밋

SW Maestro 15th | 재가요양 시장의 새로운 구인구직 플랫폼 | 케어밋. Contribute to 3IDLES/idle-iOS development by creating an account on GitHub.

github.com

 

4편에 걸쳐 회고하려는 주제는 다음과 같다.

 

뭔가 모바일 개발자가 한번쯤은 생각해볼 만한 주제라고 생각해서 글을 적어보려고한다..!

 

1. Clean architecture란 무엇이고 왜 사용하는가?
2. MVVM디자인 패턴은 무엇이고 왜 사용하는가?
3. 재사용 가능한 UI는 어떻게 구현되는가?
4. 모듈화?! 대체 왜하는거지?
5. 가치가 높은 개발자는 무엇일까? 
6. 협업방식과 훌륭한 팀원은 무엇인가?

 

 

 

클린아키텍처 무엇이고 왜 사용하는가?

 

클린아키텍처를 처음 접했던 것은 이번년 초였던 것 같다.

 

알고보니 클린아키텍처는 꽤나 유명한 개념이였고, 왜 지금에서야 이 개념을 알게되었는지 조금 의문이 든다.

(너무 귀닫고 산게 아닌지하는...🥲)

 

처음에는 책(로버트.C 마틴)을 읽었고

 

소프트웨어 마에스트로 김종찬 멘토님이 진행하시는 모바일클래스를 통해 궁금증을 조금씩 해소할 수 있었다.

 

 

계층화를 통해 계층별 역할을 정하고 각 계층에 속한 객체들이 해당 역할을 수행하게끔한다.

 

 

이상적이고 멋진 말이라고 생각한다. 하지만 이걸 코드로 옮기는 것은 생각보다 순탄하지 않았다.

 

 

Domain이란 뭔가...? 🤔

 

Domain 사전정의

 

 

우선 도메인이란 말이 참 낯설었다. 도메인.. 네트워크시간에 공부한 것 같은데..

 

 

아직까지도 정확한 뜻은 모르겠지만 대게 쓰이는 용도를 보면 '정수(Essence)'랑 뜻이 비슷한 것 같다.

 

 

말그대로 도메인 레이어는 앱의 본질이자 정수.. 정체성으로 가장 중요한 어떤 것.

 

볶음밥과 도메인

 

음식으로 비유하면 볶음밥 조리법이라고 볼 수 있다.

 

"강한 불에 기름과 함께 밥과 재료들을 볶는다. " 이것이 볶음밥의 정수이다.

 

어떤 재료가 들어가는 지 어떤 후라이팬을 사용하는지 하는 것들은 크게 중요하지 않고 정체성에 영향을 주지 못한다.

 

그렇다!! 이 느낌이 도메인이다.

 

 

Data.. 는 뭘까..?

데이타, 참 많이 쓰이는 용어이다,

 

휴대폰 요금제로도 쓰이고 등등, 클린아키텍처에서 말하는 데이터는 Data 말그대로이다..!

 

말 그대롭니다.

 

모바일 환경에서 데이터는 외부(네트워크)혹은 내부(로컬 DB등)에서 올 수 있다.

 

Data계층은 정확하게 데이터를 프로그램 내로 가져오는 역할을 수행한다.

 

 

도메인과 데이터 .. 그리고 레포지토리😇

 

도메인과 데이터는 그럼 어떤 관계를 가질까?

 

그렇다..! 데이터는 재료이다, 도메인은 볶음밥 레시피이다..!

 

하지만 재료가 있다고 바로 볶음밥을 만들 수 있을까? 

 

볶음밥을 하기 위해선 재료들을 잘게 다질 필요가 있다.

 

 

"잘게 다져진 재료"

 

 

맛있게 볶음밥을 할 수 있는, 앱! 아니 볶음밥조리에 맞는 형태이다.

 

재료를 잘게 다지는 역할을 하는 것이 바로 레포지토리이다.

 

 

개발측면에서 보면 DTO를 Entity로 변경하는 역할이라고 볼 수 있다.

 

 

여기서 DTO는 Raw한 재료를, Entity는 잘게 다져진 재료이다.

 

 

케어밋 프로젝트와 클린아키텍처

 

케어밋 프로젝트를 처음 시작할 때 마음과짐은 아래와 같았다.

 

 

"음, 클린아키텍처.. 완벽하게 이해는 안돼지만.. 일단 써보고 나중에 깨닫자"

 

 

이런 마음가짐은 훗날 수많은 리팩토링을 불러왔다.

 

 

예를들어 에러처리를 들 수 있다.

 

 

리팩토링전 케어밋 프로젝트는 아래와 같다.

 

리팩토링전

 

해당 구조를 구현할 시기 사실 도메인 레이어에 역할에 대해 완벽하게 이해가지 않았다.

 

"데이터 레이어에서 DTO를 Entity로 변경하고, 그걸 ViewModel에서 잘 쓰면 되는 것 아닌가?? 🤔"

 

 

그리고 이상한 생각을 해버렸다.

 

 

"아하! 에러 처리를 도메인에서 하면되겠내!! 에러도 엔티티처럼 사용할 수 있으니깐!!"

 

 

"아하 이게 도메인의 존재 이유구나"

 

 

위 사진에서 볼 수 있는 Error를 DomainError로 변경하는 작업은 도메인 레이어에서 진행된다.

 

 

처음에는 대게 잘했다고 생각했다.

 

 

하지만 생각을 해보면 문제는 금방찾을 수 있었다.

 

 

"볶음밥말고 나는 그냥 밥만 주세요"

 

 

😇 😇 😇

 

 

그렇다 도메인 로직이 필요없이 바로 엔티티를 요구하는 경우 굳이 Domain레이어를 거칠 필요가 없었다.

 

ViewModel에서 레포지토리를 직접적으로 사용하는 경우를 말한다.

 

 

하,,, 이 경우 도메인 레이어에서 어떠한 로직도 수행하지 않지만,

 

 

Error타입을 DomainError로 변경하는 것에 의의를 두고 Domain레이어에 새로운 객체를 만들어야 하는 것인가? 🥲

 

 

그렇게 탄생한 도메인 객체의 일부는 아래와 같다.

 

리팩토링전 도메인 객체

 

해당 사진의 convert함수는 Error를 DomainError로 변경하는 역할을 한다.

 

 

저 한가지로 로직을 위해 Domain객체를 만들고 적지않은 코드를 작성해야 한다.

 

 

개발초기 Domain 레이어의 역할에 대한 의문이 완전히 해소되지 않아 계속 방치했지만, 사실 정답은 뻔했다.

 

 

DTO를 Entity로 변환하는 과정은 Repository에서 담당하는 것이 올바르다, Error역시 예외가아니다.

 

 

"앱내 모든 로직은 도메인을 거쳐야해!!" 라는 명목하에 안티 패턴을 초례한 사례라고 볼 수 있다.

 

 

후에 코드를 전면 수정했다.. 참 힘들었다(이것이 기술부채). 🥲

 

 

리펙터링 후 UseCase객체 매서드

 

자 자 어디보자..

 

사실 convert함수가 제거된 것을 배곤 큰 차이는 없다.

 

UseCase객체가 아무런 역할을 하지 않는다는 점에서 OOP원칙에 반하지만,

 

ViewModel에 Repository를 직접 주입하는 것까지 리팩토링하는 것은 오바라고 생각했다. 😅 

(다음부턴 이러지 말자..)

 

레포지토리의 역할을 명확하게 했다는 것에 의의를 두기로했다.

 

 

이렇듯 기능이 뚜렷하지 않은 쓸모없는 UseCase에 대해 논의한 좋은 컨퍼런스 영상이 있어 공유하겠다.

 

 

 

간략하게 요약하자면, 조직마다 룰이 조금씩 다르다고 한다.

 

어떤 조직은 OOP에 다소 위배되더라도 쓸모없는 UseCase를 유지하기도 하며, 레포지토리를 직접 ViewModel에서 사용하는 것을 허용하는 조직도 있다고 한다.

 

Entity, VO란 무엇인가..?

 

가장 햇갈렸던 부분은 돌이켜보면 Entity엔 어떤 객체들이 포함되어야하는가? 였던 것 같다.

 

 

처음에 이해했던 것은, "앱전반에 관심사 가지고 있는 객체" 즉, Domain부터 시작해 모든 앱영역에서 사용되는 객체로 인식했다.

 

 

하지만, 이렇게 생각하면 엔티티는 끝도 없이 늘어난다.

 

 

앞서 언급한 볶음밥 예시를 떠올려보자 우리 도메인의 핵심은 볶음밥을 만드는 것이다.

 

 

그릇과 포크 숫가락은 도메인의 관심사가 아니다.

 

 

따라서 도메인의 관심사가 아닌 타입은 엔티티영역에 속할 필요가 없다.

 

 

오히려 혼란만을 야기한다.

 

 

케어밋 프로젝트는 해당관점에서 굉장히 미흡하다. 잘못된 이해를 했기 때문이다.

 

케이밋 프로젝트의 엔티티

 

추후에 Screen관련 객체들은 Presentation 레이어로 이동시킬 계획이다. 🥲

 

 

OOP관점에서 Entity

 

OOP관점에서 객체는 분명한 역할을 가져야한다.

 

내가 개발한 엔티티 클래스를 한번봐보자

 

public class CenterProfileVO: Codable {
    public let centerName: String
    public let officeNumber: String
    public let roadNameAddress: String
    public let lotNumberAddress: String
    public let detailedAddress: String
    public let longitude: String
    public let latitude: String
    public let introduce: String
    public let profileImageInfo: ImageDownLoadInfo?
    
    public init(centerName: String, officeNumber: String, roadNameAddress: String, lotNumberAddress: String, detailedAddress: String, longitude: String, latitude: String, introduce: String, profileImageInfo: ImageDownLoadInfo?) {
        self.centerName = centerName
        self.officeNumber = officeNumber
        self.roadNameAddress = roadNameAddress
        self.lotNumberAddress = lotNumberAddress
        self.detailedAddress = detailedAddress
        self.longitude = longitude
        self.latitude = latitude
        self.introduce = introduce
        self.profileImageInfo = profileImageInfo
    }
}

 

 

분명 클래스이지만, 사실상 역할을 수행할 수 있는 메서드는 존재하지 않는다.

 

즉, 해당 타입은 구조체랑 다를 바가없다.

 

역할이 없다보니 의미가 퇴색되고 혼동을 초례하게 된다.

 

혼자 개발하는 프로젝트의 경우 큰 문제가되지 않지만, 협업을 할 경우

 

"이 객체는 뭐죠..?, 흠.. 재사용 가능한가요?"

 

추후에는 이러한 문제점을 인식하고 최대한 역할을 가지는 방향(인스턴스 매서드 추가)으로 디벨롭 하거나,

 

역할이 불명확한 경우 모두 구조체로 처리했다.

 

객체지향으로 개발을 한다고 말하려면 정신을 반짝 차려야한다...! 🤩

 

 

DataSource, DTO 너낸 또 뭐야..? 😓

 

처음에는 잘 이해되지 않았다. 아니... Repository가 저장소고 DataSource아니여??

 

왜 또 DataSource라는 것이 필요한 건데???

 

 

Repository의 역할을 명확하게 이해한 시점에서 DataSource의 역할은 분명하다.

 

DataSources

 

요약하자면 DataSource는 JSON상하차 일꾼이다...! 😆

 

 

데이터를 Repository로 전달하는 역할을 한다고 말할 수 있다.

 

 

그리고 DataSource가 다루는 JSON을 갖 파싱하여 만들어낸 굉장히 Raw한 데이터 타입들을 바로 DTO라고 한다...!

 

 

그러면 프로젝트를 모듈화 했을 때 가질 수 있는 이상적인 의존관계는 다음과 같다.

 

Domain Data이상적 모듈 의존관계

 

여기서 알 수 있는 점은 DataSource는 Domain에 의존하지 않아도 된다는 점이다.

 

 

당연한 말이지만, 클린아키텍처를 처음 접할 때

 

 

"Domain은 모든 모듈에서 의존해도 상관없지잖아!"

 

 

위 생각에 사로잡혀 본질적으로 모듈의 결합도를 낮춰야한다는 생각을 망각해버렸다.

 

 

결국 케어밋 프로젝트는 Domain모듈(Entity)과 DataSource가 커플링이 심하게 발생하였다.

 

 

프로젝트 모듈 의존구조의 일부

 

 

리팩토링으로 수정하기 힘든 수준이라 너무 아쉽다. 😭 (DTO가 너무 많고 복잡함)

 

 

현재 프로젝트에는 큰 영향이 없지만, 이게 만약 현업 프로젝트였다면..? 엄청난 기술 부채가 될 수 있다고 생각한다.

 

 

맻음말

 

클린아키텍처를 아직도 완전히 이해했다고는 생각하지는 않지만 어느정도 채화됬다고는 말할 수 있을 것 같다.

 

 

이번 글에서는 클린아키텍처를 적용하며 어려웠던 점들과 계층별 의의에 대해 다루었다. (Presentation은 굳이)

 

 

의존성 주입, 단방향 의존성 등 다른 주제 역시 다루고 싶지만, 다음을 기약해보겠다.

 

 

이번 프로젝트를 통해 클린아키텍처에 대해 느낌점을 말해보자면 다음과 같다.

 

 

1. 오류가 발생한 코드를 빠르게 찾을 수 있어 좋았다.

2. 복잡한 기능을 분할 정복할 수 있는 틀이라 좋았다.

3. 의존성 주입때문에 프로토콜을 하나하나 만드는 것은 힘들었다. 이 프로젝트에 필요한 코드였을까? 의문이 들긴한다.

4. 덕분에 ViewModel코드가 깔끔해졌다.

5. 세분화된 기능별로 테스트 코드를 작성하게 편했다.

 

등등..

 

하아~ 피곤하다... 하지만 오늘도 성장했다...⭐️

 

이 글에 잘못된 부분에 대한 댓글은 언제나 환영합니당 🙌