도은
- 플랫폼마다 제공하는 코드 재사용 메커니즘은 제각각이지만, 연관된 코드를 모듈로 묶는 방법은 모두 지원한다.
- 모듈성은 사실 제대로 정의하기 쉽지 않은 개념
- 공인된 정의는 따로 없으니 이 책에서는 나름대로 정의 내려보려고 한다.
💡 본인이 선택한 개발 플랫폼에서 모듈성과 그것을 구현한 수많은 코드를 이해하는 것은 아키텍트에게 대단히 중요한 일
- 아키텍트가 대충 아무렇게나 조각들을 이어 붙여 시스템을 설계하면 무수한 난관에 봉착해 움짝달싹 못 하는 시스템이 되어버린다.
🤔 생각) 책에서 말하는 '움짝달싹 못 하는 시스템'에 대해 그려지기도 한다.
- 응집도가 낮거나 결합도가 높은 코드는 시스템의 유지보수에 어려움을 만드는 것 같다. (사실 시스템이 아니여도 여느 코드에서도 마찬가지긴 하다)
- 응집도가 낮으면, 관련된 기능이 여러 곳에 흩어져 있어 유지보수 시에도 비효율적일 수 있고 놓치는 부분이 생길 수 있다.
- 결합도가 높으면, 변경 사항에 대한 이펙트 걱정이 태산이 된다. 기능이 변경되거나 추가되었을 때 깨질 가능성이 있다면 모듈화 하지 않는게 나을 수 있다고 생각한다.
정답은 없는 것 같다.. 개인적으로는
- 어느 정도의 반복과 풀어쓰는 것에 대해 긍정적으로 생각한다.
- 오히려 예민하게 모듈화하는 것은 코드 흐름 파악에 부정적인 것 같고, 무언가 추가될 여지가 있다면 더욱 고민할 필요가 있다. 뭔가가 추가되었을 때 깨질 코드라면 모듈화하지 않는 게 좋은 것 같다.
3.1 정의
- 사전에서 모듈을 찾아보면, '복잡한 구조를 만드는 데 쓰이는 각각의 표준화환 부품이나 독립적인 단위'라고 나온다.
- 우리는 모듈성을 이용해, 함수가 될 만한 서로 연관된 코드를 논리적으로 묶는다.
3.2 모듈성 측정
- 세 가지 핵심 개념인 응집, 커플링, 커네이선스를 집중적으로 살펴보자.
응집
- 응집은 한 모듈의 구성 요소가 동일한 모듈 안에 얼마나 포함되어 있는지를 나타낸다.
- 기능적 응집: 모듈의 각 파트는 다른 파트와 연관되어 있고 기능상 꼭 필요한 모든 것이 모듈에 들어있다.
- 순차적 응집: 두 모듈이, 한쪽이 데이터를 출력하면 다른 한쪽이 그것을 입력 받는 형태로 상호작용한다.
- 소통적 응집: 두 모듈이, 각자 정보에 따라 작동하거나 어떤 출력을 내는 형태로 통신 체인을 형성한다.
- 절차적 응집: 두 모듈은 정해진 순서대로 실행되어야 한다.
- 일시적 응집: 모듈은 시점 의존성에 따라 연관된다. 예를 들어, 많은 시스템들이 시동할 대 그다지 관련이 없어 보이는 것들을 초기화하는 경우가 많은데, 이런 작업들이 일시적으로 응지됐다고 할 수 있다.
- 논리적 응집: 모듈의 내부 데이터는 기능적이 아니라, 논리적으로 연관되어 있다. 이를 테면, 데이터를 변환하는 모듈이 그렇다. 서로 연관된 작업들이지만, 하는 일은 전혀 다르다.
- 동시적 응집: 같은 소스 파일에 모듈 구성 요소가 들어 있지만, 그 외에에는 아무 연관성도 없다. 이는 가장 좋지 않은 형태의 응집이다.
💡 어떻게 분리하냐는 아키텍트의 업무의 핵심이라고 할 수 있는 트레이드오프 분석이다.
- 카이댐버와 케메러가 개발한 '메서드의 응집 결여도(LOCM) (opens in a new tab)'은 모듈의 구조적 응집도를 나타낸다.
- LOCM 메트릭은 아키텍처 스타일을 전환하기 위해 코드베이스를 분석하는 아키텍트에게 매우 유용하다.
- LOCM 메트릭을 활용하면 어쩔 수 없이 커플링된 클래스, 처음부터 한 클래스가 아니었던 클래슬르 발견하는 데 도움이 된다.
추상도, 불안정도
- 추상화를 전혀 하지 않고 하나의 엄청나게 큰 함수 코드가 있는 코드베이스도 있고, 반대로 너무 지나치게 추상화해서 코드가 서로 어떻게 연관되어 있는지 파악하기 어려운 코드베이스도 있다.
- 불안정도는 코드베이스의 변동성을 의미한다. 불안정도가 높은 코드베이스는 변경 시 커플링이 높아 더 깨지기 쉽다.
메인 시퀀스로부터의 거리
- 불안정도와 추상도를 이용해 계산한다.
- 이상적인 선에 가까운 클래스는 서로 경쟁적인 두 메트릭의 건전한 조합이라고 볼 수 있다.
- 오른쪽 위로 치우친 부분을 쓸모없는 구역(즉, 너무 추상화를 해서 사용하기 어려운 코드)
- 왼쪽 아래로 치우친 부분을 고통스런 구역(즉, 추상화를 거의 안 하고 구현 코드만 잔뜩 넣어 취약하고 관리하기 힘든 코드)
커네이선스
- 두 컴포넌트 중 한쪽이 변경될 경우 다른 쪽도 변경해야 전체 시스템의 정합성이 맞는다면 이들은 커네이선스를 갖고 있는 것이다.
정적 커네이선스 (소스 코드 레벨의 커플링)
- 명칭 커네이선스
- 여러 컴포넌트의 엔티티명(개념의 명칭)이 일치해야 한다.
- 타입 커네이선스
- 여러 컴포넌트의 엔티티 타입이 일치해야 한다.
- 의미 커네이선스와 관례 커네이선스
- 여러 컴포넌트에 걸쳐 어떤 값의 의미가 일치해야 한다. (켄벤션?)
- 위치 커네이선스
- 여러 컴포넌트는 값의 순서가 일치해야 한다.
- 메서드와 함수 호출 시 전달되는 매개변수 값의 순서가 맞아야 한다. (이 순서의 의미가 복잡해진다면 객체로 key, value 형태를 사용하는 것도 방법이겠다)
- 알고리즘 커네이선스
- 여러 컴포넌트는 특정 알고리즘이 일치해야 한다.
- 동일한 형태의 결과를 내야 한다.
동적 커네이선스 (런타임 호출을 분석)
- 실행 커네이선스
- 여러 컴포넌트의 실행 순서가 중요하다.
- 시점 커네이선스
- 여러 컴폰너트의 실행 시점이 중요하다.
- 값 커네이선스
- 상호 연관된 다수의 값들을 함께 변경할 때 발생한다.
- 식별 커네이선스
- 여러 컴폰너트가 동일한 엔티티를 참조할 떄 발생한다.
커네이선스 속성
- 강도
- 어떤 유형의 커네이선스를 얼마나 쉽게 리팩터링할 수 있느지에 따라 커네이선스 강도를 결정한다.
- 정적 커네이선스는 소스 코드를 분석하거나 최신 도구를 활용하면 어렵잖게 개선할 수 있다.
-
지역성
- 커네이선스의 지역성은 코드베이스의 모듈들이 서로 얼마나 가까이 있는가
- 동일한 모듈에서 근접한 코드는 보통 더 분리된 코드보다 높은 형태의 커네이선스를 가진다.
- 강도와 지역성을 함께 고민해야 한다.
- 동일한 모듈에서 더 강한 형태의 커네이선스가 발견된다면 그와 동일한 커네이선스가 널리 흩어져 있는 것보다는 코드 스멜이 덜 하다는 증거다.
-
정도
- 커네이선스 정도는 커네이선스가 미치는 영향의 규모에 관한 것
- 이 값이 작을수록 코드베이스 입장에서는 바람직
- 모듈이 몇 개 안 된다면 동적 커네이선스가 높아도 별로 해롭지 않지만, 일반적으로 코드베이스는 점점 커지기 마련이니 사소한 문제도 점점 더 악화될 것
- 페이지-존스가 제시한 커네이선스를 이용해 시스템의 모듈성을 개선하는 방법
- 시스템을 캡슐화한 요소들로 잘게 나누어 전체 커네이선스를 최소화
- 캡슐화 경계를 벗어나는 나머지 커네이선스를 모조리 최소화
- 캡슐화 경계 내부에서 커네이선스를 최대화