넘치게 채우기

5-5. 정책과 수준 본문

개발/Clean Architecture

5-5. 정책과 수준

riveroverflow 2023. 11. 26. 14:34
728x90
반응형

소프트웨어 시스템이란, 정책을 기술한 것이다.

실제로 컴퓨터 프로그램의 핵심부는 이게 전부다.

컴퓨터 프로그램은 각 입력을 출력으로 변환하는 정책을 상세하게 기술한 설명서다.

소프트웨어 아키텍처를 개발하는 기술에는 이러한 정책을 신중하게 분리하고, 정책이 변경되는 양상에 따라 정책을 재편성하는 일도 포함된다.

 

동일한 이유로 동일한 시점에 변경되는 정책은 동일한 수준에 위치하며, 동일한 컴포넌트에 속해야 한다.

서로 다른 이유로, 혹은 다른 시점에 변경되는 정책은 다른 수준에 위치하며, 반드시 다른 컴포넌트로 분리해야 한다.

흔히 아키텍처 개발은 재편성된 컴포넌트들을 비순환 방향 그래프(DAG)로 구성하는 기술을 포함한다.

그래프에서 정점(node)은 동일한 수준의 정책을 포함하는 컴포넌트에 해당한다.

방향이 있는 간성(edge)는 컴포넌트 사이의 의존성을 나타낸다.

간선은 다른 수준에 위치한 컴포넌트를 서로 연결한다.

이러한 의존성은 소스 코드, 컴파일타임의 의존성이다.

 

좋은 아키텍처라면 각 컴포넌트를 연결할 때 의존성의 방향이 컴포넌트의 수준을 기반으로 연결되도록 만들어야 한다.

즉, 저수준 컴포넌트가 고수준 컴포넌트에 의존하도록 설계되어야 한다.

 

수준

수준을 엄밀하게 정의하자면 입력과 출력까지의 거리다.

시스템의 입력과 출력 모두로부터 멀리 위치할수록 정책의 수준은 높아진다.

입력과 출력을 다루는 정책이라면 시스템에서 최하위 수준에 위치한다.

 

그림 19.1의 데이터 흐름도는 간단한 암호화 프로그램을 그린 것으로, 입력 장치에서 문자를 읽어서,

테이블을 참조하여 문자를 번역한 후, 번역된 문자를 출력 장치로 기록한다.

데이터 흐름은 굽은 실선 화살표로 표시했다.

프로그램을 제대로 설계했다면 소스 코드 의존성은 곧은 점선처럼 표시되어야 한다.

 

그림19.1: 간단한 암호화 프로그램

 

 

번역 컴포넌트는 이 시스템에서 최고 수준의 컴포넌트인데, 입력과 출력에서부터 가장 멀리 떨어져 있기 때문이다.

주목할 점은 데이터 흐름과 소스 코드 의존성이 항상 같은 방향을 가리키지는 않는다는 사실이다.

소스 코드 의존성은 그 수준에 따라 결합되어야 하며, 데이터 흐름을 기준으로 결합되어서는 안된다.

자칫하면 잘못된 아키텍처가 만들어지는데, 예를 들어 암호화 프로그램을 다음처럼 작성한다면 그렇게 된다.

 

function encrypt() {
    while(true)
    writeChar(translate(readChar()));
}

이는 잘못되었다.

고수준인 encrypt 함수가 저수준인 readCharwriteChar함수에 의존하기 때문이다.

그림 19.2의 클래스 다이어그램은 이 시스템의 아키텍처를 개선해본 모습니다. 주목할 점은 Encrypt 클래스,, CharWriter와 CharReader인터페이스를 둘러싸고 있는 점선으로 된 경계다.

이 경계를 횡단하는 의존성은 모두 경계 안쪽으로 향한다. 이 경계로 묶인 영역이 이 시스템에서 최고 수준의 구성요소다.

 

 

그림 19.2: 시스템의 더 나은 아키텍처를 보여주는 클래스 다이어그램

 

여기에서 ConsoleReader와 ConsoleWriter는 클래스로 표현했다.

이들 클래스는 입력과 출력에 가깝기 때문에 저수준이다.

이 구조에서 고수준의 암호화 정책을 저수준의 입력/출력 정책으로부터 분리시킨 방식에 주목하자.

이 방식 덕분에 이 암호화 정책을 더 넓은 맥락에서 사용할 수 있다.

입력과 출력에 변화가 생기더라도 암호화 정책은 거의 영향을 받지 않기 때문이다.

정책을 컴포넌트로 묶는 기준은 정책이 변경되는 방식에 달려있다는 사실을 상기하자.

 

단일 책임 원칙(SRP)와 공통 폐쇄 원칙(CCP)에 따르면 동일한 이유로 동일한 시점에 변경되는 정책은 함께 묶인다.

고수준 정책, 즉 입력과 출력에서부터 멀리 떨어진 정책은 저수준 정책에 비해 덜 빈번하게 변경되고, 보다 중요한 이유로 변경되는 경향이 있다.

저수준 정책, 즉 입력과 출력에 가까이 위치한 정책은 더 빈번하게 변경되며, 보다 긴급성을 요하며, 덜 중요한 이유로 변경되는 경향이 있다.

이처럼 모든 소스 코드 의존성의 방향이 고수준 정책을 향할 수 있도록 정책을 분리했다면, 변경의 영향도를 줄일 수 있다.

시스템의 최저 수준에서 중요하지 않지만 긴급한 변경이 발생하더라도, 보다 높은 위치의 중요한 수준에 미치는 영향은 거의 없게 된다.

 

이 논의는 저수준 컴포넌트가 고수준 컴포넌트에 플러그인되어야 한다는 관점으로 바라볼 수도 있다.

그림 19.3의 컴포넌트 다이어그램이 이러한 배치를 보여준다.

Encryption 컴포넌트는 IO Devices 컴포넌트를 전혀 알지 못한다.

반면 IO Devices는 Encryption 컴포넌트에 의존적이다.

 

그림 19.3: 저수준 컴포넌트는 고수준 컴포넌트에 플러그인되어야 한다.

 

 

결론

이 장에서 설명한 정책에 대한 논의는 SOLID 원칙을 모두 포함한다.

이 원칙들의 설명을 다시 읽어 보며 각 원칙이 어디에서 무슨 이유로 사용되었는지를 살펴보자.

728x90
반응형

'개발 > Clean Architecture' 카테고리의 다른 글

5-7. 소리치는 아키텍처  (0) 2023.11.28
5-6. 업무 규칙  (0) 2023.11.27
5-4. 경계 해부학  (0) 2023.11.24
5-3: 경계: 선 긋기  (0) 2023.11.22
5-2. 독립성  (0) 2023.11.20