[Clean Architecture] 컴포넌트 원칙(ADP, SDP, SAP)
카테고리: Clean Architecture
저자는 컴포넌트 구조와 관련된 아키텍쳐를 침범하는 힘은 기술적이며, 정치적이고, 가변적이라고 하였습니다. 여기서 정치적이다라는 말이 상당히 인상깊었습니다. 기술적이며, 가변적인것은 그럴 수 있다고 생각했는데, 일적인 부분에서 정치가 개입되면 안된다고 생각했기 때문입니다. 하지만, 실제로 일을 시작해보니 입김이 쎈 고참들이 하는 이야기가 곧 정답이 되는 경우가 많기 때문에 내가 더 좋은 아이디어가 있다고 하더라도 묵살될 수 있음을 느꼈습니다. 즉, 좋은 구조를 만들기 위해서는 결국 기술적인 요소와 함게 참여가 필요하다고 느꼈습니다.
ADP (의존성 비순환의 원칙)
컴포넌트 의존성 그래프에 순환이 있어서는 안된다.
코드를 잘 짜고 나서 당장은 동작하던것이 머지(Merge)만 하면 동작하지 않았던 경험이 있을것입니다. 이것은 제 모듈은 잘 동작하게 만들어 놨지만, 제 모듈에서 의존하는 다른 컴포넌트에 수정이 일어나서 발생하는 현상일 확률이 높습니다. 이런 현상을 저자는 숙취 증후군이라고 명명합니다.
이러한 숙취 증후군을 없애는 방법으로는 첫번째 주단위 빌드와 의존성 비순환의 원칙이 있습니다.
첫번째 주단위 빌드의 경우에는 소프트웨어적인 문제를 정책적으로 해결한 방법이라고 할 수 있습니다. 주단위로 빌드하여 서로 버그를 잡으며 컴포넌트를 수정해 나가자는 취지 입니다. 하지만, 이것은 프로젝트가 커지면 현실적으로 불가능한 방법일 뿐더러, 문제의 원인인 버그가 나타나는것을 해결하는것이 아니라 결과만 도출하는 해결방법이라고 생각합니다. 따라서 개발자는 2번째 방법을 지향해야 합니다.
두번째 방법은 의존성 비순환의 원칙 입니다. 해당 원칙은 이름 그대로 의존성이 서로 순환되면 안된다는 원칙입니다. 여러가지에 의존할 수 있지만, 그 의존성이 돌고돌아 처음의 컴포넌트로 오면 안된다는 것입니다. 또한, 순환 의존성을 제거하여 개발 환경을 릴리스 가능한 컴포넌트 단위로 분리하여 개별 개발자 또는 단일 개발팀이 책임질 수 있는 작업 단위가 될 수 있도록 하는것입니다. 이렇게하면, 어떤 팀도 다른 팀에의해 좌지우지 되지 않습니다. 책임 소재가 명확해 지는것이죠.

위 사진처럼 Main(최상위 컴포넌트)에서 시작한 의존성은 이어지더라도 절대로 최상위 컴포넌트로 도착하지 않습니다. 해당 모습이 의존성 순환이 사라진 모습입니다.
또한, 이렇게 해놓으면 테스트를 구성할 때 대체로 적은 노력이 든다는 뜻이며, 고려해야 할 변수도 상대적으로 적어집니다. 그리고 시스템 전체를 릴리스할때는 컴포넌트 단위를 합쳐서 상향식으로 진행되게 됩니다.
다음은 컴포넌트 순환을 끊는 방법 입니다.
- 의존성 역전의 원칙(DIP)를 사용한다.
- 2개의 컴포넌트가 서로 순환의존하고 있다면 2가지를 모두 의존하는 새로운 컴포넌트를 만든다.
의존성 순환은 혼자 호기롭게 시작한 프로젝트를 하다보면 쉽게 겪을 수 있는 문제 입니다. 따라서, 프로젝트의 규모가 커질때 의존성 구조에 순환이 발생하는지를 항상 관찰하며 없애야 합니다. 결국 컴포넌트 의존성 그래프는 자주 변경되는 컴포넌트로부터 안정적이며 가치가 높은 컴포넌트를 보호하려는 아키텍트가 만들고 가다듬게 됩니다. 아직 아무런 클래스도 설계하지 않은 상태에서 컴포넌트 의존성 구조를 설계하려고 시도한다면 상당히 큰 실패를 맛볼 수 있습니다. 따라서 컴포넌트 의존성 구조는 시스템의 논리적 설계에 발맞춰 성장하며 또 진화해야 합니다.
SDP (안정된 의존성 원칙)
안정성의 방향으로(더 안정된 쪽에) 의존하라.
설계란 결코 정적일 수 없습니다. 소프트웨어 설게에서는 더더욱 그렇다. 그렇기에 컴포넌트를 의존시킬때는 해당 컴포넌트가 변경될것을 예상합니다. 그렇게에 우리는 변경이 쉽지 않은 컴포넌트가 변동이 예상되는 컴포넌트에 의존하게 만들어서는 절대 안됩니다.
즉, 해당 원칙의 이름에서 볼 수 있듯이 가급적 안정된 컴포넌트를 의존시키는것이 좋습니다. 그렇다면 안정성이란 무엇일까요? 웹스터 사전에서는 쉽게 움직이지 않는이라고 정의 합니다.

위 사진에서 3개의 컴포넌트가 X 컴포넌트에 의존하고 있습니다. X 컴포넌트는 다른 컴포넌트에 의존하고 있지 않기 때문에 안정된 컴포넌트라고 할 수 있습니다. 그리고 3가지의 컴포넌트를 책임지고 있습니다. X가 의존하고 있는 컴포넌트가 없기 때문에 X는 외부 요인으로 변경될 일이 없습니다. 따라서 X 컴포넌트는 독립적이라고 말합니다.

반대로, 위 사진은 Y가 여러 컴포넌트에 의존하고 있습니다. Y를 바꿀 수 있는 요소가 3가지나 있기 때문에 Y는 안정적인 컴포넌트가 아닙니다. 이 경우 Y는 의존적이라고 말할 수 있습니다.
그렇다면, 어떻게 안정성을 측정할 수 있을까요? 소프트웨어에서는 fan-in과 fan-out으로 표현 합니다.
- fan-in : 안으로 들어오는 의존성
- fan-out : 바깥으로 나가는 의존성
- I : 불안정성 : fan-out / (fan-in + fan-out) 이 지표는 (0,1) 범위의 값을 가짐. I = 0일때 최고로 안정된, I = 1일때 최고로 불안정한 컴포넌트임
그렇다면, 모든 컴포넌트가 안정적이어야 할까요? 그것은 아닙니다. 모든 컴포넌트가 안정적이라면 이 소프트웨어는 변경이 불가능합니다. 이는 바람직한 상황이 아닙니다. 따라서 모든 컴포넌트를 안정된 컴포넌트로 만들기 보다는 SDP를 지키며 안정성이 높은 쪽을 의존하게 해야 합니다. (만약에 지켜지지 않게되면 SDP를 적용하기 위해 DIP를 사용할 수 있음)
SAP(안정된 추상화 원칙)
컴포넌트는 안정된 정도만큼만 추상화되어야 한다.
시스템에서는 자주 변경해서는 절대로 안되는 소프트에어도 있습니다. 고수준 아키텍처나 정책 결정과 관련된 (해당 서비스의 정책) 소프트웨어가 그 예 입니다. 이처럼 업무 로직이나 아키텍쳐와 관련된 결정에는 변동성이 없기를 기대합니다. 따라서 시스템에서 고수준 정책을 캡슐화하는 소프트웨어는 반드시 안정된 컴포넌트(I=0)에 위치해야 합니다. 불안정한 컴포넌트 (I=1) 은 반드시 변동성이 큰 소프트웨어, 즉 쉽고 빠르게 변경할 수 있는 소프트웨어만을 포함해야 합니다.
하지만 고수준 정책을 안정된 컴포넌트에 위치시키면, 그 정책을 포함하는 소스 코드는 수정하기가 어려워집니다. 이로 인해 시스템 전체 아키텍쳐가 유연성을 잃을 수 있습니다. 이것을 해결할 수 있는 원칙이 있는데 바로 OCP 입니다. 그렇다면 어떤 클래스가 이 원칙을 준수할 수 있을까요? 바로 추상 클래스(또는 인터페이스) 입니다.
인터페이스나 추상 클래스를 사용하는 이유는 안정적인 컴포넌트라면 반드시 인터페이스와 추상 클래스로 구성되어 쉽게 확장할 수 있어야 하기 때문입니다. 안정된 컴포넌트가 확장이 가능해지면 유연성을 얻게 되고 아키텍처를 과도하게 제약하지 않게 됩니다.
추상화 정도를 측정하는 방법도 존재합니다.
- Nc : 컴포넌트의 클래스 개수
- Na : 컴포넌트의 추상 클래스와 인터페이스 개수
- A : 추상화 정도, (A = Na/Nc)
A지표는 0과 1 사이의 값을 갖습니다. A가 0이면 컴포넌트에는 추상 클래스가 하나도 없다는 뜻입니다. A가 1이면 컴포넌트는 오로지 추상 클래스만을 포함한다는 뜻입니다.

위 그림을 보게 되면 A (추상화 정도)와 I (안정된 정도)의 관계가 나타나 있습니다. 1도 추상적이지 않고 1도 안정되지 않은 지역을 고통의 지역이라고 합니다. 물론, 변경될 일이 없는 모듈의 경우에는 고통스럽지 않지만, 조금이라도 변경이 예상되는 모듈인 경우에는 해당 모듈을 바꿀 때 상당히 고통스럽기 때문입니다.
반면 최고로 추상적이고 최고로 안정된 모듈은 의존관계가 생기지 않기 때문에, 쓸모 없는 지역이라고 합니다. 프로젝트를 하다보면 간혹 쓰지 않는 인터페이스나 추상 클래스가 있는데 그것들을 가리켜 말할 수 있습니다.
그래서 가장 이상적이라고 여기는 구간이 바로 (0,1) 부분과 (1,0) 부분을 이은 선이라고 할 수 있습니다. 저기에 100% 맞게 설계해야한다는 어폐가 있을 수 있고 저기에 근접하게 설계해야 합니다.
우리가 추구해야 할 방향이지만 지표는 신이 아니기 때문에 불완전할 수 있습니다. 즉 이 지표들이 주는 인사이트와 경험을 토대로 더 좋은 컴포넌트를 설계하는것이 중요하다고 할 수 있겠습니다.
댓글 남기기