많은 글이 클린 아키텍처를 레이어별 프로젝트/폴더 구조로 오해한다. 이는 CCP(공통 폐쇄 원칙) 위반과 문헌의 모호성, 커뮤니티 확산이 낳은 결과이며, 해결책으로 피처(기능) 기반 폴더링을 제안한다.
클린 아키텍처는 프로젝트 구조가 아니다. | by Steve Bishop | Medium
===============
가입하기
가입하기

상단 하이라이트
팔로우
읽는 데 7분
·
2024년 4월 13일
1.2K
24
공유
확대 이미지가 표시됩니다

클린 아키텍처에 관한 글은 무수히 많고, 기본적인 것들은 사람들이 잘 짚는다. 하지만 내가 계속해서 반복적으로 보는 공통적인 문제가 하나 있다. 그건 사람들이 Robert(엉클 밥) Martin의 책이나 원래의 블로그 글에 실제로 존재하지 않는 프로젝트 구조를 추천한다는 점이다. (참고 참조)
내가 보기에 이런 잘못된 추출은 두 가지 근본 원인과 하나의 증폭 원인에서 비롯된다. 먼저 두 가지 근본 원인부터 설명하고, 그것들이 어떻게 증폭되는지 이야기하겠다. 글 마지막에는 프로젝트를 어떻게 구조화해야 하는지에 대한 주류의 지혜에 가장 좋은 대안을 제시하고, 그 이유도 설명하겠다.
첫 번째 근본 원인은 엉클 밥이 해당 주제에 대한 초기 블로그 글에 올린 유명한 그래픽 때문이다. 아래에서 클린 아키텍처의 전체 개념을 보여주는 이 그래픽을 볼 수 있다:
확대 이미지가 표시됩니다

https://blog.cleancoder.com/uncle-bob/images/2012-08-13-the-clean-architecture/CleanArchitecture.jpg
그렇다면 이 그래픽에는 무엇이 문제일까? 사실 그래픽 자체에는 문제가 없다. 문제는 이를 해석하는 방식에 있다. 많은 사람들이 이 그림을 근거로 각 레이어가 애플리케이션의 하나의 프로젝트, 패키지, 또는 루트 폴더를 나타내야 한다는 잘못된 결론을 내렸다. 하지만 이렇게 하면 엉클 밥이 책에서 설명한 공통 폐쇄 원칙(CCP, Common Closure Principle)을 실제로 위반하게 된다.
공통 폐쇄 원칙이란 무엇인가? CCP는 다음과 같이 말한다:
하나의 컴포넌트 안의 클래스들은 동일한 종류의 변경에 취약하다는 점을 기준으로 함께 묶여야 한다. 어떤 변경이 한 컴포넌트에 영향을 미칠 때, 그 변경은 해당 컴포넌트의 모든 클래스에 영향을 주고 다른 컴포넌트에는 영향을 주지 않아야 한다.
컴포넌트란, 언급하자면, 유스 케이스에 결속(cohesive)된 클래스들을 의미한다. 즉, 유스 케이스의 변경에 의해 영향을 받을 수 있는 클래스들은 함께 묶여야 한다. 하지만 이 클래스들을 서로 다른 폴더, 프로젝트, 패키지로 옮기면 더 이상 함께 묶여 있지 않게 되고, 따라서 CCP를 위반하게 된다.
아래는 흔히 볼 수 있는 레이어드 폴더 구조의 예시다:

이러한 "레이어 폴더" 아래에 "기능별 폴더"를 두는 방식은 유사한 기능을 수행하는 클래스들을 같은 폴더에 넣는다. 그러나 동일한 저장소의 아래 이미지에서 보듯, 하나의 컴포넌트는 온갖 곳에 흩어지게 된다:

https://github.com/ardalis/CleanArchitecture
이 경우, Contributor 컴포넌트는 40개가 넘는 개별 클래스와 최소 4개의 서로 다른 프로젝트, 그리고 최대 3단계로 깊어지는 20개 이상의 서로 다른 폴더에 걸쳐 흩어져 있다. 이는 말도 안 되는 수준의 분절과 분산이다. 도대체 누가 이 코드를 이해하거나 다룰 수 있겠는가? 그리고 이것은 단지 하나의 컴포넌트일 뿐이다. 아직 전체 애플리케이션도 아니다. 수백 개의 컴포넌트를 가진 엔터프라이즈 애플리케이션을 다뤄야 한다고 상상해 보라.
이는 코드의 응집도(cohesiveness)가 아니라 기능 레이어에 따라 코드를 분리했을 때 발생하는 대표적인 사례이며, 그에 따르는 모든 슬픔을 보여준다.
이 레이어 기반 폴더링 구조가 등장하게 된 두 번째 원인은, 프로젝트와 패키지 내 코드의 조직에 대해 책이나 원글 어디에서도 다루지 않았기 때문이다. 엉클 밥은 이와 관련해 아무 말도 하지 않았다(글 끝의 참고 참조). 이로 인해 사람들은 추측하게 되고, 그건 문제를 낳는다.
우리 업계에서 탄탄하고 건전한 조언이 나왔지만, 일부 세부 사항이 다뤄지지 않아 혼선이 생긴 게 이번이 처음은 아니다. 애자일 선언문은 비즈니스 오너의 니즈를 충분히 다루지 못하고 구체성이 떨어진 탓에 왜곡되어버린 대표적인 좋은 아이디어다. 애자일 팀은 재무를 어떻게 처리해야 하는가? 애자일을 효과적으로 확장하는 방법은? 팀을 애자일하게 훈련하는 방법은? 등등. 선언문 자체가 이런 질문에 답하지 않기 때문에 수많은 책, 글, 영상, 소셜 미디어 게시물들이 이런 질문에 답하려 노력해 왔다. 이러한 모호성 위에 하나의 생태계가 만들어졌다.
클린 아키텍처도 비슷한 운명을 겪었다. 유스 케이스란 정확히 무엇인가? 클래스인가 모듈인가? 인터랙터와 같은 것인가? 이 모든 것 속에서 부트스트래핑은 어디에 있는가? 그리고 이 글이 다루려는 질문, 내 프로젝트 폴더 구조는 어떻게 되어야 하는가?
앞의 두 가지는 이 혼란의 근본 원인이라고 나는 생각한다. 그렇다면 세 번째 증폭 원인은 무엇인가?
첫 두 근본 원인의 모호함은, 선한 의도를 가진 이들과 클린 아키텍처라는 이름을 활용해 이득을 보려는 이들 모두가 채워 넣을 수 있는 빈틈을 만들었다. 공정하게 말하자면, 이 글 역시 또 다른 해석으로 보일 수 있다. 이에 대해 내가 할 수 있는 변명은 없다. 다만, 내가 한 말이 마음에 든다면 그걸로 충분하다는 말밖에는.
베스트 프랙티스를 해석하고 소프트웨어 개발자 대중을 교육하며 생계를 꾸리는 사람들이 큰 커뮤니티를 이룬다. 지난 6년 동안 나도 그중 한 사람이었다. 내 경험상, 커뮤니티에서는 어려운 개념을 단순화해 주거나 설명해 주는 듯 보이는 아이디어들이 흔히 통념이 되곤 한다. 누군가 책을 읽고 자신의 해석을 내놓고, 몇 편의 영상을 올리고, 몇 편의 글을 쓰고, 저장소를 하나 공개하면, 갑자기 그 해석이 표준처럼 굳어진다.
그 시점이 되면 해석의 정확성보다 그 해석의 확산력과 그 뒤에 있는 인물이 더 중요해진다. 내가 위에 예시로 든 코드 저장소는 2,600번 이상 포크되었고 14,800개가 넘는 스타를 받았다. 클린 아키텍처 주제의 저장소 중 가장 인기 있는 저장소 중 하나(어쩌면 가장 인기 있는)다.
이런 분들에게 많은 공을 돌린다. 매우 똑똑한 사람들이고 대개는 적어도 대부분을 제대로 짚는다. 그러나 여기서는 통념이 너무 엇나가서 클린 아키텍처가 제공하는 장점을 오히려 훼손할 정도라고 우려한다. 쉽게 이해되고 이해하기 쉬운 코드베이스여야 할 것이, 클래스가 사방에 흩어진 산만한 코드베이스로 바뀐다. 그 결과, 이런 코드베이스를 접하는 사람들은 이것이 클린 아키텍처의 사례라는 주장을 듣고, 이 모든 추함의 원인이 그 철학 자체라고 결론을 내린다.
다시 말하지만, 이런 면에서 애자일과의 유사성은 소름 돋을 정도로 닮았다.
피처 폴더(feature folders)다.
이 글에서 이 주제를 또 길게 늘어놓지는 않겠다. 코드를 피처 폴더로 조직하는 방법에 대해 설명한 글은 이미 충분히 많다. 수십 년 동안 사용되어 온 익숙한 폴더링 구조다. 대신, 왜 코드베이스가 레이어가 아니라 피처 기준으로 조직되어야 하는지 그 이유만 설명하겠다.
애플리케이션은 수직이 아니라 수평으로 성장한다. 다시 말해, 애플리케이션에는 끊임없이 더 많은 기능이 추가되지만 레이어는 거의 추가되지 않는다. 이러한 확장을 대비해야 한다. 코드가 커질수록 컴포넌트와 관련된 클래스를 더 쉽게 찾을 수 있어야 하고, 성장에 따라 코드베이스를 손쉽게 별도의 패키지와 서비스로 분리할 수 있어야 한다. 코드를 분리할 수 있는 솔기(seam)를 쉽게 찾을 수 있어야 한다. 폴더는 이러한 솔기를 만드는 데 아주 분명한 수단이다.
아래는 아까와 똑같은 저장소의 스크린샷이지만, 내가 클래스 이름을 조금 바꾸고 재조직한 것이다.

https://github.com/Xipooo/FeatureFolderCleanArchitecture
이 폴더 구조만 봐도 무슨 일이 일어나고 있는지 이해하기 쉽다. 애플리케이션의 서로 다른 도메인을 담는 폴더가 있다. 각 도메인에는 그 도메인의 이름을 딴 최상위 폴더가 있고, 각 폴더 안에는 해당 도메인과 관련된 각 유스 케이스(피처)별 하위 폴더가 있다. 그 폴더들 안에는 해당 유스 케이스(피처)에 결속된 클래스들이 들어 있다.
당신이 이 제품의 신규 개발자라고 상상해 보라. 글 앞부분의 구조 대신 이 폴더 구조를 보게 된다면? 이 코드를 탐색하고 각 클래스의 용도와 다른 클래스와의 연관을 이해하는 것이 훨씬 쉽다. 새로운 피처(유스 케이스)를 추가하는 것은, 그 피처와 관련된 모든 클래스가 들어 있는 새 폴더 단 하나만 추가하면 된다.
이 코드에서 거의 즉시 생산성을 낼 수 있다. 어렵지 않게 코드를 추가할 수 있다. 원하는 것을 빠르게 찾을 수 있다.
하지만 여기서 가장 중요한 점을 아무리 강조해도 지나치지 않다. 첫 번째 프로젝트 구조를 확장한다고 상상해 보라. 모든 관련 클래스를 찾아 새 프로젝트로 옮겨야 한다. 끔찍한 일이며, 그 과정에서 무엇을 망가뜨릴지 누가 알겠는가.
반면, 이 피처 폴더 구조에서는 이 폴더들 중 어느 것이든 그대로 떼어내 별도의 패키지나 서비스로 옮겨도 다른 폴더들에 영향을 주지 않는다. 이 애플리케이션은 확장하기가 훨씬 더 쉽다.
그게 바로 깔끔하고 좋은 아키텍처다.
*참고: 책의 마지막 장, 34장은 Simon Brown(엉클 밥 아님)이 집필했는데, 프로젝트 구조를 다루며 레이어별 패키징/폴더링을 명시적으로 반대한다. 이 글 역시 그 점을 반대한다.
1.2K
1.2K
24
팔로우
작성자: Steve Bishop -----------------------
나는 조직이 소프트웨어 개발의 도전을 극복하도록 돕는다.
팔로우

응답 작성
취소
응답
이 글과 지금까지 달린 댓글을 읽고, 다음과 같이 관찰했다:
64
답글 1개
답글 달기
글에 감사하지만 중요한 점을 놓치셨다고 생각합니다: 클린 아키텍처(원래의 Ports & Adapters 개념의 의미)는 의존성에 관한 것이며, 그 방향은 바깥에서 안으로, 즉 인프라스트럭처로부터…더 보기
23
답글 2개
답글 달기
예! 드디어 내가 많은 사람들에게 계속해서 말해야 했던 것을 누군가 말해주네요. 클린 아키텍처는 구현을 위한 것이 아니라 결과를 위한 개념들의 모음입니다.
저는 NestJS 팬입니다. 클린 아키텍처를 가능하게 하면서도 "슬라이스" 혹은 피처…더 보기
40
답글 달기
모든 응답 보기

Programming for humans ---------------------- ### 프로그래밍 언어의 목적은 무엇인가?
5월 30일

소속:
작성:
S.M.A.R.T. 목표는 멍청하다 ------------------------- ### 더 강한 팀을 만들려면 왜 이걸 버려야 하는가
2022년 5월 30일

소속:
작성:
SMART 목표는 그만, PACED 목표를 하자 ---------------------------------------------- ### 올바른 마인드셋이 좋은 목표 설정의 핵심
2022년 6월 8일

당신은 소프트웨어 개발자인가, 소프트웨어 엔지니어인가? ----------------------------------------------------
2024년 10월 1일

2월 24일

소속:
작성:
5월 24일
소속:
작성:
6일 전

7월 14일
소속:
작성:
3월 19일

소속:
작성:
3일 전