서비스 지향 아키텍처(SOA)와 마이크로서비스아키텍처(MSA)는 최근에 큰 인기를 끌고 있는 이유는 다음과 같다.
- 서비스를 사용하면 상호 결합이 철저하게 분리되는 것처럼 보인다.
- 서비스를 사용하면 개발과 배포 독립성을 지원하는 것처럼 보인다.
나중에 보겠지만, 이 역시도 일부만 맞는 말이다
SOA: 기존 애플리케이션들의 기능을 비즈니스적인 의미가 있는 기능 단위로 묶고, 표준화된 호출 인터페이스를 통해 서비스라는 소프트웨어 컴포넌트 단위로 재조합한 후, 이 서비스들을 서로 조합하여 업무 기능을 구현한 애플리케이션을 만들어내는 소프트웨어 아키텍처
MSA: 여러 개의 작은 서비스로 구성되어 각 서비스가 독립적으로 개발되고 배포되는 구조
서비스 아키텍처?
서비스를 사용한다는 것은 본질적으로 아키텍처에 해당하지 않는다.
시스템 아키텍처는 의존성 규칙을 준수하고 고수준의 정책을 저수준의 세부사항으로부터 분리하는 경계에 의해 정의된다. 단순히 애플리케이션의 행위를 분리할 뿐인 서비스라면 값비싼 함수 호출에 불과하고, 아키텍처 관점에서 그다지 중요하지 않다.
모든 서비스가 반드시 아키텍처 관점에서 중요해야 하는 것은 아니다. 기능을 프로세스나 플랫폼에 독립적이 되도록 서비스를 생성하면 의존성 규칙 준수 여부와 무관하게 도움이 되는 경우가 많다.
아키텍처적으로 중요한 서비스중요하지 않는 서비스
아키텍처 경계를 넘나드는 함수 | 시스템의 나머지 많은 함수들(행위를 서로 분리) |
모놀리틱 시스템, 컴포넌트 기반 시스템 | 단순 데이터 처리 함수, 포맷팅 및 유틸리티 함수, 로컬 비즈니스 로직 함수 |
하지만, 서비스 그 자체로는 아키텍처를 정의하지 않는다.
함수들의 구성 형태도 이와 비슷하다. 모놀리틱 시스템이나 컴포넌트 기반 시스템에서 아키텍처를 정의하는 요소는 바로 의존성 규칙을 따르고 아키텍처 경계를 넘나드는 함수 호출들이다. 반면 시스템의 나머지 많은 함수들은 행위를 서로 분리할뿐으로 아키텍처는 전혀 중요하지 않다.
그리고 이는 서비스도 마찬가지다. 결국 서비스는 프로세스나 플랫폼 경계를 가로지르는 함수 호출에 지나지 않는다. 아키텍처적으로 중요한 서비스도 있지만, 중요하지 않는 서비스도 존재한다.
이번 챕터에서 우리가 살펴볼 서비스는 전자다.
모놀리틱 시스템: 하나의 전체로 통합된 시스템
컴포넌트 기반 시스템: 재사용이 가능한 컴포넌트의 개발 또는 상용 컴포넌트들을 조합하여 재사용
서비스의 이점
결합 분리의 오류
시스템을 서비스들로 분리함으로써
서비스 사이의 결합이 확실히 분리된다는 이점이 있음.
→ 모든 서비스 인터페이스가 잘 정의 되어있으며 서비스는 다른 서비스 변수에 직접 접근할 수 없어야 함.
그러나
프로세스 내의 또는 네트워크 상의 공유 자원 때문에 결합될 가능성이 여전히 존재.
서비스 사이를 오가는 데이터 레코드에 새로운 필드를 추가한다면 이 필드를 사용하는 서비스는 모두 변경되어야함.
서비스들은 이 필드에 담긴 데이터를 해석하는 방식을 사전에 완벽하게 조율 해야함.
따라서 서비스들은 데이터 레코드에 강하게 결합되어 서비스들 간에 간접적인 결합이 생김.
ex) 서비스 사이를 오가는 데이터 레코드에 새로운 필드를 추가한다면, 이 필드를 통해 동작하는 모든 서비스는 반드시 변경되어야 한다.
개발 및 배포 독립성의 오류
서비스를 사용함에 따라 예측되는 또 다른 이점은 전담팀이 서비스를 소유하고 운영한다는 점이다. 그래서 데브옵스(dev-ops) 전략의 일환으로 전담팀에서 각 서비스를 작성하고, 유지보수하며, 운영하는 책임을 질 수 있다. 이러한 개발 및 배포 독립성은 확장 가능한(scalable)것으로 간주 된다.
대규모 엔터프라이즈 시스템을 독립적으로 개발하고 배포 가능한 수십, 수백, 수천 개의 서비스들을 이용하여 만들 수 있다고 믿는다. 시스템의 개발, 유지보수, 운영 또한 비슷한 수의 독립적인 팀 단위로 분할할 수 있다고 여긴다.
하지만 이는 일부일 뿐이다.
첫째로 대규모 인터프라이즈 시스템은 서비스 기반 시스템 이외에도 모노리틱, 컴포넌트 기반 등 다른 방식으로 구축할 수 있다.
둘째, 결합 분리의 오류에 따르면 서비스라고 해서 항상 독립적으로 개발하고, 배포 운영 할 수 있는 것이 아니다. 데이터나 행위에서 어느정도 결합되어 있다면 결합된 정도에 맞게 개발, 배포, 운영을 조정해야만 한다.
야옹이 문제
택시 통합서비스를 구현하기 위해 배치된 서비스들
특징을 보면 기능적으로 분해되어있음.
이로인해 서비스가 모두 결합되어있어 추가로 야옹이 배달 기능을 구현하려면 기존 서비스를 전부다 변경해야함.
독립적으로 개발하고 배포하거나 유지될 수 없음.
→ 횡단 관심사가 지닌 문제. (서비스 지향이든 아니든 모든 SW이 문제에 직면함.)
횡단 관심사: 핵심 관심에 영향을 주는 프로그램의 영역으로, 로깅과 트랜잭션, 인증처리와 같은 시스템 공통 처리 영역이 해당된다.
객체가 구출하다.
이문제는 컴포넌트 기반 아키텍처에서 해결가능.
배차 관련 로직은 Rides 컴포넌트에, 야옹이 관련 로직은 Kittens 컴포넌트로 추출되었다.
이 두 컴포넌트는 기존 컴포넌트들에 있는 추상 기반 클래스를 템플릿 메서드나 전략 패턴을 통해 오버라이드 한다.두 개의 신규 컴포넌트인 Rides와 Kittens가 의존성 규칙을 준수한다는 점에 주목하자. 그리고 이 기능들을 구현하는 클래스들은 UI의 제어하에 팩토리(Factories)가 생성한다는 점도 주목하자.
이 전략을 따르더라도 야옹이 기능을 구현하려면 TaxiUI는 변경해야만 한다. 하지만 그 외의 것들은 변경을 하지 않아도 된다. 대신 야옹이 기능을 구현한 새로운 jar파일이나 젬(Gem), DLL을 시스템에 추가하고, 런타임에 동적으로 로드하면 된다.
따라서 야옹이 기능은 결합이 분리되며, 독립적으로 개발하여 배포할 수 있다.
템플릿 메소드 패턴 : 여러 클래스에서 공통으로 사용하는 메서드를 템플릿화
전략 패턴 : 실행(런타임) 중에 알고리즘 전략을 선택하여 객체 동작을 실시간으로 바뀌도록
의존성 규칙 : 의존성 규칙은 모든 소스코드 의존성은 반드시 외부에서 내부로, 고수준 정책을 향해야 한다
컴포넌트 기반 서비스
서비스또한 SOLID 원칙대로 설계할 수 있으며 컴포넌트 구조를 갖출 수 있어 기존 컴포넌트를 변경하지 않고도 새로운 컴포넌트를 추가할 수 있음.
ex) java 의 경우 jar 파일을 통해 기능을 추가하면 기존 jar 파일에 정의된 추상 클래스를 확장하여 만들어진다. 이를 통해 새로운 기능 배포는 개방 폐쇄 원칙을 준수하게 된다.
각 서비스의 자신만의 컴포넌트 설계로 되어있어서 파생 클래스를 만드는 방식으로 야옹이 같은 신규 기능을 추가 할 수 있음.
횡단 관심사
횡단 관심사를 처리하려면 다이어그램처럼 서비스 내부의 의존성 규칙을 준수하는 컴포넌트 아키텍처로 설계되어야함.
아키텍처경계는 서비스 사이에 존재하지 않으며, 아키텍처 경계를 정의하는것은 서비스 내에 위치한 컴포넌트임.
결론
서비스는 시스템의 확장성과 개발 가능성 측면에서 유연하지만, 그 자체로는 아키텍처적으로 중요한 요소는 아니다. 시스템의 아키텍처는 시스템 내부에 그어진 경계와 경계를 넘나드는 의존성에 의해 정의된다. 시스템의 구성 요소가 통신하고 실행되는 물리적인 메커니즘에 의해 아키텍처가 정의되는 것이 아니다.
서비스는 단 하나의 아키텍처 경계로 둘러싸인 단일 컴포넌트로 만들 수 있다. 혹은 여러 아키텍처 경계로 분리된 다수의 컴포넌트로 구성할 수도 있다. 드물게는 클라이언트와 서비스가 강하게 결합되어 아키텍처적으로 아무런 의미가 없을 때도 있다.
'IT > architecture' 카테고리의 다른 글
[Clean Architecture 정리] 29장. 클린 임베디드 아키텍처 (0) | 2025.02.15 |
---|---|
[Clean Architecture 정리] 28장. 테스트 경계 (0) | 2025.02.15 |
[Clean Architecture 정리] 26장. 메인(Main) 컴포넌트 (0) | 2025.02.15 |
[Clean Architecture 정리] 23장. 프레젠터와 험블 객체 (0) | 2025.02.15 |
[Clean Architecture 정리] 22장. 클린 아키텍처 (0) | 2025.02.15 |