iOS 프로젝트/moti(네이버 부스트캠프)

[iOS] moti 개발일지 - 2. 클린 아키텍처와 Swift Pacakage 모듈화

유정주 2023. 12. 27. 12:20
반응형
 

GitHub - boostcampwm2023/iOS02-moti: 네이버 부스트캠프 8기 iOS moti 앱 📸

네이버 부스트캠프 8기 iOS moti 앱 📸. Contribute to boostcampwm2023/iOS02-moti development by creating an account on GitHub.

github.com

 

서론

moti 프로젝트는 클린 아키텍처와 MVVM-C로 구조를 잡았고, Swift Pacakage로 모듈화를 진행했습니다.

클린 아키텍처를 선택한 이유와 코디네이터를 접목한 이유를 먼저 알아보고,

모듈화를 진행한 이유와 그중에서도 Swift Pacakage를 선택한 이유를 알아보겠습니다.

 

 

클린 아키텍처

moti는 클린 아키텍처로 레이어를 분리해 구조화했습니다.

레이어 분리가 필요하다고 느낀 이유는 관심사의 분리와 협업 때문입니다.

 

관심사의 분리

먼저 관심사의 분리에 대해 말해보겠습니다.

저희는 이번에 객체를 잘게 나눠보자! 결심을 했습니다.

 

이 객체에는 ViewController, ViewModel도 포함되어 있고, 이렇게 나누다 보니 폴더가 중구난방 되기 시작했습니다.

폴더가 중구난방 되는 이유를 고민해 보니 분류할 적절한 기준이 없기 때문이라고 생각했습니다.

 

저희는 적절한 기준으로 클린 아키텍처의 도메인 레이어, 프레젠테이션 레이어, 데이터 레이어가 매력적으로 느껴졌습니다.

명확하게 분리되어 있고, 자료가 많으니 초보 개발자가 처음 시도하는데 딱 좋은 아키텍처로 느껴졌어요.

그래서 저희는 클린 아키텍처를 적용하기로 결정했습니다.

 

협업

두 번째 이유는 협업이에요.

moti는 저를 포함해서 두 명이 iOS 협업을 진행하고 있습니다.

코드 충돌을 피하기 위해 최대한 다른 파일을 수정하기로 했습니다.

그러기 위해서 자연스럽게 객체 분리, 파일 분리가 필요해졌고, 위에 적은 이유로 클린 아키텍처를 선택했어요.

분리해서 작성했지만 결국엔 하나의 이유로 합칠 수 있겠네요 ㅎㅎ

 

 

클린 아키텍처 적용 범위

클린 아키텍처를 적용하기로 결정한 뒤 어느 정도로 적용을 할 건지 고민을 했습니다.

결론부터 말하면 저희는 도메인 레이어, 프레젠테이션 레이어, 데이터 레이어로 나누기로 했습니다.

 

처음에는 객체의 역할에 따라 레이어도 세부적으로 나눠보려고 했는데요.

아키텍처 초보인 저희가 세부적으로 나눈다고 해도 제대로 나눌 수 있을까? 걱정이 되었습니다.

그렇게 나눈 게 제대로 된 분리인지도 의문이 들었고요.

 

이에 대해 멘토님께 조언을 구한 결과, 잘못 나눈걸 다시 합치는 것보다

처음에 크게 나누고 차차 분리하는 게 더 쉽다고 말씀해 주셨습니다.

 

이후 멘토님의 조언과 함께 더 고민해 보며 크게 세 개로 나누고 이후에 더 잘게 분리하기로 결정했습니다.

현재는 프레젠테이션에서 디자인도 분리되었고, 프레젠테이션에서 수정 범위가 적고 역할 분리가 가능한 객체를 추가로 분리할 계획이에요.

 

함께 고민한 팀원과 적절한 조언을 해주신 멘토님께 다시 한번 감사드립니다 :)

 

 

클린 아키텍처 후기와 UseCase

클린 아키텍처 만족도는 상당히 높았습니다.

물론 어려운 점도 있었지만, 저희가 원했던 객체 분리를 적절히 할 수 있었고, 코드 충돌도 줄어들었습니다.

 

클린 아키텍처를 적용하면서 가장 고민이 되었던 점은 UseCase에요.

아직 비즈니스 모델이 재사용되는 흐름이 적다 보니 UseCase가 의미가 있을지 고민이 되었어요.

실제로 지금까지 작성된 UseCase는 전부 Repository와 ViewModel을 연결하는 역할만 하고 있었습니다.

public func execute(achievementId: Int) async throws -> [Emoji] {
    return try await repository.fetchEmojis(achievementId: achievementId)
}

이런 느낌으로요.

 

이게 정말 UseCase의 최선일까?라는 생각을 해봤고 절대 아니라는 게 결론이었습니다.

더 잘 쓸 수 있는 방법을 고민해 봤고, UseCase의 존재 이유를 함께 생각해 보며 결론을 낼 수 있었습니다. (멘토님의 조언도 함께했습니다 ㅎ)

 

UseCase는 반복되는 비즈니스 로직을 위한 객체입니다.

따라서 UseCase에는 반복되는 비즈니스 로직이 포함되면 되는 거였어요.

결론만 보니 너무 당연한 이야기인데 처음의 저는 왜 몰랐을까요? ㅎㅎ;;

그 결과, 

public func execute() async throws -> Version {
    let version = try await repository.fetchVersion()
    saveVersion(version)
    return version
}

이런 느낌으로 UseCase를 수정할 수 있었어요.

이 예시 코드만 보면 큰 차이가 없는데 변화를 시도했다는 거에 집중해 주세요 ㅎ

 

클린 아키텍처를 더 잘 쓰기 위해 고민했고, 그 고민은 아직도 현재 진행형입니다.

예를 들면, ViewModel에서 진행하는 페이지네이션은 Repository에 있는 게 더 적절하지 않을까? 하는 고민이요.

지금의 구조를 살펴보며 더 제대로 분리하기 위해 계속 고민하려고 합니다.

 

 

코디네이터 Coordinator

다음은 코디네이터 패턴에 대해 얘기해 봅시다.

처음 저희는 코디네이터가 굳이 필요할까? 의문이 들었습니다.

그래서 그냥 MVVM으로 진행했어요.

 

그런데 구현을 하다 보니 ViewController에서 의존성을 주입하고, 화면을 이동하는 로직때문에 ViewController가 상당히 커졌어요.

비즈니스 로직을 분리하기 위해 ViewModel을 만든 것처럼

의존성 주입과 화면 이동 로직을 분리하기 위해 코디네이터를 적용하기로 결심했습니다.

 

다른 개인 프로젝트에서는 이런 고민이 든 적이 없었는데 자연스럽게 분리해야겠다는 고민이 드는 경험을 했네요.

네이버 부스트캠프에서 자주 들었던 말이 "디자인 패턴은 무작정 쓰는 것보다 필요하다고 느껴졌을 때 쓰는 게 좋다"였는데

그걸 직접 경험하니 기분이 좋으면서 신기하더라고요.

참 좋은 경험 했다 싶었습니다 ㅎㅎ

 

아무튼 코디네이터 패턴을 적용하기로 결정하면서 추가 학습을 해야 했습니다.

이게 생각보다 어렵더라고요...?

특히 VC 간의 데이터 전달을 구현하는 게 어려웠습니다.

코디네이터가 없었으면 그냥 ViewController에 주입하면 되는데

코디네이터를 통해 전달하고... Delegate로 받고... 이런 흐름이 추가되면서 복잡해지는 게 느껴졌습니다.

3 depth로 데이터를 전달할 때는 정말 아찔하더라고요...

 

이 문제에 대해 다른 캠퍼들과 토의를 해봤는데 그냥 자연스러운 부작용(?)이라고 해서

역시 모든 건 좋은 점만 있지 않고 단점도 존재한다는 걸 배웠습니다. (인생의 교훈을 개발하면서... ㄷㄷ)

 

 

모듈화

다음 고민한 건 모듈화입니다.

모듈화를 적용할지 말지부터 고민했는데, 결론부터 말하면 크게 의미는 없을 것 같지만 적용은 해보기로 했습니다.

한 번 경험해보고 싶었거든요.

 

크게 의미가 없다고 생각한 이유는 프로젝트 크기가 크지 않다고 생각했기 때문입니다.

또한, 6주 간의 짧은 일정에서 모듈화의 장점이 크게 빛을 내기 힘들다고 판단했어요.

6주 내내 프로젝트 전역으로 변화가 발생할 테니까요.

 

그럼에도 모듈화를 적용하기로 결정한 건 경험 때문이었습니다.

채용 공고에 모듈화가 있는 경우가 많아서 모듈화를 경험해보고 싶었어요.

회사에서는 기술을 함부로 적용하기 어렵죠.

따라서 이번 기회에 모듈화를 적용해 보며 경험을 쌓아보기로 했습니다.

 

근데 이것만으론 의미 있는 의사 결정이 아니라고 생각했어요.

그래서 저희는 모듈화를 적용해 보며 한 가지 더 확인하기로 했습니다.

모듈화를 적용하기 전에 저희는 "모듈화가 의미가 없을 거야"라고 생각했는데

정말로 모듈화가 의미가 없을지, 만약 정말로 의미가 없다면 그 이유는 짧은 기간뿐일지, 다른 이유는 없을지 고민해 보기로 했습니다.

(아직 유의미한 결과를 도출하진 못했지만... 유지보수를 하며 더 느끼는 게 많아질 거 같아서 따로 포스팅해 보겠습니다!)

 

 

Swift Pacakage 모듈화

모듈화는 Swift Pacakage를 이용해 진행했습니다.

Tuist와 Swift Pacakage를 고민했는데요.

순혈인 Swift Pacakage를 선택했습니다.

 

채용 공고에는 Tuist가 더 많지만, 서드파티보다는 퍼스트파티를 먼저 사용해보고 싶었어요.

모듈화 자체가 처음이라 도구보다는 모듈화에 집중하고 싶었답니다.

 

예상치 못한 부작용

Swift Pacakage로 모듈화를 진행하며 예상하지 못한 부작용을 만났습니다.

바로 #Preview를 사용하지 못하는 거였습니다.

#Preview에서 이 에러가 계속 발생하더라고요...

 

기술의 원리를 생각해 보니 어쩌면 당연한... 거였습니다...

Swift Pacakage는 변경된 내용만 간헐적으로 컴파일을 하는 거고,

#Preview는 수시로 컴파일을 해서 UI를 표시하는 거니 두 개를 함께 사용하면 모순인 거죠.

 

안타깝게도 모듈화가 더 우선되었고, 그 결과 #Preview에서 오류가 발생하는 것이었습니다.

 

이 경험을 통해 기술 선택은 신중해야 한다는 것을 배웠습니다.

회사에서 기술 선택을 신중히 하는 이유도 직접 경험을 했고요.

지금 저에게는 예상치 못한 오류가 경험이고 작은 해프닝이지만, 회사에서 예상치 못한 오류는 큰 손해일테니까요... ㅠ

좋은 경험이었습니다.

 

 

마무리

이번 포스팅에서는 클린 아키텍처, 코디네이터, 모듈화를 선택한 이유와 과정, 결과를 작성했습니다.

프로젝트 전역에 적용되는 구조를 고민하며

어떻게 해야 관심사와 객체를 잘 분리하고, 협업을 쉽게 할 수 있을지, 적용한 기술을 어떻게 잘 쓸지 고민한 경험이었습니다.

이 포스팅에서 다룬 고민들은 단기간에 해결되는 고민도 아니고, 장시간 유지보수를 할수록 값져지는 것이라고 생각해요.

그래서 꾸준히 유지보수 하면서 비교하고 고민하려고 합니다.

 

다음 포스팅에서는 moti에 적용한 인터랙션 애니메이션에 대해 다뤄보겠습니다.

 

감사합니다.


아직은 초보 개발자입니다.

더 효율적인 코드 훈수 환영합니다!

공감 댓글 부탁드립니다.

반응형