클린 아키텍처 스터디 4부 정리

ubrain·2023년 1월 25일
0

클린 아키텍쳐

목록 보기
5/5
post-thumbnail

컴포넌트

배포 단위, 잘 설계된 컴포넌트라면 반드시 독립적으로 빼포 가능하고 독립적으로 개발 가능해야 한다.

  • 컴포넌트의 간략한 역사
    과거의 소프트웨어는 메모리에서 프로그램의 위치와 레이아웃을 직접 프로그래머가 제어.
    프로그램이 접근하는 라이브러리도 같이 포함을 하여 단일 프로그램으로 컴파일 했는데, 하드웨어적인 제약 때문에 컴파일이 매우 오래 걸림.
    컴파일 시간을 줄이기 위해 라이브러리를 분리했고, 이러한 라이브러리 역시 메모리에 할당.
    하지만 이 역시 어플리케이션의 크기가 커짐으로 인해 문제가 발생

  • 재배치성
    지능적인 로더를 사용해서 메모리에 재배치할 수 있는 형태의 바이너리를 생성하도록 컴파일러를 수정.
    로더는 재배치 코드가 자리할 위치 정보를 전달 받음.
    재배치 코드에는 로드한 데이터에서 어느 부분을 수정해야 정해진 주소에 로드할 수 있는지를 알려주는 플래그가 삽입.
    -> 이를 통해 프로그래머는 함수 라이브러리를 로드할 위치와 애플리케이션을 로드할 위치를 로더에게 지시할 수 있게 됨.
    로더는 여러개의 바이너리를 입력 받은 후, 차례대로 메모리로 로드하면서 재배치하는 작업을 처리했고 이를 통해 프로그래머는 오직 필요한 함수만 로드하는 것이 가능.

컴파일러는 재배치 가능한 바이너리 안의 함수 이름을 메타데이터 형태로 생성하도록 수정하여 라이브러리 함수를 호출할때 함수 이름을 외부 참조로 생성하고, 라이브러리 함수를 정의하는 프로그램이라면 해당 이름을 외부 정의로 생성.
외부 정의를 로드할 위치가 정해지면 로더가 외부 참조를 외부 정의에 링크하였고 이것이 링킹 로더의 탄생.

링커

프로그램이 점점 커짐에 따라 링킹로더의 한계가 발생하여 링크와 로드 2단계로 분리.
프로그래머가 링커라는 별도의 애플리케이션을 링크 과정을 맡음
-> 로더의 로딩 과정이 아주 빨라짐

프로그램이 커지는 속도보다 하드웨어의 발전 속도가 커져 로드와 링크를 동시에 처리하는 것이 가능해짐.
다수의 파일 또는 다수의 공유 라이브러리를 링크 한 후 실행하는 컴포넌트 플러그인 아키텍처가 가능해졌다.

컴포넌트 응집도

컴포넌트 응집도와 관련된 3가지 원칙은 REP, CCP, CRP이다.

REP(재사용/릴리스 등가 원칙)

재사용 단위는 릴리스 단위와 같다.
단일 컴포넌트는 응집성 높은 클래스와 모듈들로 구성되어야 한다.
컴포넌트를 구성하는 모든 모듈은 서로 공유하는 중요한 테마나 목적이 있어야 한다.

CCP(공통 폐쇄 원칙)

동일한 이유로 동일한 시점에 변경되는 클래스를 같은 컴포넌트로 묶는 원칙.
SOLID 원칙 중 SRP를 컴포넌트 관점에서 다시 쓴 것.
유지보수가 재사용성보다 중요한데, 한 컴포넌트가 하나의 역할만 맡게 된다면 해당 컴포넌트만 변경 및 테스트를 진행하면 되고 나머지 컴포넌트들은 그대로 유지시켜도 괜찮기 때문애 유지보수가 수월.
서로 다른 이유로 변경되는 클래스들을 다른 컴포넌트로 분리해야한다.

CRP(공통 재사용 원칙)

컴포넌트 사용자들을 필요하지 않는 것에 의존하게 강요하지 않아야 한다.
재사용이 되는 경향이 있는 클래스와 모듈들을 같은 컴포넌트에 포함해야 한다.
SOLID 원칙 중 ISP를 컴포넌트 레벨로 확장시켰다고 볼 수 있다.

컴포넌트 응집도에 대한 균형 다이어그램

REP와 CCP는 포함원칙이어서 컴포넌트를 더 크게 만든다.
CRP는 배제 원칙이어서 컴포넌트를 더 작게 만든다.
이 셋 중 특정한 부분에 너무 포커스를 맞추면 아래와 같은 문제가 발생.

  • REP, CRP : 컴포넌트 변경이 너무 빈번
  • CRP, CCP : 재사용이 어려움
  • REP, CCP : 불필요한 릴리스가 너무 빈번함

뛰어난 아키텍트라면 이 셋의 적절한 밸런스를 잡는 것이 중요.
일반적으로 프로젝트를 처음 시작할때는 삼각형의 오른쪽, 프로젝트가 성숙하면 삼각형의 왼쪽으로 점점 이동하게 된다.

결론

어떤 클래스들을 묶어서 컴포넌트로 만들 때 재사용성과 개발성이라는 상충하는 힘을 고려해야한다. 또한 지금 내가 중점적으로 두고 있는 가치가 시간이 흐름에 따라 변할 수도 있다는 사실을 명심하고 균형을 맞추도록 노력해야한다.

컴포넌트 결함

ADP(의존성 비순환 원칙)

컴포넌트 의존성 그래프에 순환이 있어서는 안 된다.
의존성 때문에 잘 동작하던 코드가 갑자기 동작하지 않는 '숙취 증후군'을 해결하기 위한 방법으로 주단위 빌드와 ADP가 널리 사용된다.

  • 주 단위 빌드
    4일 동안은 독립적으로 개발 후 금요일에 빌드를 하는 방식의 개발.
    프로젝트가 커지면 실효성이 떨어진다.

  • 순환 의존성 제거하기
    릴리즈 단위의 컴포넌트를 한 개발자 또는 한팀이 맡게하여, 버전에 맞게 선택을 하게한다.
    의존성 구조를 반드시 관리해야 이런 개발이 가능하다.
    어느 컴포넌트에서 시작하더라도, 의존성 관계를 따라가면서 최초의 컴포넌트로 되돌아 갈 수 없다.
    특정 컴포넌트를 개발할때 테스트를 구성할 때 대체로 적은 노력이 들고, 고려해야 될 변수도 상대적으로 적다.
    시스템을 릴리스 할 경우 릴리스 절차는 상향식으로 작은 컴포넌트 부터 큰컴포넌트 식으로 진행하면 된다.
    의존성의 순환이 발생하게 되면 숙취 증후군은 피할 수 없어진다.
    의존성 순환이 발생이 된다면 인터페이스를 사용해 의존성을 역전 시키거나, 의존하는 클래스를 새로운 컴포넌트로 이동시킨다.

  • 하향식 설계
    컴포넌트는 하향식으로 설계 될 수 없으며, 컴포넌트는 시스템에서 가장 먼저 설계할 수 있는 대상이 아니며, 오히려 시스템이 성장하고 변경될 때 함께 진화.
    컴포넌의 의존성 다이어그램은 애플리케이션의 기능을 기술하지 않고 빌드 가능성과 유지보수성을 보여주는 지도이다.

SDP(안정된 의존성 원칙)

안정성의 방향으로 의존해야 한다.
소프트웨어에서 변경은 불가피한데, 이때 변경이 잦을 컴포넌트와 변경이 잦지 않은 컴포넌트를 잘 구분해야한다.
소프트웨어는 변경이 잦은 컴포넌트 보다는 변경이 적은 컴포넌트에 의존하도록 설계가 돼야 한다.

안정성

컴포넌트가 많은 컴포넌트에 의지할 수록 해당 컴포넌트는 불안정한 컴포넌트이다.

안정성의 지표

  • Fan-in : 안으로 들어오는 의존성. 컴포넌트 내부의 클래스에 의존하는 컴포넌트 외부의 클래스 개수
  • Fain-out : 바깥으로 나가는 의존성. 컴포넌트 외부의 클래스에 의존하는 컴포넌틔 내부의 클래스 개수
  • I(불안정성) = Fan-out/(Fan-in + Fan-out)

의존성의 방향으로 갈수록 I 지표 값이 감소해야한다.

  • 안정화 된 컴포넌트가 불안정한 컴포넌트에 의존하는 것은 좋지 않으며, 이럴 경우 별도의 추상 컴포넌트를 생성해 안정성이 높아야 하는 컴포넌트가 이 추상 컴포넌트에 의지하게 해야한다.

SAP(안정된 추상화 원칙)

컴포넌트는 안정된 정도만큼만 추상화되어야 한다.
안정적인 컴포넌트는 인터페이스와 추상 클래스로 구성되어 쉽게 확장할 수 있어야 한다.
안정된 컴포넌트가 확장이 가능해지면 유연성을 얻게 되고 아키텍처를 과도하게 제약하지 않게된다.
의존성은 추상화의 방향으로 향하게 해야한다.

  • 추상화 정도
    추상화 정도(A) = 컴포넌트의 추상 클래스와 인터페이스 개수/컴포넌트의 클래스 개수

  • 추상화 정도 측정


1. 고통의 구역

  • (0,0) 주변의 친구들.
  • 추상적이지 않아 확장할 수 없고, 안정적이라 변경하기도 힘듦.
  • 일부 소프트웨어의 엔티티가 이 구역에 위치. DB스키마가 변경되면 고통스러움.
  • 구체적인 유틸리티 라이브러리도 이 구역에 위치. 예를 들면 String 컴포넌트로 변동성이 거의 없음.
    하지만 변경이 일어나면 혼란초래.
  • 변동성이 없는 컴포넌트는 (0,0) 구역에 위치하더라도 해롭지 않으나 변동성이 있는 컴포넌트는 여기 위치하면 안된다.
  1. 쓸모없는 구역
  • (1,1) 주변의 친구들.
  • 최고로 추상적이지만, 누구도 그 컴포넌트에 의존하지 않음. 따라서 쓸모가 없다.
  1. 배제 구역 벗어나기
  • 변동성이 큰 컴포넌트는 고통의 구역과 쓸모없는 구역(배제 구역)을 벗어나야 한다.
  • 이는 (1,0)과 (0,1)을 잇는 선분이다.(주계열)
  • 주계열에 위치한 컴포넌트는 자신의 안정성에 비해 '너무 추상적'이지도 추상화 정도에 비해 '너무 불안정'하지도 않다.
  • 추상화된 수준에 어울릴 정도로만 다른 컴포넌트에 의존, 구체화된 수준에 어울릴 정도로만 다른 컴포넌트에 의존.
  • 가장 바람직한 지점은 주계열의 두 종점 (0,1), (1,0)
  • 뛰어난 아키텍트라면 대다수의 컴포넌트가 두 종점에 위치하도록 만들기 위해 매진.
  • 주계열과의 거리
    주계열 위 또는 가까이 위치한 컴포넌트가 바람직하다면, 얼마나 이 상태로 부터 멀리 떨어져 있는지 측정하는 지표.
D=|A+I-1|

D가 0에 가까울수록 이상적.
D가 0에서 가깝지 않다면 해당 컴포넌트는 재검토한 후 재구성할 수 있다.
D지표의 평균과 분산을 통해 다른 컴포넌트에 비해 예외적인 컴포넌트 추출 가능.

결론

의존성 관리 지표를 통해, 설계의 의존성과 추상화 정도가 내가 생각하는 훌륭한 수준에 얼만큼 부합하는지 측정 가능.
하지만 이 지표는 절대적인 것이 아니라 내가 맞게 설계를 했는지 알게 해주는 참고 자료일뿐.

profile
Keep going하는 개발자

0개의 댓글