티스토리 뷰

반응형

회사에서 TDD 관련 외부세션을 듣다가 '서비스가 안티패턴' 이라는 말을 들었다.

왜 안티패턴일까..? 머리에 궁금증이 남아있었는데 

클린아키텍처 27장('크고 작은 모든' 서비스들) 을 읽으며 이유를 알게 되었다. 

 

그래서 정리!

(참고로 책을 쭉 이끌어온 저자의 맥락을 이 요약글에 다 담을 수 없으니.. 책을 읽어보시는 것을 추천드립니다.)


 

[1] 서비스 아키텍처? 서비스의 이점? 

 

서비스 지향 '아키텍처' 와 마이크로서비스 '아키텍처' 는 최근에 큰 인기를 끌었다. 

그 이유는 다음과 같다.

 

- 서비스를 사용하면 상호 결합이 철저하게 분리되는 것처럼 보인다.  // 결합 분리

- 서비스를 사용하면 개발과 배포 독립성을 지원하는 것 처럼 보인다. // 개발 및 배포 독립

 

둘다 일부만 맞는 말이다. 뒤에서 살펴보겠다.

 

우선 서비스를 사용한다는 것이 본질적으로 아키텍처에 해당되는 지 생각해보자. 답은 NO. 

시스템 아키텍처는 의존성 규칙을 준수하며 고수준의 정책을 저수준의 세부사항으로부터 분리하는 경계에 의해 정의된다.

단순히 애플리케이션의 행위를 분리할 뿐인 서비스라면 값비싼 함수 호출에 불과하며, 아키텍처 관점에서 꼭 중요하다고 볼 수는 없다. 서비스 그 자체로는 아키텍처를 정의하지 않는다. 

 

이제 돌아와서, 다시 위의 두가지 이점의 오류를 살펴보자. 

 

 

# 결합 분리의 오류

시스템을 서비스들로 분리함으로써 얻게 되리라 예상되는 큰 이점 하나는 서비스 사이의 결합이 확실히 분리된다는 점이다.

이 말에는 어느정도 일리가 있지만, 꼭 그런 것만은 아니다. 

서비스는 개별 변수 수준에서는 각각 결합이 분리된다. 하지만 프로세서 내의 또는 네트워크 상의 공유자원 때문에 결합될 가능성이 여전히 존재한다. 더욱이 서로 공유하는 데이터에 의해 이들 서비스는 강력하게 결합되어버린다.

 

예를들어 서비스 사이를 오가는 데이터 레코드에 새로운 필드를 추가한다면, 이 필드를 사용해 동작하는 모든 서비스는 반드시 변경되어야한다. 또한 이 서비스들은 이 필드에 담긴 데이터를 해석하는 방식을 사전에 완벽하게 조율해야한다. 따라서 서비스들은 이 데이터 레코드에 강하게 결합되고, 서비스들 사이는 서로 간접적으로 결합되어 버린다. 

 

 

# 개발 및 배포 독립성의 오류

서비스를 사용함에 따라서 예측되는 또 다른 이점은 전담팀이 서비스를 소유하고 운영한다는 점이다.

각 서비스의 개발, 유지보수, 운영을 독립적인 팀 단위로 분할할 수 있다고 생각한다.

이 말에도 어느정도 일리가 있지만, 극히 일부일 뿐이다. 

 

첫째로, 대규모 엔터프라이즈 시스템은 서비스 기반 시스템 이외에도, 모노리틱 시스템이나 컴포넌트 기반 시스템으로도 구축할 수 있다는 사실은 역사적으로 증명되어 왔다. 따라서 서비스는 확장 가능한 시스템을 구축하는 유일한 선택지가 아니다.

 

둘째, 서비스라고 해서 항상 독립적으로 개발하고, 배포하며 운영할 수 있는 것은 아니다. 데이터나 행위에서 어느 정도 결합되어 있다면 결합된 정도에 맞게 개발, 배포, 운영을 조정해야만 한다. 

 

 

[2] 야옹이 문제

앞의 두가지 오류에 대한 예로 택시통합시스템을 살펴보자 

확장가능한 시스템을 구축하고 싶었기에, 우리는 수많은 작은 마이크로 서비스를 기반으로 구축하기로 결정했다.

개발팀 직원을 많은 소규모 팀으로 세분화했고, 각 팀이 팀 규모에 맞게 적당한 수의 서비스를 개발하고, 유지보수하며, 운영하는 책임을 지도록 했다. 

 

 

TaxiUI 서비스는 고객을 담당하며, 고객은 모바일 기기를 이용해서 택시를 호출한다.

 

TaxiFinder 서비스는 여러 TaxiSupplier의 현황을 검토하여 사용자에게 적합한 택시후보들을 선별한다.

TaxiFinder 서비스는 해당 사용자에 할당된 단기 데이터 레코드에 후보 택시들의 정보를 저장한다.

 

TaxiSelector 서비스는 사용자가 지정한 비용, 시간, 고급 여부 등의 조건을 기초로 후보 택시 중에서 적합한 택시를 선택한다.

이제 TaxiSelector 서비스가 해당 택시를 TaxiDispatcher 서비스로 전달하면,

 

TaxiDispatcher 서비스는 해당 택시에 배차 지시를 한다. 

 

 

1년 후..

마케팅팀은 도시에 야옹이를 배달하는 서비스를 제공하겠다는 계획을 발표한다.

사용자는 자신의 집이나 사무실로 야옹이를 배달해달라고 주문할 수 있다

 

택시 업체 몇몇만 참여한다. 

어떤 운전자는 고양이 알러지가 있을 수 있기 때문에, 해당 운전자는 이 서비스에서 제외되어야한다. 

또한 일반 택시 승객도 알러지가 있을 수 있으므로, 배차를 신청한 고객이 알러지가 있다고 밝힌 경우라면 지난 3일 사이에 야옹이를 배달했던 차량은 배차되지 않아야한다. 

 

서비스 다이어그램을 살펴보자. 이 기능을 구현하려면 이들 서비스 중 어디를 변경해야할까? 전부다.

다시 말해 이 서비스들은 모두 결합되어있어서 독립적으로 개발하고, 배포하거나, 유지될 수 없다.

 

이게 바로 횡단 관심사(cross-cuttong concern)가 지닌 문제다. 

모든 소프트웨어는 서비스 지향이든 아니든 이 문제에 직면하기 되기 마련이지만,,,

그림 27.1 의 서비스 다이어그램과 같이 

종류의 기능적 분해는 새로운 기능이 기능적 행위를 횡단하는 상황에 매우 취약하다. 

 

 

 

[3] 컴포넌트 기반 서비스

컴포넌트 기반 아키텍처는 이 문제를 어떻게 해결했을까?

SOLID 설계 원칙을 잘 들여다보면, 다형적으로 확장할 수 있는 클래스 집합을 생성해 새로운 기능을 처리하도록 함을 알 수 있다. 

 

 

배차에 특화된 로직은 Rides 컴포넌트로 추출되고, 야옹이에 대한 신규기능은 Kittens 컴포넌트에 들어갔다.

이 두 컴포넌트는 기존 컴포넌트들에 있던 추상 기반 클래스를 템플릿 메서드나 전략(strategy)패턴 등을 이용해서 오버라이드한다.

두개의 신규 컴포는트인 Rides와 Kittens가 의존성규칙을 준수한다는 점에 다시 한번 주목하자.

또한 이 기능들을 구현하는 클래스들은 UI의 제어하에 팩토리(Factories)가 생성한다는 점에도 주목하자.

 

야옹이 기능은 결합이 분리되며, 독립적으로 개발하여 배포할 수 있다. 

 

 

--------------- 

 

서비스에도 이렇게 할 수 있다.

서비스가 반드시 소규모단일체(monolith)여야할 이유는 없다. 

각 서비스의 내부는 자신만의 컴포넌트 설계로 되어있어서 파생 클래스를 만드는 방식으로 신규기능을 추가할 수 있다.

파생클래스들은 각자의 컴포넌트 내부에 놓인다. 

 

 

 

 

서비스 내부를 의존성 규칙도 준수하는 컴포넌트 아키텍처로 설계해야한다. 

아키텍처 경계를 정의하는 것은 서비스들이 아니라 서비스 내에 위치한 컴포넌트다. 

 

 

 

[4] 결론 

(여기서부터는 책에 나오는 문장이 아니라 제 생각입니다)

 

서비스를 안티패턴이라고 하는 이유는 책에서 말하는 것처럼

결합 분리 & 개발, 배포 독립에 대한 큰 믿음을 가지게 하지만,

횡단 관심사 이슈를 만나면 그게 와장창 깨지기 때문이 아닐까..?

 

하지만 이를 대응할 수 있는

컴포넌트 기반 서비스를 만든다면  안티패턴이 아닐 수 있다. 

 

 

 

 

 

[ 더보면 좋을 것 ]

 

Common Microservices Anti-Patterns 에 대해 나와있는 글! 

 

https://www.developer.com/design/solving-microservices-anti-patterns/

 

Overcoming the Common Microservices Anti-Patterns | Developer.com

A look into some of the common anti-patterns when working with Microservices architecture. Learn how to overcome them.

www.developer.com

 

 

 

반응형
댓글