Service 분리 전략
하나의 어플리케이션을 어떻게 서비스로 분할 할 것인가?
마이크로 서비스 아키텍처는 여러개의 독립된 서비스들이 함께 커뮤니케이션 하면서 공동의 목표를 달성하는 시스템 아키텍처이다.
공동의 목표가 어플리케이션에 해당할 것이고, 이런 공동의 목표를 달성하기 위한 여러개의 서비스를 어떻게 분리하느냐의 문제를 이야기 하고자 한다.
서비스 분리는 단순히 서비스의 기능적인 문제를 넘어서 조직구성에 대한 문제로 확대해서 봐야하기 때문에 생각보다 쉽지 않다.
그러나 서비스의 분리를 위한 대 원칙과 전제가 존재하며 우선 이 내용에 대해서 알아보자.
마이크로 서비스 구성
- 마이크로 서비스를 구성하는 서비스 컴포넌트는 단순해야하며, 테스트가 쉽고, 독립적으로 개발 배포 되어야한다.
- 서비스를 개발하고 운영하는 조직은 작은 조직들로 구성이 되며 한 조직에 약 6 ~ 10명의 멤버가 존재한다.
- 각 팀은 autonomous(자율성) 를 가지고 서비스를 운영한다.
- 각 팀은 하나 혹은 몇개의 서비스를 운영한다.
서비스 아키텍처 원칙
- 서비스는 하나의 팀이 개발하고, 테스트하며, 쉽게 배포할 수 있는 작은 조직이 운영할 수 있는정도가 되어야한다.
- SRP(Single Responsibility Principle) 가이드 라인에 맞추어 서비스의 변경은 하나의 사유로 인해서 변경이 되어야하며, 변경은 하나의 클래스 혹은 서비스의 변경에만 영향을 줄 수 있도록 한다.
- CCP(Common Closure Princpile) 컴포넌트의 클래스는 동일한 종류의 변경에 대해서 닫혀 있어야한다. (즉 하나의 변경이 연관이 강한 클래스들의 영역내에서 한정적으로 수정되어야한다)
- 강한 응집도: 서비스는 매우 밀접한 기능들끼리 함께 구현이 되어야한다.
- 느슨한 결합도: 서비스의 변경이 다른 서비스에 영향이 가지 않도록 구성이 되어야한다.
- 서비스는 자체적으로 테스트가 가능해야한다.
- 각 서비스는 2피자 팀과 같이 소규모 팀으로 구성이 되어야한다. 팀은 약 (6 ~ 10명이 적당하다.)
- 각 팀은 하나 혹은 몇개의 서비스를 반드시 자치적으로 운영해야한다. (다른 팀과의 협업을 최소화 하도록 구성되는 것이 좋다.)
이러한 구성원칙을 바탕으로 어떻게 서비스를 분할할지 고민해 보는 것은 좋은 접근이다.
잘못된 서비스 분할
잘못된 서비스 분할을 예로 들고 싶은 것은 순수 기능단위의 분리가 있다. 기능 단위로 서비스를 분리하게 되면 어느순간 관리해야할 컴포넌트 수가 엄청나게 불어 있는 것을 알게 될 것이다.
그리고 분리된 기능들이 상호 관련된 작업들을 호출하기 위해서 서비스 네트워크가 매우 복잡하게 된다.
가장 큰 문제는 다음 몇가지 상황일때 이다.
작은 조직에서 많은 서비스 기능들로 분리된 아키텍처
이런 경우에는 소수의 인원이 매우 큰 시스템을 다루는 상황이 연출된다. 한명이서 3 ~ 4개 이상의 서비스 컴포넌트들 개발하고, 배포하는 상황이 발생된다. 이런 경우에는 장애 상황을 인지하고, 장애의 원인을 파악하는 것이 매우 어렵다. 하나의 조직이 수십개로 나눠진 컴포넌트를 모두 다루어야 한다면 어떤 선택을 해야할까? 시스템 개선이라는 것이 일어날 수 있는 구조일까?
이렇게 여러 서비스를 작은 조직이 관리해야하는 상황이 발생하면 시간이 지날수록 서비스를 개선하고, 운영이 더욱 어려워 진 구조가 될 것이다.
생각해보면 이렇게 마이크로 서비스 아키텍처를 분리한 곳이 의외로 많음에 놀랄 수도 있다.
관련된 여러 서비스 주체들로 인한 생산성 저하.
가장 많은 어려움중에 하나는 서비스를 개선하기 위해서 개선 대상이 되는 서비스가 여러 팀에 걸쳐있는 경우이다. 잘 분리된 서비스도 각 팀의 KPI 에 따라서 타 팀이 요구하는 요구사항을 들어주기는 쉽지 않다. 작은 변경에도 여러 팀의 도움이 필요하다면, 몇시간이면 개발 될 내용이 수일이 소요될 수 있다. 필자의 경우에는 타 팀과 서비스 개선을 위해서 특정 API 를 요청했으나, 타 팀이 미리 잡아둔 KPI와 업무 스케줄에 밀려 3개월을 기다린 적도 있다. 이럴때에는 조직 차원이 지원까지 필요한 상황임을 암시한다.
코드의 변경이 대규모 통합 테스트 수준으로 확대 되는 경우
서비스들 사이의 커플링이 심할 수록 모놀리식 아키텍처와 마찬가지로 하나의 변경이 다른 템 서비스에 영향을 주게 되는 경우가 많다. 보통 이는 버젼관리를 올바르게 하지 않은 케이스이거나, 마이너 버젼의 스펙 변경이 타 팀의 비즈니스에 영향을 주는 경우이다. 이런 경우라면 서비스들이 서로 강하게 커플링 되어 있다는 의미로 봐야한다. 작은 변경에도 통합 테스트를 해야하는 상황이 온 것이다.
이 외에도 잘못된 서비스 분할 케이스는 찾아보면 그리 어렵지 않게 찾아볼 수 있을 것이다. 결코 이런 형태로 서비스가 분리되지 않도록 처음 이야기한 원칙을 잘 지킬 수 있는 상황인지 점검해볼 필요가 있다.
서비스 Agility
마이크로 서비스로 서비스를 분리하는 이유는 무엇일까?
처음 마이크로 서비스 아키텍처와 모놀리식 아키텍처를 선택하는 이유에서 밝힌바와 같이 모놀리식 아키텍처로 충분한 서비스가 있고, 마이크로 서비스 아키텍처로 분할 해야하는 경우도 있다. 모놀리식 아키텍처에서 작업상의 충돌과 비즈니스 생산성에 대한 이슈로 인해서 마이크로 서비스로 전개해 나가는 경우를 이야기 했었다. 그리고 미리 비즈니스 요구사항을 명확히 구분하고, 처음부터 마이크로 서비스로 전개하는 경우도 이야기 했었다.
정리해보면 마이크로 서비스 분리는 비즈니스 생산성을 위한 목표가 가장 큰 이유라고 할 것이다.
최근에는 비즈니스 Agility 를 매우 강조한다. 서비스의 변경은 하루가 멀다하고 빈번하게 변경이 이루어지며, 고객의 요구사항을 분석하고, 빠르게 대응하여 시장에 내어 놓아야 하는 상황에 놓이게 된다. 이럴 경우 어떤 아키텍처가 가장 효과적인지를 고민해야할 것이다.
이런 이야기는 어떻게 보면 일반론처럼 보인다. 그러나 이는 실제 상황이며 일반론이 아니라고 말하고 싶다.
필자가 있었던 쇼핑몰 회사에서는 상품, 주문, 취소반품, 배송, 고객센터, 판매자센터 등 다양한 비즈니스가 있었다. 이들 각각은 모두 개별적인 서비스의 목표가 있다.
상품팀의 목표
- 상품팀은 판매자가 올린 상품을 어떻게 하면 쉽게 검색할 수 있는지 연구한다.
- 상품팀은 판매자가 올린 상품을 어떻게 전시해야 구매자가 매력을 느끼고 구매할지 연구한다.
- 상품팀은 구매자가 제품의 상세 내용을 직관적으로 이해하고, 구매에 결정적인 선택을 할 수 있도록 다양한 구매자의 동선을 연구한다.
- 기타 등등..
판매자 센터의 목표
- 판매자 센터는 판매자가 상품을 어떻게 쉡게 올릴 수 있는지 연구한다.
- 판매자 센터는 판매자가 상품 가격을 잘못 올렸을 경우 판매자에게 미리 노티하여, 사고를 방지할 수 있는 방법을 연구한다.
- 판매자 센터는 판매자가 판매자에게 이해하기 쉽게 재무/재표를 보여줄 수있도록 연구한다.
- 기타 등등..
자 위의 케이스만 살펴보자. 이런 업무를 수행하기 위해서 각 팀은 어떤 일을 해야하고, 어떻게 의사소통 해야하는가? 그리고 자신들의 서비스에 새로운 기술을 빠르게 개발하고, 고객에게 선보이기 위해서는 어떻게 일을 해야할까? 이러한 것이 모두 비즈니스 Agility가 필요한 작업이며, 아마도 서비스가 발전해야할 근본적인 방향임을 이해할 수 있을 것이다.
서비스 어질리티를 달성하기 위해서 팀의 구성과 서비스의 분리는 어떻게 이루어 져야 하는가에 대해서 깊이 있는 고민이 필요하다.
필자 개인적인 생각으로 이러한 비즈니스 어질리티를 달성할 수 있도록 설계하는 것이 가장 중요한 포인트라고 생각하고 있다.
비즈니스 영역별로 서비스 분할
비즈니스 어질리티, 서비스 어질리티를 이야기 하면서 분할 수 있는 하나의 방법은 비즈니스 영역별로 서비스를 분리하는 것이다.
from : https://microservices.io/i/decompose-by-business-capability.png
비즈니스 영역으로 나누기
- 비즈니스 영역을 정의한다. 비즈니스 영역의 범위를 명확히 하고, 어떻게 RnR 을 분리해야할지에 대한 논의가 필요하다.
- 비즈니스 영역은 가치를 창출 하는 것이어야한다. 이전에 설명한 것과 같이 비즈니스는 해당 비즈니스 행위가 고객이나 회사에 가치를 창출해 줄 수 있어야한다.
- 비즈니스 영역은 동일한 강한 연관성이 있는 서비스들로 영역을 잡는다.
- 비즈니스 영역은 하나의 팀이 자체적으로 구현할 수 있는 정도로 분리된다.
- 타 팀과의 상호 협업은 있을 수 있으나, 가능하면 자체 팀이 해결할 수 있는 범위로 업무를 나누는 것이 좋다.
이점
- 비즈니스 기능이 비교적 안정적이므로 안정적인 아키텍처
- 개발 팀들은 Cross-Functional 팀이며, 차지적으로 동작한다. 그리고 기술적인 기능보다 비즈니스 가치를 전달할 수 있다.
- 서비스들은 느슨한 결합과, 강한 결합도를 가진다.
이러한 비즈니스 영역별 서비스 분리 방법을 취하는 경우 팀의 구성과 자율성이 매우 중요하다.
즉, 비즈니스 가치 창출을 위해서 팀은 자치적으로 운영되고, 데이터를 바탕으로 자체 의사결정을 할 수 있도록 구성이 되어야한다.
크로스 펑셔널 팀으로 구성이 되는 경우 이러한 자치적 운영과 비즈니스 퀄리티를 향상할 수 있다.
크러스 펑셔널 팀이란 하나의 팀을 구성할 때 구성원 개개인 마다 다양한 영역의 전문가들로 모여져 있으며, 이들 팀들이 함께 자신의 전문영역을 발휘해 비즈니스 분석과 개선사항을 수행하고, 개발, 테스트, 배포를 진행하는 것을 말한다. 비즈니스 요구사항을 분석할 수 있는 전문가, 그리고 비즈니스 지표 데이터를 수집하고 분석할 수 있는 능력, 이를 개선하기 위한 기술적인 기술자등 여러 기술들을 보유한 팀원들이 하나의 조직을 이루도록 구성하는 것이 좋다.
개인적인 생각으로는 비즈니스 영역별로 서비스를 구성하는 것이 상당히 좋은 방향이지만, 조직의 구조상 쉽지 않은 영역이라고 생각이 든다. 조직의 이합 집산은 경영권자의 승인이 필요한 작업이기 때문이다.
Domain Driven Design 서브 도메인 분할
도메인 드리븐 디자인 방식은 비즈니스를 도메인으로 생각하거나, 각각의 관심사에 따른 업무 영역을 도메인으로 분리하여 디자인을 하는 방식이다. 어떻게 보면 비즈니스 영역을 서비스로 분리하는 것과 유사하다.
몇가지 차이점을 본다면 도메인을 좀더 중점적으로 바라보는 것이다.
- Core Domain: 비즈니스의 핵심 부라고 할 수 있으며, 비즈니스 존재 이유가 되는 영역이며, 코어 도메인을 위해서 주변 서브 도메인들이 연합하는 구조라고 할 수 있다.
- Supporting Domain: 연관된 비즈니스 도메인이며, 핵심 도메인을 더욱 잘 구현하고 표출하기 위한 수단으로서의 도메인이다.
- Generic Domain: 비즈니스 이외에 주면의 영역이며, 언제든지 변경되거나 교체될 수 있는 도메인을 말한다.
from: https://microservices.io/i/decompose-by-subdomain.png
이렇게 서비스를 구분하면, 하나의 어플리케이션에서 코어 도메인은 오직 한개가 될 수도 있고, 몇개의 코어 도메인들이 분리될 수도 있다. 그러나 가장 중요한 포인트는 Core Domain 을 기준으로 서비스가 분리되고, 이들을 보조하는 서비들도 분리되어 별도 팀이 관리하거나, 혹은 코어 도메인을 담당하는 팀에서 해당 서비스를 함게 개발 운영할 수도 있다.
장점
- 비즈니스 기능이 비교적 안정적이므로 안정적인 아키텍처
- 개발 팀들은 Cross-Functional 팀이며, 차지적으로 동작한다. 그리고 기술적인 기능보다 비즈니스 가치를 전달할 수 있다.
- 서비스들은 느슨한 결합과, 강한 결합도를 가진다.
도메인을 기준으로 나누는 방식도 개략적으로 알아 보았다.
비즈니스 영역별 분리 방법과 도메인 드리븐 서비스 분리 방법은 마이크로 서비스를 분리하는 목적에 가장 이상적인 모델로 볼 수 있으며, 비즈니스 Agility 역시 달성할 수 있는 방법이라고 할 수 있다.
다만 고려해야하는 사항은 조직의 방향성이 잘 반영이 될 수 있어야 하며, 이런 방향성으로 인해서 잘 맞지 않을 수 있는 부분이 있을 수 있다.
팀별 서비스 분할
팀별 서비스 분할은 기존의 비즈니스 업무 조직에 따라서 서비스를 분리하는 것을 말한다. 서비스 개편 혹은 IT 로 전환하는 업무인경우 더욱 잘 맞을 수 있으며, 기존에 불합리하게 수행하던 업무를 재정리하여 서비스로 분리하는 작업 역시 이에 해당한다.
from : https://microservices.io/i/ServicePerTeam.png
콘 웨이 법칙을 보면 시스템 아키텍처는 조직의 커뮤니케이션 구조를 닮는다라고 하며, 이 의미는 오랫동안 업무를 수행해 오던 조직이며, 업무를 개선해 나가는 조직의 경우 명확한 비즈니스 워크 플로우와 RnR 을 나누고 있기 때문에 서비스를 분리하는 경우 가장 자연스럽게 서비스가 분리 된다라고 할 수 있다.
팀별 서비스 분할에도 마이크로 서비스를 성공적으로 도입하기 위해서는 다음과 같은 사항들이 필요하다.
핵심사항
- 팀은 반드시 작게 유지 되어야한다. 5-9명
- 팀은 자치적으로 운영되고, 느슨하게 결합되어야한다.
- 팀의 크기와 복잡도에 따라 팀의 코드 베이스가 유지 되어야하며, 팀의 능력을 넘어서면 안된다.
- 잘 정제된 서비스는 -ilities 를 향상시킨다. maintainability, testability, deployability
해결책
- 각 서비스는 팀에 의해서 소유된다.
- 이것은 변경에 대해서 단일 책임을 가진다.
- 이상적인 것은 팀은 오직 하나의 서비스만을 가지는 것이다.
- 각 팀은 하나 혹은 여러가지 비즈니스 기능을 책임진다. (비즈니스 수용성)
- 팀은 단일 변경에 대한 책임을 가진다. 이는 코드베이스가 하나 혹은 몇개의 모듈로 구성됨을 의미한다.
- 해당 코드 베이스는 팀의 역량을 넘어서지 않아야한다.
- 팀의 코드 배포는 하나 혹은 몇개의 서비스로 배포 된다.
- 팀은 반드시 하나의 서비스를 가지는 것을 기본으로 하며, 분리가 필요한경우 분리가 주는 장점이 반드시 필요하다.
장점
- 각 팀은 자치권을 가지고, 다른팀과 최소한의 영향도를 가진다.
- 팀은 느슨하게 결합된다.
- 팀의 자치권과 느슨한 결합을 가지면서 서비스의 최소한의 개수를 가지도록 해야한다.
- 코드 퀄리티를 향상하도록 하며 코드의 오너쉽을 가진다.
단점
- 팀들은 엔드유저 기능과 일치하지 않을 수 있다.
- 서비스에 걸쳐 있는 기능을 구현하는 것은 더 많은 복잡도와 다른팀과의 협업이 필요하다.
결론
지금까지 몇가지 서비스 분리 방법에 대해서 알아 보았다.
무엇이 가장 좋은 선택인지는 조직의 성격이 크게 작용하며, 조직이 나아가고자 하는 비즈니스 미래상에 따라 달라진다고 할 수 있을 것이다.
그리고 모두 서비스를 분리하기 위해서 기본적으로 갖추어야할 핵삼 시항들을 대부분 유사하다고 할 수 있다.
작은 조직과 서비스 자치권을 가진 조직, 느슨한 결합도와 높은 응집도 등을 제공할 수 있도록 해야 한다는 것도 알아보았다.
그러나 반드시 중점적으로 고민해야하는 사항은 분리된 서비스가 가치를 창출할 수 있는 방향인지가 중요한 포인트이다. 가치 창출은 Agility 역시 매우 중요하며, 어떻게 Agility를 달성할 수 있는지에 대해서 고민하고 가장 적합한 방법을 도입할 필요가 있을 것이다.
참고자료
- https://microservices.io/patterns/decomposition/decompose-by-business-capability.html