클린아키텍처 썼는데 왜 프로젝트가 더 더러워지지

MJ Studio
MJ Studio
Published in
9 min readMar 29, 2022

--

우리의 프로젝트는 클린아키텍처가 문제가 아닐 가능성이 높다.

Elixir

Introduce

개발자 로버트 마틴은 2012년에 어떤 소프트웨어 개발 방법론을 제시하고 클린아키텍처라고 칭합니다. 링크

적자생존이라는 말처럼 이 훌륭한 아키텍처는 2022년까지 이어져오며 개발을 시작하려면 무조건 알고 들어가야 하는 필수 지식이 되었습니다.

그렇다면 이건 개발을 이제 막 시작하는 누구나 알 수 있을 정도로 쉬운 내용인가요? 글쎄요 잘 모르겠지만 일단 저자는 이 아키텍처는 간단하고 따라 하기 쉽고 여러분의 두통까지 해결해 준다고 합니다.

Conforming to these simple rules is not hard, and will save you a lot of headaches going forward.

물론 오늘날까지 훌륭한 방법론으로 평가받고 있지만 은탄환은 없듯이 언제 어디서든 어떤 프로젝트에서든 무적기처럼 사용할 순 없습니다.

클린아키텍처를 소개하고자 쓴 글이었으면 여기서 아키텍처를 짧게 설명하고 장단점 및 샘플 코드를 설명하고 끝내야 합니다.

그것보단 클린아키텍처를 다루며 빠지기 쉬운 함정과 굳이 써야 하는지에 대해서 이야기해 보려고 합니다.

이해의 혼동

정의

다음과 같은 항목중에 클린아키텍처의 구현에 있어 없으면 큰일나는 친구들은 누구일까요?

  1. MVVM
  2. Database & Model
  3. Dependency Inversion
  4. Repository 인터페이스와 구현체
  5. Dependency Injection

정말 이름만 들어도 클린해보이는 친구들이 많네요.

아쉽게도 저 친구들중 아무도 클린아키텍처와 직접적인 연관은 없습니다.

왜냐하면 클린아키텍처는 소프트웨어 전반적인 추상적 방법론이기 때문입니다. 앞서 언급한 것들이 “수단”으로 이용되어 구현하는 데 쓰일 순 있지만 절대 아키텍처 그 자체와 포함관계가 되거나 필수불가결한 존재가 될 수 없습니다.

그럼에도 불구하고 클린아키텍처를 공부하다 보면 저런 것들을 많이 접하게 되는 이유는 똑똑한 개발자들이 구현체를 만드는 데 저것들이 쓰이면 깔끔하게 구현이 된다라고 발전시키고 코드를 공유했기 때문입니다(물론 엉클밥은 Dependency Inversion정도는 본문에서 언급하긴 했습니다).

본문에서 Repository란 단어는 단 한개도 찾아볼 수 없지만 클린아키텍처의 구현체에서 Repository를 빼놓을 수 없는 이유는 디자인 패턴중 하나인 Repository가 모듈간 레이어의 분리에 효율적이라고 판명이 났기 때문입니다.

그러면 클린아키텍처는 뭘까요?

클린아키텍처의 시작과 끝은 글의 가장 첫 문단에 언급된 The Dependency Rule 그 자체입니다.

의존성 규칙은 그의 설명대로 정말 간단한 개념입니다. 모듈의 의존성이 단방향으로 이어져야 한다는 것입니다. 흔히 보이는 과녁그림도 이를 설명하기에 가장 적합한 그림입니다.

본문의 다른 부분들은 이 의존성 규칙이 지켜지는 상황 하에서 효율적으로 모듈들을 분리하는 방법과 의존성 규칙을 지키기 위한 트릭들을 부연설명합니다.

그러니까 사실 엉클밥은 과녁판같이 생긴 단방향 의존성 아키텍처를 제시했으며, 이를 구현하기 위한 제 1원칙으로 The Dependency Rule을 꼽은 것이며, 이걸 지키기 위해 여러 추상적인 개념들을 덧붙인 것입니다.

즉, 우리가 실제로 구현하고 있는 클린아키텍처에서의 Repository 구현체는 사실 아키텍처의 가장 중요한 원칙을 지키기위해 파생된 추상체들을 구현하기 위한 추상체의 구현체였습니다(?).

아키텍처와 디자인 패턴

두 용어를 혼용해서 쓰기도 합니다만 기본적으로 아키텍처란 디자인 패턴보단 좀 더 범용적인 방법론을 의미할 때가 많습니다.

클린아키텍처와 MVVM패턴, 무엇이 더 안드로이드 개발에 우수한 방법론인가요?

이 질문은 두 용어를 동일한 선상에 두고 비교를 하는 것부터 틀려있습니다.

클린아키텍처는 소프트웨어 전반적인 개념이고 MVVM은 그저 UI(View)의 상태를 어떤식으로 관리하며 소통할 지 정의한 하나의 디자인 패턴이기 때문입니다.

클린아키텍처에서 따지자면 Interface Adapters에 포함될 수 있는 개념이고 MVVM이 MVC, MVVI 따위나 그냥 한 파일에 모든걸 다갖다박아둔 30,000줄 짜리 CrazyHeavyMegaView.kt 정도여도 UI 프레임워크에게 화면을 어떻게 그릴지 알려줄 수 있고 Use Cases와 소통한다면 클린아키텍처의 관점에선 아무런 문제가 없습니다.

이런식으로 클린아키텍처를 이해하는데 추상적인 개념을 이해하는 것 보다 자잘한 부분이나 구현체를 먼저 익히게 되는것은 너무도 쉽게 우리를 혼란스럽게 합니다(유경험자입니다).

레이어

실제 프로젝트를 설계할 때 위 사진에서 네개의 레이어로 존재하는 모든 것들을 정확히 나눌 수는 없습니다(사실 있지만 엄청난 노력이 필요할 것입니다).

게다가 우리는 이런 4줄의 과녁모양에 집착할 필요가 없습니다. 실제로 엉클밥의 글 본문에서 Only Four Circles? 이란 섹션에서 다음과 같이 언급합니다.

There’s no rule that says you must always have just these four. However, The Dependency Rule always applies.

다른건 다 포기하서라도 무조건 준수해야 하는 것은 의존성 규칙입니다. 의존성 규칙만 지킨다면 뭔짓을 해도 일단 클린 아키텍처라고 우겨도 합법입니다.

하지만 클린아키텍처를 소개하는 어떤 글들은 너무 과도하게 레이어에 집착하는 경향이 있다고 소신발언 한 번 하겠습니다.

모듈간 레이어를 나누는건 탁월한 선택입니다. 이건 굳이 클린아키텍처가 아니더라도 항상(99%) 옳은 방법입니다.

과녁사진이 아닌 과녁을 오려서 펼치거나 특정 플랫폼에 맞춰 일자 형식으로 레이어들이 구분되어 딱 선을 그을 수 있는 그림들은 클린아키텍처를 구현법을 설명해주기에 정말 좋은 방식입니다.

그러나 처음부터 어떤 파일을 어떤 위치에 두는 틀을 잡아버리고 개발을 시작하게 된다면 아키텍처를 구현하기 위한 수단으로 레이어링을 하는것이 아닌 수단을 위한 코드를 짜게될 위험이 있습니다.

프로젝트가 커감에 있어 아키텍처의 추상적 개념은 유지하되 그 구현법에 있어서는 코드와 다를 것 없이 유연한 리팩터링이 필요하다고 생각합니다.

어떤 레이어에 우리 플랫폼의 코드 중 어떤 것들이 위치해야 하는지를 파악하는 것도 좋지만 그보다 더 중요한건 왜 이게 거기에 있어야 하고 그로 인해 얻을 수 있는 이점이 무엇인지 고민해 보는 것입니다.

다음과 같은 재미있는 글을 읽었습니다.

이 글의 내용은 예를 들어, 안드로이드에서 클린아키텍처를 구현하는데 하나의 공식처럼 여겨지는 presentation, data, domain 레이어들은 그렇게 코드를 짜기만해서 이점이 있냐에 대한 근본적 질문입니다.

존재하는 모든 파일들을 공통된 세개의 레이어에 그냥 나누기보다 각 feature 별로 나누고 부분적으로 위 아키텍처를 적용함이 옳다고 말합니다.

우리가 흔히 이 세 개의 레이어를 나눠 아키텍처를 적용해야 할 시기는 프로젝트가 커졌을 때라고 생각하고 있지만 실제로 “리팩터링”의 저자인 마틴 파울러는 이 방법이 상대적으로 작은 모듈에만 적용되어야 된다고 말합니다.

클린아키텍처 덕을 보고 계신가요?

앞서 언급한대로 presentation, data, domain 의 레이어로 전체 앱을 나누고 정말 아키텍처로 인한 이득을 보고 계실 수도 있습니다.

하지만 그것이 단순히 레이어를 나누어 소스코드를 분리시켜 좀더 편하게 느껴지는 것이 아닌지, 모듈간 의존성 규칙이 잘 지켜지고 있는지 살펴봐야 합니다.

내부 모듈에서 외부 모듈을 참조하고 있을 때, 외부 모듈의 클래스의 이름을 바꾸는 게 어렵지 않다고 느껴진다면 그게 클린아키텍처덕인지 아니면 똑똑한 IDE의 자동 리팩터링 기능 덕분인지 경계해야합니다.

앞으로 도입될 가능성이 0.001%인 로컬 데이터베이스를 위해 구현체가 RemoteDataSource 하나뿐인 DataSource를 추상화해 Repository와 Data Source를 굳이 시간을 들여 나누는 것이 의미가 있을 지 고민이 필요합니다.

클린아키텍처 써야하나요?

의존성 규칙을 지켰을 때 얻는 장점은 명확하고 훌륭합니다. 수정해야할 부분만 수정하고, 테스트도 쉽고 다른사람한테 자랑도 할 수 있고 뭐 아무튼 개쩝니다.

앞서 참조한 다른 글에서도 아키텍처는 상황에 맞게 써야한다고 마무리를 짓는데, 정말 저도 글쓴이로써 책임감이 없다고 느끼실 수도 있지만 그 말보다 옳은 것은 없고 가장 어려운 것이라고 생각합니다.

예를 들어, 제가 개발하고 있는 React Native앱은 API 연동측에 많은 레이어를 두지 않습니다. 왜냐하면 가변적인 데이터 구조로 인해 굳이 로컬 데이터베이스를 이용해서 캐시를 해둘 필요가 없고 Node.js의 모듈 시스템을 잘이용하면 API 모듈을 monkey-patch를 이용해 테스트하기 너무 쉽기 때문입니다. 이제 누가 저한테 “이건 클린아키텍처를 사용해 다시짜야해” 라고 한다면 그 말엔 명분이 없을 것입니다.

Hello.kt -> Bye.kt -> What.kt -> The.kt -> Hell.kt

위 5개의 모듈은 클린아키텍처로 구현되었지만 대체 뭔 모듈인지 수정하려고 하면 알아볼 수가 없을 것입니다.

과장된 느낌이 있지만, 이게 클린아키텍처의 문제일까요? 아키텍처는 프로젝트의 모든 문제를 자동으로 해결해주는 마법의 영약 엘릭서(Elixir)가 될 수 없습니다.

하지만 전반적인 맥락에서 예상되는 곳에 있을 게 있고 프로젝트가 어떻게 진행되는지 한 눈에 알아볼 수 있는 훌륭한 보조제정도는 될 것이라고 생각합니다.

글 읽어주셔서 감사합니다. 🎉

--

--