본문 바로가기
Binary Book Club/소프트웨어 아키텍처 101

[소프트웨어 아키텍처 101] Ch.3 모듈

by ♡˖GYURI˖♡ 2024. 1. 5.
소프트웨어 아키텍처 용어의 95%는 '모듈성'의 이로움을 찬양하는 데 사용되고 있지만, 정작 모듈성을 어떻게 달성할지에 대해서는 별다른 얘기가 없다.
- 글렌포드 J. 마이어스, 1978년

 

플랫폼마다 제공하는 코드 재사용 메커니즘은 제각각이지만, 연관된 코드를 '모듈'로 묶는 방법은 모두 지원한다. 본인이 선택한 개발 플랫폼에서 모듈성과 그것을 구현한 수많은 코드를 이해하는 것은 아키텍트에게 대단히 중요한 일이다. 우리가 아키텍처를 분석해야 할 (메트릭, 피트니스 함수, 시각화 등) 많은 도구가 바로 이 모듈성에 기반하기 때문이다.

 

모듈성은 일종의 구성 원리이다. 아키텍트가 대충 아무렇게나 조각들을 이어 붙여 시스템을 설계하면 무수한 난관에 봉착해 옴짝달싹 못 하는 시스템이 될 것이다. 물리학에 비유하자면, 소프트웨어 시스템은 엔트로피가 증가하는 방향으로 움직이는 복잡한 시스템을 모델링한다. 질서를 유지하려면 물리적 시스템에 에너지를 투입해야 하는데, 소프트웨어 시스템도 마찬가지이다. 아키텍트는 끊임없이 에너지를 소비해서 시스템을 구조적으로 탄탄하게 유지해야 한다.

 

 

1. 정의

사전에서 모듈을 찾아보면, '복잡한 구조를 만드는 데 쓰이는 각각의 표준화한 부품이나 독립적인 단위'라고 나온다. 우리는 모듈성을 이용해 객체 지향 언어의 클래스나 함수형 언어의 함수가 될 만한 서로 연관된 코드를 논리적으로 묶는다. 프로그래밍 언어는 대부분 모듈성 메커니즘을 제공하며, 개발자는 보통 연관된 코드를 함께 묶는 수단으로 모듈을 사용한다. (ex. 자바 패키지 com.mycompany.customer에는 고객에는 관한 것들을 담는 식)

 

아키텍트는 개발자가 코드를 어떻게 패키징하는지 반드시 알아야 한다. 아키텍처에 중요한 영향을 미치기 때문이다. 여러 패키지가 서로 단단히 커플링되어 있으면 그 중 하나를 다른 작업에 재사용하기가 아주 어려워진다. 

 

네임스페이스
: 개체를 구분할 수 있는 범위를 나타내는 말
: 일반적으로 하나의 이름 공간에서는 하나의 이름이 단 하나의 개체만을 가리키게 된다.

 

우리는 아키텍처를 논할 때 클래스, 함수처럼 코드를 묶어 놓은 덩어리를 모듈성이라는 일반 용어로 나타낸다. 이것은 논리적인 구분이지 물리적인 구분은 아닌데, 이 차이점이 굉장히 중요한 경우가 있다. 예를 들어, 모놀리식 애플리케이션은 편의상 꽤 많은 클래스를 한 덩이로 묶어도 크게 상관없지만, 아키텍처를 재구축할 때에는 이렇게 커플링된 구조가 모놀리스를 나누는 데 걸림돌이 된다.

 

따라서 모듈성은 특정 플랫폼에(서) 함축되어 있거나 불가피한 물리적인 분리와 다른 개념으로 바라보는 게 좋다.

 

 

2. 모듈성 측정

응집

  • 한 모듈의 파트(구성 요소)가 동일한 모듈 안에 얼마나 포함되어 있는지를 나타낸다.
  • 이상적으로 응집된 모듈이라면 모든 파트가 함께 패키징되어 있을 것이다.
응집된 모듈을 나누려고 해봐야 더 커플링되고 가독성은 떨어진다.
- 래리 콘스탄틴

 

1) 기능적 응집

모듈의 각 파트는 다른 파트와 연관되어 있고 기능상 꼭 필요한 모든 것이 모듈에 들어있다.

 

2) 순차적 응집

두 모듈이, 한쪽이 데이터를 출력하면 다른 한쪽이 그것을 입력 받는 형태로 상호작용한다.

 

3) 소통적 응집

두 모듈이, 각자 정보에 따라 작동하고(거나) 어떤 출력을 내는 형태로 통신 체인을 형성한다.

ex) 데이터베이스에 레코드를 추가하면 그 정보에 따라 이메일이 만들어지는 식

 

4) 절차적 응집

두 모듈은 정해진 순서대로 실행되어야 한다.

 

5) 일시적 응집

모듈은 시점 의존성에 따라 연관된다. 예를 들어, 많은 시스템들이 시동할 때 그다지 관련이 없어 보이는 것들을 죽 초기화하는 경우가 많은데, 이런 작업들이 일시적으로 응집되었다고 할 수 있다.

 

6) 논리적 응집

모듈의 내부 데이터는 기능적이 아니라, 논리적으로 연관되어 있다. 이를테면, 텍스트, 직렬화 객체, 스트림 형태로 받은 데이터를 변환하는 모듈이 그렇다. 서로 연관된 작업들이지만 하는 일은 전혀 다르다. 이런 종류의 응집의 좋은 예는 거의 모든 자바 프로젝트에서 사용되는 StringUtils 패키지에서 찾아볼 수 있다. 이 패키지에는 각기 다른 작업을 수행하는 정적 메서드가 많이 있지만 서로 연관성은 없다.

 

7) 동시적 응집

같은 소스 파일에 모듈 구성 요소가 들어 있지만 그 외에는 아무 연관성도 없다. 이는 가장 좋지 않은 형태의 응집이다.

 

LCOM

공유 필드를 통해 공유되지 않는 메서드의 총 갯수

  • 클래스 X : LCOM 점수가 낮고 구조적 응집이 우수함
  • 클래스 Y : 응집 결여, 세 쌍의 필드/메서드는 각자 자기 클래스에 두어도 상관이 없을 듯 함
  • 클래스 Z : 응집이 조합된 모양새, 마지막 필드/메서드 쌍은 자체 클래스로 빼내도 됨

LCOM 메트릭을 활용하면 어쩔 수 없이 커플링된 클래스, 처음부터 한 클래스가 아니었던 클래스를 발견하는데 도움이 된다.

 

커플링

  • 구심 커플링 : 코드 아티팩트로 유입되는 접속 수
  • 원심 커플링 : 다른 코드 아티팩트로 유출되는 접속 수

 

추상도

구현 대비 추상화 정도

 

불안정도

원심 커플링 / (구심 커플링 + 원심 커플링)

 

메인 시퀀스로부터의 거리

 

메인 시퀀스로부터의 거리는 추상도와 불안정도 사이의 이상적인 관계를 나타낸다. 예를 들어 아래의 그래프에서 클래스의 메인 시퀀스로부터의 거리는 D이다.

 

위의 그래프에서 개발자는 후보 클래스를 그려보고 이상적인 선에서 얼마나 떨어져 있는지 거리를 잰다. 이 선에 가까울 수록 클래스 균형이 잘 맞다는 방증이다. 오른쪽 위로 치우친 부분을 쓸모없는 구역(zone of uselessness, 너무 추상화를 많이 해서 사용하기 어려운 코드), 반대로 왼쪽 아래로 치우친 부분을 고통스런 구역(zone of pain, 추상화를 거의 안 하고 구현 코드만 잔뜩 넣어 취약하고 관리하기 힘든 코드)이라고 한다.

커네이선스

  • 변화 종속성
  • 어느 한쪽을 바꾸면 다른 쪽도 함께 바꾸어야 저네적으로 정합성이 유지되는 상태
두 커모넌트 중 한쪽이 변경될 경우 다른 쪽도 변경해야 전체 시스템의 정합성이 맞는다면 이들은 커네이선스를 갖고 있는 것이다.
- 밀러 페이지-존스

 

 

1) 정적 커네이선스

  • 명칭 커네이선스 : 여러 컴포넌트의 엔티티명이 일치해야 함
  • 타입 커네이선스 : 여러 컴포넌트의 엔티티 타입이 일치해야 함
  • 의미(관례) 커네이선스 : 여러 컴포넌트에 걸쳐 어떤 값의 의미가 일치해야 함
  • 위치 커네이선스 : 여러 컴포넌트는 값의 순서가 일치해야 함
  • 알고리즘 커네이선스 : 여러 컴포넌트는 특정 알고리즘이 일치해야 함

 

2) 동적 커네이선스

  • 실행 커네이선스 : 여러 컴포넌트의 실행 순서가 중요함
  • 시점 커네이선스 : 여러 컴포넌트의 실행 시점이 중요함
  • 커네이선스 : 상호 연관된 다수의 값들을 함께 변경할 때 발생함
  • 식별 커네이선스 : 여러 컴포넌트가 동일한 엔티티를 ㅏㅁ조할 때 발생함

 

런타임 호출은 호출 그래프에 비해 효과적인 분석 도구가 많지 않아 아키텍트는 동적 커네이선스를 파악하기가 쉽지 않다.

 

 

커네이선스 속성

강도

아키텍트는 개발자가 어떤 유형의 커네이선스를 얼마나 쉽게 리팩터링 할 수 있는지에 따라 커네이선스 강도(strength)를 결정한다. 아래 그림처럼, 다양한 유형의 커네이선스가 더 바람직하다. 아키텍트와 개발자는 더 나은 유형의 커네이선스를 리팩터링해서 코드베이스의 커필링 특성을 개선할 수 있다.

지역성

커네이선스의 지역성(locality)은 코드베이스의 모듈들이 서로 얼마나 가까이 있는가 이다. (동일한 모듈에서) 근접한 코드는 보통 (별개의 모듈 또는 코드베이스로) 더 분리된 코드보다 높은 형태의 커네이선스를 가진다. 즉, 모듈을 서로 떨어뜨렸을 때 커플링이 형편없는 형태의 커네이선스는 모듈을 서로 가까이 붙여 놓는 식으로 개선할 수 있다. 만약, 동일한 컴포넌트에 있는 두 클래스가 의미 커네이선스를 갖고 있다면, 두 컴포넌트가 동일한 형태의 커네이선스를 갖고 있을 때보다 코드베이스에 덜 해로울 것이다.

 

정도

커네이선스 정도(degree)는 커네이선스가 미치는 영향의 규모(소수의 클래스에 영향을 미치는가, 아니면 수 많은 클래스에 영향을 미치는가)에 관한 것이다. 이 값이 작을수록 코드베이스 입장에서 바람직하다. 다음은 페이지-존스가 제시한, 커네이선스를 이용해 시스템의 모듈성을 개선하는 세 가지 방법이다.

  1. 시스템을 캡슐화한 요소들로 잘게 나누어 전체 커네이선스를 최소화한다.
  2. 캡슐화 경계를 벗어나는 나머지 커네이선스를 모조리 최소화한다.
  3. 캡슐화 경계 내부에서 커네이선스를 최대화한다.

 

1990년대 커네이선스의 문제점

  • 이들 메트릭은 아키텍처 구조보다는 저수준 코드의 세부분을, 코드 품질 및 정리 상태 위주로 관찰함
  • 커네이선스 자체는 요즘 아키텍트가 내려야 할 근본적인 결정에 관한 문제는 다루지 않음