목록개발/Clean Architecture (28)
넘치게 채우기
시스템 아키텍처는 일련의 소프트웨어 컴포넌트와 그 컴포넌트들을 분리하는 경계에 의해 정의된다. 이러한 경계는 다양한 형태로 나타난다. 이 장에서는 이러한 형태 중에서 가장 흔한 몇 가지를 살펴보자. 경계 횡단하기 ‘런타임에 경계를 횡단한다’함은 그저 경계 한쪽에 있는 기능에서 반대편 기능을 호출하여 데이터를 전달하는 일에 불과하다. 적절한 위치에서 경계를 횡단하게 하는 비결은 소스 코드 의존성 관리에 있다. 왜 소스 코드일까? 왜냐하면 소스 코드 모듈 하나가 변경되면, 이에 의존하는 다른 소스 코드 모듈도 변경하거나, 다시 컴파일해서 새로 배포해야 할지도 모르기 때문이다. 경계는 이러한 변경이 전파되는 것을 막는 방화벽을 구축하고 관리하는 수단으로써 존재한다. 두려운 단일체 아키텍처 경계 중에서 가장 단..
소프트웨어 아키텍처는 선을 긋는 기술이다. 이러한 선을 경계라고 한다. 경계는 소프트웨어 요소를 서로 분리하고, 경계 한편에 있는 요소가 반대편에 있는 요소를 알지 못하도록 막는다. 이들 선 중 일부는 프로젝트 수명 중 아주 초기에, 심지어 코드가 작성되기도 전에 그어지며, 어떤 선은 매우 나중에 그어진다. 초기에 그어지는 선들은 가능한 한 오랫동안 결정을 연기시키기 위해, 그래서 이들 결정이 핵심적인 업무 로직을 오염시키지 못하게 만들려는 목적으로 쓰인다. 아키텍트의 목표는 필요한 시스템을 만들고 유지하는 데 드는 인적 자원을 최소화하는 것이라는 것을 상기하자. 그렇다면 인적 자원의 효율을 떨어뜨리는 요인은 무엇일까? 바로 결합이다. 특히 너무 일찍 내려진 결정에 따른 결합이다. 어떤 종류의 결정이 이..
앞서 서술한 바와 같이 좋은 아키텍처는 다음을 지원해야 한다. 시스템의 유스케이스 시스템의 운영 시스템의 개발 시스템의 배포 유스케이스 첫 번째 주요 항목인 유스케이스의 경우, 시스템의 아키텍처는 시스템의 의도를 지원해야 한다는 뜻이다. 만약 시스템이 장바구니 애플리케이션이라면, 이 아키텍처는 장바구니와 관련된 유스케이스를 지원해야 한다. 실제로 아키텍트의 최우선 관심사는 유스케이스이며, 아키텍처에서도 유스케이스가 최우선이다. 아키텍처는 반드시 유스케이스를 지원해야한다. 하지만 앞서 논의한 바와 같이 아키텍처는 시스템의 행위에 그다지 큰 영향을 주지 않는다. 행위와 관련하여 아키텍처가 열어 둘 수 있는 선택사항은 거의 없다. 하지만 영향력이 전부는 아니다. 좋은 아키텍처가 행위를 지원하기 위해 할 수 있..
아키텍처(architecture)라는 단어는 중대한 결정과 심도 있는 기술적 기량을 떠올리게 한다. 소프트웨어 아키텍처란 무엇인가? 소프트웨어 아키텍트는 무슨 일을 하며, 언제 그 일을 하는가? 무엇보다도 소프트웨어 아키텍트는 프로그래머이며, 앞으로도 계속 프로그래머로 남는다. 소프트웨어 아키텍트는 코드와 동떨어져서는 안된다. 소프트웨어 아키텍트는 최고의 프로그래머이며, 앞으로도 계속 프로그래밍 작업을 맡을 뿐만 아니라 동시에 나머지 팀원들이 생산성을 극대화할 수 있는 설계를 하도록 방향을 이끌어준다. 프로그램 작업을 계속하는 이유는, 발생하는 문제를 경험해보지 않으면 다른 프로그래머를 지원하는 작업을 제대로 수행할 수 없기 때문이다. 소프트웨어 시스템의 아키텍처란 시스템을 구축했던 사람들이 만들어낸 시..
지금부터 다룰 세 가지 원칙은 컴포넌트 사이의 관계를 설명한다. 이 장에서도 마찬가지로 개발 가능성과 논리적 설계 사이의 균형을 다룬다. ADP: 의존성 비순환 원칙 “컴포넌트 의존성 그래프에 순환(cycle)이 있어서는 안 된다.” 주 단위 빌드(Weekly Build) 주 단위 빌드는 중간 규모의 프로젝트에서는 흔하게 사용된다. 먼저 모든 개발자는 일주일의 첫 4일 동안은 서로를 신경 쓰지 않는다. 개발자는 모두 코드를 개인적으로 복사하여 작업하며, 전체적인 기준에서 작업을 어떻게 통합할지는 걱정하지 않는다. 그런 후, 금요일이 되면 변경된 코드를 모두 통합하여 시스템을 빌드한다. 이 접근법은 5일 중 4일 동안 개발자를 고립된 세계에서 살 수 있게 보장해준다. 그러나, 프로젝트가 커지면 프로젝트 통..
어떤 클래스를 어느 컴포넌트에 포함시켜야 할까? 이는 중요한 결정이므로 제대로 된 소프트웨어 엔지니어링 원칙의 도움을 받아야 한다. 이 장에서는 컴포넌트 응집도과 관련된 세 가지 원칙을 논의한다. REP: 재사용/릴리스 등가 원칙(Reuse/Release Equivalence Principle) “재사용 단위는 릴리스 단위와 같다.” 지난 십 년은 메이븐(Maven), 라이닝언(Leiningen), RVM 같은 모듈 관리 도구가 우후죽순으로 등장한 시기였다. 이 같은 도구는 점점 중요해졌는데, 이 기간에 재사용 가능한 컴포넌트나 컴포넌트 라이브러리가 엄청하네 많이 만들어졌기 때문이다. 우리는 이제 소프트웨어 재사용의 시대에 살고 있다. 객체 지향 모델의 오랜 약속 중 하나가 실현되었다. 돌이켜보면 REP..
컴포넌트는 배포 단위다. 시스템의 구성요소로 배포할 수 있는 가장 작은 단위다. 여러 컴포넌트를 서로 링크하여 실행 가능한 단일 파일로 생성할 수 있다. 또는 서로 묶어서 .war같은 파일과 같은 단일 아카이브로 만들 수도 있다. 또는 컴포넌트 각각을 .jar이나 .dll같이 동적으로 로드할 수 있는 플러그인이나 .exe파일로 만들어서 독립적으로 배포할 수도 있다. 컴포넌트가 마지막에 어떤 형태로 배포되든, 잘 설계된 컴포넌트라면 반드시 독립적으로 배포 가능한, 따라서 독립적으로 개발 가능한 능력을 갖춰야 한다. 컴포넌트의 간략한 역사 소프트웨어 개발 초창기에는 메모리에서 프로그램 위치와 레이아웃을 프로그래머가 직접 제어했다. 오늘날의 프로그래머는 이러한 프로그래밍 방식이 낯설 것이다. 요즘의 프로그래머..
의존성 역전 원칙(DIP)에서 말하는 ‘유연성이 극대화된 시스템’이란 소스 코드 의존성이 추상(abstarction)에 의존하며 구체(concretion)에는 의존하지 않는 시스템이다. 자바와 같은 정적 타입 언어에서 이 말은 use, import, include 구문은 오직 인터페이스나 추상 클래스같은 추상적인 선언만을 참조해야 한다는 뜻이다. 구체적인 대상에는 절대로 의존해서는 안 된다. 루피와 파이썬과 같은 동적 타입언어에서도 동일하다. 소스 코드 의존 관계에서 구체 모듈은 참고해서는 안 된다. 이 아이디어를 규칙으로 보기는 확실히 비현실적이다. 소프트웨어 시스템이라면 구체적인 많은 장치에 반드시 의존하기 때문이다. 예를 들어 String은 구체 크래스이며, 이를 애써서 추상 클래스로 만들려는 시도..
인터페이스 분리 원칙은 아래 그림의 다이어그램에서 그 이름이 유래했다. 그림 10.1에 기술된 상황에서, 다수의 사용자가 OPS 클래스의 오퍼레이션을 사용한다. User1은 오직 op1을, User2는 오직 op2를, User3는 op3만을 사요한다고 가정해보자. 그리고 OPS가 정적 타입 언어로 작성된 클래스가로 해보자. 이 경우 User1에서는 op2와 op3를 전혀 사요하지 않음에도 User1의 소스 코드는 이 두 메서드에 의존하게 된다. 이러한 의존성으로 인해 OPS 클래스에서 op2의 소스 코드가 변경되면 User1도 다시 컴파일한 후 새로 배포해야 한다. 사실 User1과 관련된 코드는 전혀 변경되지 않았음에도 말이다. 이러한 문제는 그림 10.2에서 보는 것처럼 오퍼레이션을 인터페이스 단위로..
1988년 바바라 리스코프(Barbara Liskov)는 하위 타입(subtype)을 아래와 같이 정의했다: “여기에서 필요한 것은 다음과 같은 치환(substitution) 원칙이다. S 타입의 객체 o1 각각에 대응하는 T타입 객체 o2가 있고, T타입을 이용해서 정의한 모든 프로그램 P에서 o2의 자리에 o1을 치환하더라도 행위가 변하지 않는다면, S는 T의 하위 타입이다:" 상속을 사용하도록 가이드하기 그림 9.1과 같이 License라는 클래스가 있다고 해보자. 이 클래스는 calcFee()라는 메서드를 가지며, Billing 애플리케이션에서 이 메서드를 호출한다. License에는 PersonalLicense와 BusinessLicense라는 두 가지 ‘하위 타입’이 존재한다. 이들 두 하위 ..