응집도와 결합도, 그리고 SRP

마이크로서비스 아키텍처(MSA)를 성공적으로 구축하고 운영하기 위해서는 다양한 설계 원칙과 고려 사항들이 존재합니다. 그중에서도 단일 책임 원칙(SRP)을 제대로 구현하기 위한 핵심 개념인 응집도(Cohesion)와 결합도(Coupling)는 반드시 깊이 있게 이해해야 합니다. 이 두 가지 개념은 MSA의 각 서비스가 얼마나 독립적이고, 변경에 얼마나 유연하게 대응할 수 있는지를 결정짓는 중요한 요소이기 때문입니다.

응집도(Cohesion): 하나의 서비스를 얼마나 밀접하게 묶을 것인가?

응집도는 하나의 서비스 내에서 서로 관련된 책임과 기능들이 얼마나 강하게 묶여 있는지를 나타내는 척도입니다. MSA에서 높은 응집도는 한 서비스가 특정 비즈니스 목표나 기능과 관련된 모든 책임을 완벽하게 수행하는 것을 의미합니다. 즉, 서비스 내의 코드와 로직이 하나의 일관된 목적을 위해 긴밀하게 협력하는 상태를 의미합니다. 반대로 낮은 응집도는 하나의 서비스가 너무 많은 책임을 떠맡거나, 서로 관련 없는 기능을 포함하고 있음을 의미합니다. 이 경우, 서비스 내부의 코드는 여러 목적을 위해 혼재되어 있어 유지보수가 어렵고, 작은 변경에도 큰 영향을 미칠 수 있습니다.

예를 들어, ‘사용자 관리’ 서비스를 생각해 봅시다. 높은 응집도를 가진 ‘사용자 관리’ 서비스는 사용자 생성, 사용자 정보 업데이트, 사용자 인증 및 권한 관리 등 사용자 관련 기능만을 담당합니다. 반면, 낮은 응집도를 가진 ‘사용자 관리’ 서비스는 사용자 정보 외에 상품 정보 관리, 주문 처리 등 관련 없는 기능까지 담당할 수 있습니다. 이 경우, 사용자 인증 로직의 변경이 상품 정보 관리 코드에 영향을 줄 수 있으며, 서비스의 복잡도가 증가하여 유지보수가 어려워집니다.

결합도(Coupling): 서비스 간의 의존성은 어떻게 관리할 것인가?

결합도는 서비스 간의 상호 의존성 정도를 나타내는 척도입니다. MSA에서 낮은 결합도는 각 서비스가 다른 서비스에 대해 최소한의 의존성만 가지는 것을 의미합니다. 즉, 한 서비스의 변경이 다른 서비스에 미치는 영향을 최소화하여 각 서비스가 독립적으로 개발, 배포 및 확장될 수 있는 상태입니다. 반대로 높은 결합도는 서비스 간에 강한 의존성을 가지고 있음을 의미합니다. 이 경우, 한 서비스의 변경이 다른 서비스에 큰 영향을 미치며, 전체 시스템의 복잡성과 불안정성이 증가하게 됩니다.

예를 들어, ‘주문 처리’ 서비스와 ‘결제 처리’ 서비스가 있다고 가정해 봅시다. 낮은 결합도를 가진 설계에서는 ‘주문 처리’ 서비스가 ‘결제 처리’ 서비스의 API를 호출하여 결제를 요청하고, ‘결제 처리’ 서비스는 결제 결과만 ‘주문 처리’ 서비스에 전달합니다. 반면, 높은 결합도를 가진 설계에서는 ‘주문 처리’ 서비스가 ‘결제 처리’ 서비스 내부의 데이터베이스에 직접 접근하거나, ‘결제 처리’ 서비스의 로직에 깊이 의존하게 됩니다. 이 경우, ‘결제 처리’ 서비스의 변경이 ‘주문 처리’ 서비스에 큰 영향을 미칠 수 있으며, 서비스 간의 디커플링이 어려워집니다.

축구 팀 비유로 이해하는 MSA의 응집도와 결합도

축구 팀을 떠올려 보자. 한 팀에는 공격수, 수비수, 골키퍼 등 각자 명확한 역할을 가진 선수들이 있다. 공격수의 유일한 목표는 골을 넣는 것이고, 수비수는 상대의 공격을 차단하는 데 집중하며, 골키퍼는 골대를 지키는 것이 최우선 임무다. 이처럼 각 포지션이 하나의 책임에만 집중하는 것이 바로 높은 응집도다. 마이크로서비스 아키텍처에서도 마찬가지다. 결제 서비스는 “결제”라는 단일 기능에 모든 에너지를 쏟아야 한다. 결제 승인, 취소, 보안 처리 등 결제와 직접적으로 관련된 작업만을 수행한다. 배송 서비스는 오로지 “배송 추적”에만 집중한다. 만약 공격수가 수비수의 역할까지 겸한다면 어떻게 될까? 경기 중 혼란이 생기고, 골을 넣는 본연의 임무를 제대로 수행하지 못할 것이다. MSA에서도 서비스가 자신의 역할을 벗어난 기능을 담당하면 코드는 복잡해지고, 변경이 발생할 때마다 예상치 못한 부작용이 발생한다.

이제 팀 내 협업 방식을 생각해보자. 선수들은 공이라는 매개체를 통해 소통한다. 미드필더가 공격수에게 공을 패스할 때, 공격수는 미드필더가 어떤 식으로 공을 보낼지 세부적인 방식을 알 필요가 없다. 그저 공을 받아 슛을 날리는 방법만 알면 된다. 심지어 미드필더가 교체되더라도, 새로운 미드필더의 패스 스타일만 익히면 기존 협업 방식을 유지할 수 있다. 이것이 낮은 결합도의 핵심이다. MSA에서 서비스 간 통신도 이와 같다. 결제 서비스와 주문 서비스가 REST API나 이벤트 메시지로 소통한다면, 한 서비스의 내부 로직이 변경되더라도 다른 서비스에는 영향을 미치지 않는다. 예를 들어 결제 서비스가 데이터베이스를 MySQL에서 MongoDB로 변경해도, 주문 서비스는 여전히 “결제 ID”만 전달하면 된다. 반면 선수들이 서로의 움직임에 과도하게 의존한다면 어떨까? 특정 미드필더가 없으면 공격수가 골을 넣지 못하는 상황이 발생할 것이다. MSA에서도 한 서비스가 다른 서비스의 데이터베이스를 직접 조회하거나 내부 구조에 의존하면, 작은 변경이 전체 시스템의 불안정으로 이어질 수 있다.

이 비유는 MSA의 핵심 원리를 단숨에 이해시킨다. 각 서비스는 축구 선수처럼 한 가지 일에 집중하고(높은 응집도), 다른 서비스와는 공(표준 인터페이스)을 통해 유연하게 협업해야(낮은 결합도) 전체 시스템이 효율적으로 작동한다.

  • 높은 응집도와 낮은 결합도를 지향해야 하는 이유: MSA에서 높은 응집도와 낮은 결합도는 서비스의 독립성을 보장하고 전체 시스템의 유연성과 안정성을 높이는 데 필수적입니다.
    • 독립적인 개발 및 배포: 각 서비스가 자신의 책임에만 집중하고 다른 서비스에 대한 의존성이 적으면, 개발팀은 각 서비스를 독립적으로 개발하고 배포할 수 있습니다. 이는 개발 속도를 높이고 배포 주기를 단축시켜 더 빠른 시장 출시를 가능하게 합니다.
    • 유연한 확장 및 변경: 각 서비스가 독립적으로 개발되고 배포될 수 있다면, 특정 서비스에 트래픽이 몰릴 경우 해당 서비스만 확장할 수 있습니다. 또한, 특정 서비스의 기능을 변경하거나 새로운 기능을 추가할 때 다른 서비스에 미치는 영향을 최소화하여 변경에 대한 부담을 줄일 수 있습니다.
    • 쉬운 유지보수 및 오류 격리: 높은 응집도는 서비스 내부의 코드가 명확하고 이해하기 쉽게 만들어 유지보수를 용이하게 합니다. 또한, 낮은 결합도는 서비스 간의 의존성을 줄여 한 서비스에 오류가 발생했을 때 다른 서비스로의 전파를 방지하고 오류 격리를 쉽게 해줍니다.
  • 좋은 응집도와 낮은 결합도의 예
    • 주문 서비스: 주문 생성, 주문 조회, 주문 취소 등 주문 관련 기능만을 담당합니다. 결제 서비스와는 API를 통해 상호작용하며, 주문 상태 변화에 대한 이벤트만 구독합니다.
    • 결제 서비스: 카드 결제, 계좌 이체 등 결제 관련 기능만을 담당합니다. 주문 서비스와는 API를 통해 상호작용하며, 결제 완료 또는 실패에 대한 이벤트만 발행합니다.
    • 상품 서비스: 상품 등록, 상품 조회, 상품 정보 업데이트 등 상품 관련 기능만을 담당합니다. 주문 서비스나 결제 서비스와는 직접적인 데이터 교환 없이 API를 통해 상호작용합니다.
  • 나쁜 응집도와 높은 결합도의 예
    • 통합 서비스: 사용자 관리, 상품 관리, 주문 처리, 결제 처리 등 다양한 기능을 하나의 서비스에서 처리합니다. 서비스 내부 코드가 복잡하고, 다른 서비스와 직접적인 데이터베이스 접근을 통해 결합되어 있습니다.
    • 모놀리식 API: 모든 서비스의 기능을 하나의 API를 통해 노출하여 서비스 간의 의존성을 높입니다. 각 서비스의 변경이 API에 영향을 미치고, API를 사용하는 다른 서비스에도 연쇄적으로 영향을 미칠 수 있습니다.

결론

MSA는 복잡한 시스템을 관리 가능한 작은 조각으로 분할하여 유연성과 확장성을 확보하는 것이 핵심입니다. 하지만 서비스가 적절한 응집도를 가지지 못하고, 서비스 간에 과도하게 결합되어 있다면 MSA의 장점을 제대로 누릴 수 없습니다. 응집도와 결합도는 단순한 이론적인 개념이 아니라, MSA의 성공적인 구축과 운영을 위한 실질적인 가이드라인입니다. 따라서 MSA를 설계하고 구현하는 모든 과정에서 이 두 가지 원칙을 항상 염두에 두어야 합니다.