안녕하세요. 휴먼스케이프 테드입니다.
오늘은 클린아키텍처의 책 중에서 아키텍처와 독립성에 대한 내용을 이야기 해보려고 합니다.
아키텍처라는 단어는 권력과 신비로움을 연상케 하곤 합니다.
하지만 아키텍트는 프로그래머이며, 앞으로도 계속 프로그래머로 남습니다. 소프트웨어 아키텍트라면 코드에서 탈피하여 고수준의 문제에 집중해야 한다는 거짓말에 절대로 속아 넘어가서는 안 됩니다.
소프트웨어 아키텍트는 코드와 동떨어져선 안되고, 최고의 프로그래머이며 앞으로도 계속 프로그래밍 작업을 맡을 뿐만 아니라 동시에 나머지 팀원들이 생산성을 극대화할 수 있는 설계를 하도록 방향을 이끌어 줍니다.
아키텍처의 주된 목적은 시스템의 생명주기를 서포트하는 것입니다. 좋은 아키텍처는 시스템을 쉽게 이해하고 쉽게 개발하며 쉽게 유지보수하고 쉽게 배포하게 해줍니다. 궁극적인 목표는 시스템의 수명과 관련된 비용은 최소화 하고, 프로그래머의 생산성은 최대화 하는 데 있습니다.
좋은 아키텍처는 다음을 지원해야 합니다.
시스템의 유스케이스
시스템의 운영
시스템의 개발
시스템의 배포
책에서는 물리적 주소 할당과 광고 우편 예가 있으니 한번씩 보셔도 좋을것 같습니다. :)
유스케이스
시스템의 아키텍처는 시스템의 의도를 지원해야한다는 뜻입니다. 예) 주문 중개 시스템, 영화 예매 시스템, 수강 신청 시스템, 소셜 서비스 등
아키텍처는 시스템의 행위에 그다지 큰 영향을 주지 않습니다. 다만 좋은 아키텍처가 행위를 지원하기 위해서 행위를 명확히 하고 외부로 드러내며, 이를 통해 시스템이 지닌 의도를 아키텍처 수준에서 알아 볼 수 있게 만들어야 합니다.
좋은 아키텍처를 갖춘다면, 해당 시스템의 유스케이스는 시스템 구조 자체에서 한눈에 드러날 수 있습니다. 이들 행위는 일급 요소(first-class element) 이며 시스템의 최상위 수준에서 알아 볼 수 있으므로, 일일히 찾아 헤매지 않아도 됩니다. (21장에서 더 잘 설명할 예정입니다.)
일급 객체(first class object) - 다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체 ( 일급 시민(first class citizen) 을 만족하는 객체 )
일급 객체를 구성하는 요소 (로빈 포플스톤) 1) 모든 요소는 함수의 실제 매개변수가 될 수 있습니다. 2) 모든 요소는 함수의 반환 값이 될 수 있습니다. 3) 모든 요소는 할당 명령문의 대상이 될 수 있습니다. 4) 모든 요소는 동일 비교의 대상이 될 수 있습니다.
출처 : https://ko.wikipedia.org/wiki/%EC%9D%BC%EA%B8%89_%EA%B0%9D%EC%B2%B4
운영
아키텍처가 시스템 운영에 미치는 영향은 다른 영향보다는 덜 극적입니다. 단순히 하드웨어를 더 투입해서 해결할 수도 있습니다. (테드: 하지만 공짜 점심은 끝났다는 말이 있듯 신경은 써야합니다..)
하지만 운영지원의 관점에서 볼때 아키텍처는 더 실질적이며 덜 피상적 입니다.
시스템이 초당 100,000명의 고객을 처리하거나, 수 밀리초 안에 3차원의 빅데이터 테이블에 질의해야 한다면, 반드시 이 작업이 가능할 수 있는 형태로 아키텍처를 구조화 해야합니다.
여러 구조들이 있지만, 뛰어난 아키텍트라면 각 구조들에 대해서 열어 두어야하는 선택사항 중의 하나 입니다. 잘 격리하여 유지하고 특정 형태로 통신 방식을 제한하지 않는다면, 시간이 지나 요구사항이 바뀌어도 스레드, 프로세스, 서비스로 구성된 기술 스펙트럼 사이를 전환하는 일이 훨씬 쉬워질 것입니다.
개발
팀 구조가 다르다면 아키텍처 관련 결정에서도 차이가 납니다.
일례로 팀이 개발자 5 명으로 구성될 정도로 작다면, 잘 정의된 컴포넌트나 인터페이스가 없더라도 서로 효율적으로 협력하여 모노리틱 시스템을 개발 할 수 있습니다.
사실 이런 팀이라면 아키텍처 관련 제약들이 오히려 방해가 된다고 여길 가능성이 높습니다. 수 많은 시스템에서 좋은 아키텍처가 결여된 이유이기도 하죠.
다른 한편으로 7명씩 5팀으로 된 팀이 시스템을 개발할때 잘 분리하지 않으면 개발이 진척되지 않습니다. 다른 요소를 고려하지 않는다면, 이 시스템의 아키텍처 각 팀마다 하나씩 발전될 가능성이 높습니다.
‘팀별 단일 컴포넌트’ 아키텍처가 최적일 가능성은 거의 없지만, 여러 팀들이 일정에 쫓겨서 일하면 결국 이 아키텍처로 귀착될 수 있습니다.
개발환경을 지원하는 데 있어서 핵심적인 역할이어야 합니다.
콘웨이의 법칙이 적용될 수 있는 지점이기도 하죠. 콘웨이의 법칙은 다음과 같습니다.
시스템을 설계하는 조직이라면 어디든지 그 조직의 의사소통 구조와 동일한 구조의 설계를 만들어 내야 합니다.
배포 (+ 배포 독립성)
목표는 ‘즉각적인 배포’ 로 잡아야 합니다. 좋은 아키텍처라면 수작업으로 진행하지 않고 빌드 된 후 즉각 배포(immediate deployment 또는 hot swap)할 수 있도록 지원해야 합니다.
이를 위해서는 시스템을 컴포넌트 단위로 적절하게 분할하고 격리시켜야 합니다.
그렇게 되면 새로운 유스케이스를 추가하는 일은 시스템을 그대로 둔 채 새로운 패키지 파일이나 서비스 몇개를 추가 하는 정도로 단순한 일이 됩니다.
안타깝지만 초기 개발 단계에서는 배포 전략을 거의 고려하지 않는데요. 이로 인해 개발은 쉬워지지만 배포는 상당히 어려운 아키텍쳐가 만들어지곤 합니다.
예를 들어 초기 단계에 개발자가 마이크로서비스 아키텍처를 사용하자! 고 결정할 수 있는데요. 처음애는 매우 쉽게 개발할 수 있다고 판단했을 지 모르지만, 배포할 시기가 되면 위협적일 만큼 늘어난 수 많은 마이크로 서비스를 발견하게 될지도 모릅니다. 이를 서로 연결하기 위해 설정하고 작동 순서를 결정하는 과정에서 오작동이 발생할 원천이 스며 들 수 도 있습니다.
만약 아키텍트가 배포문제를 초기에 고려했다면 이와는 다른 결정을 내렸을 것입니다. 서비스 컴포넌트와 프로세스 수준의 컴포넌트를 하이브리드 형태로 융합하여 관리하는 방식으로 말이죠.
선택사항 열어 놓기 (option)
하지만 이렇게 글로 쓰는건 쉽지만, 실제로 컴포넌트 구조와 관련된 관심사들 사이에서 균형을 잡는건 매우 어렵습니다.
좋은 아키텍처는 선택사항을 열어 둠으로써, 향후 시스템에 변경이 필요할 때 어떤 방향으로든 쉽게 변경 할 수 있도록 해야합니다.
계층(layer) 결합 분리
아키텍트는 필요한 모든 유스케이스를 지원할 수 있는 시스템 구조를 원하지만, 유스케이스 전부를 알지 못합니다.
하지만 어떤 시스템인지는 알기 때문에 단일 책임 원칙과 공통 폐쇄 원칙을 적용하여, 의도와 맥락에 따라 다른 이유로 변경되는 것들은 분리하고 동일한 이유로 변경되는것들은 묶습니다.
서로 결합되지 않는 수평적인 계층으로 분리하는 예로는 UI, 애플리케이션에 특화된 업무, 애플리케이션과 독립적인 업무, DB 등을 들 수 있습니다.
유스케이스 결합 분리
계층 분리를 수평으로 생각한다면 유스케이스는 수직으로 좁다란 조각으로 볼 수 있습니다. 아래의 Request가 가로지르는 방향으로 컴포넌트들이 사용된다고 볼 수 있겠죠. 이렇게 되면 시스템을 분할 할 수있고 서로 겹치지 않게 만들수 있습니다. 새로운 유스케이스를 추가해도 기존 유스케이스에 영향을 줄 일도 없게 되죠.
Layer Architecture, 출처 : https://medium.com/@priyalwalpita/software-architecture-patterns-layered-architecture-a3b89b71a057
개발 독립성
컴포넌트가 완전히 분리되면 팀 사이 간섭은 줄어듭니다.
기능팀, 컴포넌트 팀, 계층 팀 혹은 또 다른 형태의 팀이라도 계층과 유스케이스의 결합이 분리되기만 하면 팀 구조를 만드는데도 도움이 될 것 입니다.
중복
소프트웨어에서 중복은 일반적으로 나쁜 것입니다. 하지만 진짜 중복과 거짓된 중복 또는 우발적인 중복들이 있습니다. 처음에는 비슷해도 각자의 경로로 발전하게 되는 경우들이 있습니다.
예를들면 UI에서 화면 구성과 같은 경우에 초기 틀은 비슷하지만 각 화면의 방향에 따라 달라지는 경우들이 있도록 말이죠.
마찬가지로 계층을 수평으로 분리하는 경우, DB 레코드의 데이터 구조가 특정 화면의 데이터 구조와 상당히 비슷하다는 걸 느낄 수도 있는데요. 이때 뷰모델을 쓰는게 아니라 DB레코드를 있는 그대로 UI로 전달하고 싶을 수 있습니다. 하지만 이런 중복은 계층간 결합을 적절하게 분리하여 유지하는 데 도움이 됩니다.
결합 분리 모드
많은 아키텍트들이 ‘서비스’ 또는 ‘마이크로서비스’ 라는 단위로 컴포넌트를 말하는데 그 구분 기준은 모호한 편입니다. 실제로 서비스에 기반한 아키텍처를 흔히 서비스 지향 아키텍처(SOA, Service-oriented architecture)라고 합니다.
이들이 실제 자주 들어보거나 어려워 보일수도 있습니다. 하지만 이게 최선의 아키텍쳐라거나 마이크로서비스가 미래라고 말하려는게 아닙니다.
핵심은 컴포넌트를 서비스 수준까지도 분리해야한다는 점이죠.
다음과 같은 분리 방법들이 있습니다.
소스 수준 분리 모드(예: ruby Gem)
소스코드와 모듈 사이의 의존성을 제어합니다. 하나의 모듈이 변해도 다른 모듈을 변경하거나 재 컴파일 하지 않도록 만들 수 있습니다. 서로 통신할때 간단한 함수 호출을 사용합니다. 흔히 모노리틱 구조라고 합니다.
배포 수준 분리 모드
DDL, jar, 공유 라이브러리 같이 배포가능한 단위들 사이의 의존성을 제어할 수 있습니다. 다른 프로세스에 상주하고 IPC나 소켓, 공유 메모리를 통해 통신할 수 있습니다.
서비스 수준 분리 모드(예: 서비스 또는 마이크로서비스)
의존하는 수준을 데이터 구조 단위까지 낮출 수 있습니다. 순전히 네트워크 패킷을 통해서만 통신하도록 만들 수 있습니다. 서로 완전히 독립적이게 됩니다.
위의 모드 중에서 어떤 모드가 사용하기에 가장 좋아 보이시나요?
처음에는 단일 서버로 시작해도 결국에 시스템이 성장하면 별도 서버에서 실행해야만 하는 컴포넌트가 생길거라고 예상할 수 있습니다.
(현 시점에서 가장 인기 있어 보이는) 한 가지 해결책은 단순히 서비스 수준에서의 분리를 기본 정책으로 삼는것입니다. 마이크로 서비스가 아무리 작아도 완전 작은 단위에서 분리될 가능성은 거의 없습니다.
다만 이렇게 할때 문제점은 개발 시간과 시스템 자원 측면의 비용이 많이 듭니다.
컴포넌트가 서비스화 될 가능성이 있다면 이 결합을 분리하되 서비스가 되기 직전까지 두는 방식(ted : 바로 분리해도 무방하도록)이 선호됩니다. (지금 휴먼스케이프에서 하고 있는 방식입니다.)
그러고는 가능한 한 오랫동안 동일한 주소 공간에 남겨 둬서 선택권을 열어 둡니다.
좋은 아키텍처는 시스템이 모노리틱 구조로 태어나서 성장해서 마이크로서비스 수준까지 만들어지고, 나중에 상황이 바뀌었을때 거꾸로 돌려 원래 형태로 되돌릴 수 있어야 합니다.
까다롭지만 규모에 따라 가장 적합한 모드를 선택해 사용할 수 있게, 만들어 주어야 합니다.
Get to know us better! Join our official channels below.
Telegram(EN) : t.me/Humanscape KakaoTalk(KR) : open.kakao.com/o/gqbUQEM Website : humanscape.io Medium : medium.com/humanscape-ico Facebook : www.facebook.com/humanscape Twitter : twitter.com/Humanscape_io Reddit : https://www.reddit.com/r/Humanscape_official Bitcointalk announcement : https://bit.ly/2rVsP4T Email : support@humanscape.io