본문 바로가기
Cloud/MSA

마이크로서비스의 실패 사례와 모듈형 모놀리스 제안! 우아한 모놀리스 세미나

by 민휘 2024. 1. 17.

 

 

 

마이크로서비스 → 모놀리스 ?! 😱
본질은 “얼마나 빨리 변경을 수용해 고객에게 비즈니스적 영향을 전달할 수 있는가?”
충분한 도메인 분석과 관리 인력이 뒷받침해주지 않으면 마이크로서비스가 비즈니스 민첩성의 발목을 잡을 수 있다.
처음 만드는 시스템이라면, 모듈화된 모놀리스 서비스로 출발해 트래픽 변동에 따라 유연하게 분리하고 통합하는 방법을 시도해볼 것을 권장한다.
얼마 전 클라우드 커뮤니티 세미나에서 DDD 발표하신 분이 계셨다. 현업에서는 MSA를 어떻게 사용하고 있나 궁금해서 이것저것 질문했더니 코드 일부분을 보여주셨는데, 너무 복잡해서 깜짝 놀랐다. 코드가 더러워지기 딱 좋아보이고, 인프라 관리도 굉장히 복잡해보였다. 발표자분께서도 복잡성에 대해서 인정하셨고, 그 부분이 관건이라고 하셨다. 복잡성을 견디지 못하면 마이크로서비스 유연함과 견고함이라는 장점을 누리지 못할 것 같았다. 그래서 마이크로서비스의 실패 사례는 없나 찾아보다가 이 세미나를 발견했다.

 

 

문제 있는 마이크로서비스

  • 여러 서비스가 하나의 데이터 테이블을 공유해서 비슷한 로직이 여러군데 나타남. 서비스 간의 순환 의존성 발생. 서비스가 함께 변경되고 배포되어야함 → 비즈니스 민첩성 달성 불가
  • 개발 인원에 비해 서비스의 수가 너무 많아서 마이크로서비스에 기술 부채가 쌓임. 요구사항을 빠르게 구현하기 위해 배치를 크게 고민하지 않고 코드를 고침 → 기술 부채, 코드 중복 발생
  • 즉, 원래 하나로 만들어야하는 서비스를 강제로 쪼갰기 때문에 발생한 문제. 그래서 모놀리스로 다시 합친 것.

 

마이크로서비스 vs 모놀리스

주어진 상황에 따라 적절한 도구를 선택하자

 

아키텍처 스타일이 문제를 해결해주지 않는다

  • 마이크로서비스의 주요 혜택 : 조직의 부합성(콘웨이 법칙 - 시스템 구조와 조직 구조를 흡사하게 맞춤), 대체 가능성, 조합성, 배포 용이성, 학습 곡선 낮음, 변경 비용 감소
  • 이와 상반되는 모놀리스의 단점 : 일부 기능을 변경했을 때 다른 기능도 테스트 필요함. 장애 전파. 전체 수평 확장만 됨. 느린 배포 사이클
  • 아키텍처를 바꿀 때 고민을 많이 하지 않으면 분산된 모놀리스 시스템을 만날 가능성이 매우 높다. 그래서 아키텍처 스타일보다 먼저 고민해야할 것이 응집과 결합을 다스리는 것!
  • 모놀리스를 잘못 만드는 팀은 마이크로서비스도 잘못 만들 것이다.

 

 

선택 : 모듈형 모놀리스

💡 좋은 아키텍처는 시스템이 모노리틱 구조로 태어나서 단일 파일로 배포되더라도, 이후에는 독립적으로 배포 가능한 단위들의 집합으로 성장하고, 또 독립적인 서비스나 마이크로서비스 수준까지 성장할 수 있도록 만들어져야 한다. 또한 좋은 아키텍처라면 나중에 상황이 바뀌었을 때 이 진행 방향을 거꾸로 돌려 원래 형태인 모노리틱 구조로 되돌릴 수도 있어야 한다. - 도서 <클린 아키텍처>

 

 

모놀리스의 의존성 관리하기

  • 모듈화 : 코드베이스를 쪼개 캡슐화하여 협력한다. 모듈은 API로만 협력한다. 모듈은 독립적으로 수정되고 테스트된다. 응집도는 높고 모듈 간 결합도는 낮다.
  • 관심사 분리로 변경에 유연한 아키텍처 : 코어와 기술적인 영역은 분리되고 독립적으로 수정 가능해야 한다.

 

 

모듈형 모놀리스 구성하기 with 이커머스

 

GitHub - arawn/building-modular-monoliths-using-spring: 스프링을 기반으로 모듈형 모노리스를 만들기 위한 방

스프링을 기반으로 모듈형 모노리스를 만들기 위한 방안을 공유합니다. Contribute to arawn/building-modular-monoliths-using-spring development by creating an account on GitHub.

github.com

 

1. 모듈 구성

도메인 중심으로 모듈을 구성해 강한 응집도 얻기 - 핵심은 비즈니스 요구사항 전달

  • domain.entity : 도메인 클래스
  • domain.usecase : 유스케이스 흐름에 맞춰 도메인 메소드 호출. 서비스
  • integrate : 다른 모듈과의 호출 관계를 나타내는 인터페이스

 

관심사에 따른 모듈화 - 핵심은 재사용

  • 수직적 관심사 : 비즈니스 특화
  • 수평적 관심사 : 로깅, 보안 등 공통적인 저수준 기능 → 도메인 패키지와 같은 위치에 commons 패키지

 

2. 의존성 관리로 모듈을 느슨하게 결합하기

우아한 객체지향에서 소개된 방법이닷!

  • 중간 객체
  • 의존성 역전

의존성 사이클이 발생하지 않게 주의. 의존성 역전도 적용됨.

 

3. 적절한 가시성으로 모듈 보호하기

  • 자바 접근 지시자 활용 : 패키지 수준으로 제한
  • gradle 멀티 프로젝트 구성해서 모듈 간 의존관계 끊기. 모듈의 인터페이스 매핑을 위해 반부패 계층 구현. 통합은 main에서
  • 자바9의 모듈화 : 노출할 인터페이스만 export, 접근 지시자와는 다름
  • 스프링 컨텍스트 계층 구조 : 루트 컨텍스트(공개 API를 위한 커스텀 애노테이션) → 각 모듈의 컨텍스트
  • 컨텍스트 설정 모듈화 : 컨텍스트 구성 설정은 각 모듈에서 수행
  • 이 상태에서 마이크로서비스로 쪼갠다면? 각 프로젝트 안에 main 메소드를 넣으면 끝!

 

 

모듈형 애플리케이션 구축을 지원하는 도구

  • Moduliths : 하나의 컨텍스트 사용하되 경계를 침범하지 못하게 도구로 제어
  • Across Framework : 컨텍스트 쪼개는 접근