본문 바로가기
SW

클린 아키텍처:소프트웨어 구조와 설계의 원칙(Clean Architecture) - 4부 : 컴포넌트 원칙 (14장)

by 라꾸스떼(YR) 2020. 3. 8.
반응형

[14장] 컴포넌트 결합

ADP: 의존성 비순환 원칙

컴포넌트 의존성 그래프에 순환이 있어서는 안 된다.

하루 종일 일해서 무언가를 작동하게 만들어 놓고 퇴근했는데, 이튿날 출근해 보면 전혀 돌아가지 않는 경험을 해본 적이 있지 않은가? 왜냐하면 누군가 당신보다 더 늦게까지 일하면서 당신이 의존하고 있던 무언가를 수정했기 때문이다. ‘숙취 증후군’은 많은 개발자가 동일한 소스 파일을 수정하는 환경에서 발생한다. 프로젝트와 개발팀 규모가 커지면 숙취는 지독한 악몽이 될 수도 있다.

2가지 해결책 : ‘주 단위 빌드(weekly build)’, ‘의존성 비순환 원칙(Acyclic Dependencies Principle, ADP)’

 

<주 단위 빌드(Weekly Build)>

먼저 모든 개발자는 일주일의 첫 4일 동안은 서로를 신경 쓰지 않는다. 개발자는 모두 코드를 개인적으로 복사하여 작업하며, 전체적인 기준에서 작업을 어떻게 통합할지는 걱정하지 않는다. 그런 후 금요일이 되면 변경된 코드를 모두 통합하여 시스템을 빌드한다. 단점은 금요일에 통합과 관련된 막대한 업보를 치러야 한다는 사실이다. 안타깝게도 프로젝트가 커지면 프로젝트 통합은 금요일 하루 만에 끝마치는 게 불가능해진다. 개발보다 통합에 드는 시간이 늘어나면서 팀의 효율성도 서서히 나빠진다. 통합과 테스트를 수행하기가 점점 더 어려워지고, 팀은 빠른 피드백이 주는 장점을 잃는다.

 

<순환 의존성 제거하기>

이 문제의 해결책은 개발 환경을 릴리스 가능한 컴포넌트 단위로 분리하는 것이다. 이를 통해 컴포넌트는 개별 개발자 또는 단일 개발팀이 책임질 수 있는 작업 단위가 된다. 개발자가 해당 컴포넌트가 동작하도록 만든 후, 해당 컴포넌트를 릴리스하여 다른 개발자가 사용할 수 있도록 만든다. 개발자는 자신만의 공간에서 해당 컴포넌트를 지속적으로 수정한다. 나머지 개발자는 릴리스된 버전을 사용한다. 새 릴리스를 적용할 준비가 되었다는 판단이 들면 새 릴리스를 사용하기 시작한다. 통합은 작고 점진적으로 이뤄진다. 특정시점에 모든 개발자가 한데 모여서 진행 중인 작업을 모두 통합하는 일은 사라진다. 이 절차가 성공적으로 동작하려면 컴포넌트 사이의 의존성 구조를 반드시 관리해야 한다. 중요한 점은 컴포넌트 간의 의존성 구조다. 이 구조가 방향 그래프(directed graph)임에 주의하자. 컴포넌트는 정점(vertex)에 해당하고, 의존성 관계는 방향이 있는 간선(directed edge)에 해당한다. 어느 컴포넌트에서 시작하더라도, 의존성 관계를 따라가면서 최초의 컴포넌트로 되돌아갈 수 없다는 사실이다. 이 구조에는 순환이 없다. 즉, 이 구조는 비순환 방향 그래프(Direted Acyclic Graph, DAG)다.

시스템 전체를 릴리스해야 할 때가 오면 릴리스 절차는 상향식으로 진행된다. 먼저 entities 컴포넌트를 컴파일하고, 테스트하고, 릴리스한다.

 

<순환이 컴포넌트 의존성 그래프에 미치는 영향>

의존성 그래프에 순환이 생기면 컴포넌트를 어떤 순서로 빌드해야 올바를지 파악하기가 상당히 힘들어진다. 사실 순환이 생기면 올바른 순서라는 것 자체가 없을 수 있다.

 

<순환 끊기>

1.의존성 역전 원칙(DIP)을 적용한다.

2.entities와 aucthorizer가 모두 의존하는 새로운 컴포넌트를 만든다. 그리고 두 컴포넌트가 모두 의존하는 클래스들을 새로운 컴포넌트로 이동시킨다.

 

<흐트러짐(Jitters)>

요구사항이 변경되면 컴포넌트 구조도 변경될 수 있다는 사실이다. 애플리케이션이 성장함에 따라 컴포넌트 의존성 구조는 서서히 흐트러지며 또 성장한다. 따라서 의존성 구조에 순환이 발생하는지를 항상 관찰해야 한다.

 

<하향식(top-down) 설계>

즉, 컴포넌트 구조는 하향식으로 설계될 수 없다. 컴포넌트는 시스템에서 가장 먼저 설계할 수 있는 대상이 아니며, 오히려 시스템이 성장하고 변경될 때 함께 진화한다.

컴포넌트 의존성 다이어그램은 애플리케이션의 빌드 가능성과 요주보수성을 보여주는 지도와 같다. 컴포넌트 구조는 프로젝트 초기에 설계할 수 없다. 구현과 설계가 이뤄지는 프로젝트 초기에 모듈들이 점차 쌓이기 시작하면, ‘숙취 증후군’을 겪지 않고 프로젝트를 개발하기 위해서 의존성 관리에 대한 요구가 점차 늘어나게 된다. 뿐만 아니라 변경되는 범위가 시스템의 가능한 한 작은 일부로 한정되기를 원한다.

의존성 구조와 관련된 최우선 관심사는 변동성을 격리하는 일이다. 우리는 변덕스러운 이유로 자주 변경되는 컴포넌트로 인해, 그렇지 않았다면 안정적이었을 컴포넌트가 영향받는 일을 원치 않는다. 결국 컴포넌트 의존성 그래프는 자주변경되는 컴포넌트로부터 안정적이며 가치가 높은 컴포넌트를 보호하려는 아키텍트가 만들고 가다듬게 된다.

애플리케이션이 계속 성장함에 따라 우리는 재사용 가능한 요소를 만드는 일에 관심을 기울이기 시작한다. 이 시점이 되면 컴포넌트를 조합하는 과정에 공통 재사용 원칙(CRP)이 영향을 미치기 시작한다.

 

<SDP: 안정된 의존성 원칙>

안정성의 바향으로(더 안정된 쪽에) 의존하라.

설계는 결코 정적일 수 없다. 설계를 유지하다 보면 변경은 불가피하다.

변경이 쉽지 않은 컴포넌트가 변동이 예상되는 컴포넌트에 의존하게 만들어서는 절대로 안 된다. 한번 의존하게 되면 변동성이 큰 컴포넌트도 결국 변경이 어려워진다.

 

<안정성>

컴포넌트 안쪽으로 들어오는 의존성이 많아지면 상당히 안정적이라고 볼 수 있는데, 사소한 변경이라도 의존하는 모든 컴포넌트를 만족시키면서 변경하려면 상당한 노력이 들기 때문이다.

X는 안정된 컴포넌트다. 새 컴포넌트가 X에 의존하며, 따라서 X 컴포넌트는 변경하지 말아야 할 이유가 세가지나 되기 때문이다. 이경우 X는 세 컴포넌트를 책임진다라고 말한다. 반대로 X는 어디에도 의존하지 않으므로 X가 변경되도록 만들 수 있는 외적인 영향이 전혀 없다. 이 경우 X는 독립적이다라고 말한다.

Y는 상당히 불안정한 컴포넌트다. 어떤 컴포넌트도 Y에 의존하지 않으므로 Y는 책임성이 없다고 말할 수 있다. 또한 Y는 세 개의 컴포넌트에 의존하므로 변경이 발생할 수 있는 외부 요인이 세 가지다. 이 경우 Y는 의존적이라고 말한다.

 

<안정성 지표>

-Fan-in:안으로 들어오는 의존성

-Fan-out:바깥으로 나가는 의존성

-I(불안정성): I=Fan-out/(Fan-in+Fan-out).

I=0 이면 최고로 안정된 컴포넌트

SDP에서 컴포넌트의 I 지표는 그 컴포넌트가 의존하는 다른 컴포넌트들의 I보다 커야 한다고 말한다.

 

<모든 컴포넌트가 안정적이어야 하는 것은 아니다>

모든 컴포넌트가 최고로 안정적인 시스템이라면 변경이 불가능하다. 이는 바람직한 상황이 아니다.

위쪽에는 변경 가능한 컴포넌트가 보이고, 아래의 안정된 컴포넌트에 의존한다.

Flexible은 변경하기 쉽도록 설계한 컴포넌트다. 우리는 Flexible이 불안정한 상태이기를 바란다.

 

<추상 컴포넌트>

오로지 인터페이스만을 포함하는 컴포넌트. 정적 타입 언어를 사용할 때 꼭 필요한 전략. 추상 컴포넌트는 상당히 안정적이며, 따라서 덜 안정적인 컴포넌트가 의존할 수 있는 이상적인 대상이다.

 

<SAP: 안정된 추상화 원칙>

컴포넌트는 안정된 정도만큼만 추상화되어야 한다.

 

<고수준 정책을 어디에 위치시켜야 하는가?>

시스템에서 고수준 정책을 캡슐화하는 소프트웨어는 반드시 안정된 컴포넌트(I=0)에 위치해야 한다. 불안정한 컴포넌트(I=1)는 반드시 변동성이 큰 소프트웨어, 즉 쉽고 빠르게 변경할 수 있는 소프트웨어만을 포함해야 한다. 하지만 고수준 정책을 안정된 컴포넌트에 위치시키면, 그 정책을 포함하는 소스 코드는 수정하기가 어려워진다. 이로 인해 시스템 전체 아키텍처가 유연성을 잃는다. OCP에서는 클래스를 수정하지 않고도 확장이 충분히 가능할 정도로 클래스를 유연하게 만들 수 있을 뿐만 아니라 바람직한 방식. 추상 클래스

 

<안정된 추상화 원칙>

안정된 추상화 원칙(Stable Abstractions Principle, SAP)은 안정성과 추상화 정도 사이의 관계를 정의한다. 이 원칙은 한편으로는 안정된 컴포넌트는 추상 컴포넌트여야 하며, 이를 통해 안정성이 컴포넌트를 확장하는 일을 방해해서는 안 된다고 말한다. 다른 한편으로는 불안정한 컴포넌트는 반드시 구체 컴포넌트여야 한다고 말하는데, 컴포넌트가 불안정하므로 컴포넌트 내부의 구체적인 코드를 쉽게 변경할 수 있어야 하기 때문이다. 따라서 안정적인 컴포넌트라면 반드시 인터페이스와 추상 클래스로 구성되어 쉽게 확장할 수 있어야 한다.

 

<추상화 정도 측정하기>

<주계열>

<배제 구역 벗어나기>

<주계열과의 거리>

 

[결론]

의존성 관리 지표는 설계의 의존성과 추상화 정도가 내가 ‘훌륭한’ 패턴이라고 생각하는 수준에 얼마나 잘 부합하는지를 측정한다. 하지만 지표는 신이 아니다. 지표는 그저 임의로 결정된 표준을 기초로 한 측정값에 지나지 않는다. 이러한 지표는 아무리 해도 불완전히다. 하지만 이들 지표로부터 무언가 유용한 것을 찾을 수 있기를 바란다.

반응형

댓글