반디북
소프트웨어 아키텍처 101
Ch3
도은
  • 플랫폼마다 제공하는 코드 재사용 메커니즘은 제각각이지만, 연관된 코드를 모듈로 묶는 방법은 모두 지원한다.
  • 모듈성은 사실 제대로 정의하기 쉽지 않은 개념
  • 공인된 정의는 따로 없으니 이 책에서는 나름대로 정의 내려보려고 한다.
💡 본인이 선택한 개발 플랫폼에서 모듈성과 그것을 구현한 수많은 코드를 이해하는 것은 아키텍트에게 대단히 중요한 일
  • 아키텍트가 대충 아무렇게나 조각들을 이어 붙여 시스템을 설계하면 무수한 난관에 봉착해 움짝달싹 못 하는 시스템이 되어버린다.
🤔 생각) 책에서 말하는 '움짝달싹 못 하는 시스템'에 대해 그려지기도 한다.
    - 응집도가 낮거나 결합도가 높은 코드는 시스템의 유지보수에 어려움을 만드는 것 같다. (사실 시스템이 아니여도 여느 코드에서도 마찬가지긴 하다)
    - 응집도가 낮으면, 관련된 기능이 여러 곳에 흩어져 있어 유지보수 시에도 비효율적일 수 있고 놓치는 부분이 생길 수 있다.
    - 결합도가 높으면, 변경 사항에 대한 이펙트 걱정이 태산이 된다. 기능이 변경되거나 추가되었을 때 깨질 가능성이 있다면 모듈화 하지 않는게 나을 수 있다고 생각한다.

    정답은 없는 것 같다.. 개인적으로는
    - 어느 정도의 반복과 풀어쓰는 것에 대해 긍정적으로 생각한다.
    - 오히려 예민하게 모듈화하는 것은 코드 흐름 파악에 부정적인 것 같고, 무언가 추가될 여지가 있다면 더욱 고민할 필요가 있다. 뭔가가 추가되었을 때 깨질 코드라면 모듈화하지 않는 게 좋은 것 같다.

3.1 정의

  • 사전에서 모듈을 찾아보면, '복잡한 구조를 만드는 데 쓰이는 각각의 표준화환 부품이나 독립적인 단위'라고 나온다.
  • 우리는 모듈성을 이용해, 함수가 될 만한 서로 연관된 코드를 논리적으로 묶는다.

3.2 모듈성 측정

  • 세 가지 핵심 개념인 응집, 커플링, 커네이선스를 집중적으로 살펴보자.

응집

  • 응집은 한 모듈의 구성 요소가 동일한 모듈 안에 얼마나 포함되어 있는지를 나타낸다.
  • 기능적 응집: 모듈의 각 파트는 다른 파트와 연관되어 있고 기능상 꼭 필요한 모든 것이 모듈에 들어있다.
  • 순차적 응집: 두 모듈이, 한쪽이 데이터를 출력하면 다른 한쪽이 그것을 입력 받는 형태로 상호작용한다.
  • 소통적 응집: 두 모듈이, 각자 정보에 따라 작동하거나 어떤 출력을 내는 형태로 통신 체인을 형성한다.
  • 절차적 응집: 두 모듈은 정해진 순서대로 실행되어야 한다.
  • 일시적 응집: 모듈은 시점 의존성에 따라 연관된다. 예를 들어, 많은 시스템들이 시동할 대 그다지 관련이 없어 보이는 것들을 초기화하는 경우가 많은데, 이런 작업들이 일시적으로 응지됐다고 할 수 있다.
  • 논리적 응집: 모듈의 내부 데이터는 기능적이 아니라, 논리적으로 연관되어 있다. 이를 테면, 데이터를 변환하는 모듈이 그렇다. 서로 연관된 작업들이지만, 하는 일은 전혀 다르다.
  • 동시적 응집: 같은 소스 파일에 모듈 구성 요소가 들어 있지만, 그 외에에는 아무 연관성도 없다. 이는 가장 좋지 않은 형태의 응집이다.
💡 어떻게 분리하냐는 아키텍트의 업무의 핵심이라고 할 수 있는 트레이드오프 분석이다.
  • 카이댐버와 케메러가 개발한 '메서드의 응집 결여도(LOCM) (opens in a new tab)'은 모듈의 구조적 응집도를 나타낸다.
  • LOCM 메트릭은 아키텍처 스타일을 전환하기 위해 코드베이스를 분석하는 아키텍트에게 매우 유용하다.
  • LOCM 메트릭을 활용하면 어쩔 수 없이 커플링된 클래스, 처음부터 한 클래스가 아니었던 클래슬르 발견하는 데 도움이 된다.

추상도, 불안정도

  • 추상화를 전혀 하지 않고 하나의 엄청나게 큰 함수 코드가 있는 코드베이스도 있고, 반대로 너무 지나치게 추상화해서 코드가 서로 어떻게 연관되어 있는지 파악하기 어려운 코드베이스도 있다.
  • 불안정도는 코드베이스의 변동성을 의미한다. 불안정도가 높은 코드베이스는 변경 시 커플링이 높아 더 깨지기 쉽다.

메인 시퀀스로부터의 거리

  • 불안정도와 추상도를 이용해 계산한다.
  • 이상적인 선에 가까운 클래스는 서로 경쟁적인 두 메트릭의 건전한 조합이라고 볼 수 있다.

메인 시퀀스와의 거리

  • 오른쪽 위로 치우친 부분을 쓸모없는 구역(즉, 너무 추상화를 해서 사용하기 어려운 코드)
  • 왼쪽 아래로 치우친 부분을 고통스런 구역(즉, 추상화를 거의 안 하고 구현 코드만 잔뜩 넣어 취약하고 관리하기 힘든 코드)

커네이선스

  • 두 컴포넌트 중 한쪽이 변경될 경우 다른 쪽도 변경해야 전체 시스템의 정합성이 맞는다면 이들은 커네이선스를 갖고 있는 것이다.

정적 커네이선스 (소스 코드 레벨의 커플링)

  1. 명칭 커네이선스
    • 여러 컴포넌트의 엔티티명(개념의 명칭)이 일치해야 한다.
  2. 타입 커네이선스
    • 여러 컴포넌트의 엔티티 타입이 일치해야 한다.
  3. 의미 커네이선스와 관례 커네이선스
    • 여러 컴포넌트에 걸쳐 어떤 값의 의미가 일치해야 한다. (켄벤션?)
  4. 위치 커네이선스
    • 여러 컴포넌트는 값의 순서가 일치해야 한다.
    • 메서드와 함수 호출 시 전달되는 매개변수 값의 순서가 맞아야 한다. (이 순서의 의미가 복잡해진다면 객체로 key, value 형태를 사용하는 것도 방법이겠다)
  5. 알고리즘 커네이선스
    • 여러 컴포넌트는 특정 알고리즘이 일치해야 한다.
    • 동일한 형태의 결과를 내야 한다.

동적 커네이선스 (런타임 호출을 분석)

  1. 실행 커네이선스
    • 여러 컴포넌트의 실행 순서가 중요하다.
  2. 시점 커네이선스
    • 여러 컴폰너트의 실행 시점이 중요하다.
  3. 값 커네이선스
    • 상호 연관된 다수의 값들을 함께 변경할 때 발생한다.
  4. 식별 커네이선스
    • 여러 컴폰너트가 동일한 엔티티를 참조할 떄 발생한다.

커네이선스 속성

  1. 강도
    • 어떤 유형의 커네이선스를 얼마나 쉽게 리팩터링할 수 있느지에 따라 커네이선스 강도를 결정한다.
    • 정적 커네이선스는 소스 코드를 분석하거나 최신 도구를 활용하면 어렵잖게 개선할 수 있다.

커네이선스 강도

  1. 지역성

    • 커네이선스의 지역성은 코드베이스의 모듈들이 서로 얼마나 가까이 있는가
    • 동일한 모듈에서 근접한 코드는 보통 더 분리된 코드보다 높은 형태의 커네이선스를 가진다.
    • 강도와 지역성을 함께 고민해야 한다.
    • 동일한 모듈에서 더 강한 형태의 커네이선스가 발견된다면 그와 동일한 커네이선스가 널리 흩어져 있는 것보다는 코드 스멜이 덜 하다는 증거다.
  2. 정도

    • 커네이선스 정도는 커네이선스가 미치는 영향의 규모에 관한 것
    • 이 값이 작을수록 코드베이스 입장에서는 바람직
    • 모듈이 몇 개 안 된다면 동적 커네이선스가 높아도 별로 해롭지 않지만, 일반적으로 코드베이스는 점점 커지기 마련이니 사소한 문제도 점점 더 악화될 것
    • 페이지-존스가 제시한 커네이선스를 이용해 시스템의 모듈성을 개선하는 방법
      1. 시스템을 캡슐화한 요소들로 잘게 나누어 전체 커네이선스를 최소화
      2. 캡슐화 경계를 벗어나는 나머지 커네이선스를 모조리 최소화
      3. 캡슐화 경계 내부에서 커네이선스를 최대화