Architecture - 모놀리스에서 MSA 로 가는 2가지 방법


많은 조직이 MSA 의 유연성과 확장성에 매료되어 기존 모놀리스 시스템을 점진적으로 전환하고자 한다.
그러나 무분별한 분할은 오히려 혼란만 가중시키고, 전략없는 전환은 빅볼 오브 머드 상태를 더 악화시킬 수 있다.

<MSA 는 어떤 상황에서 적절할까?>
MSA 는 단순한 트렌드가 아니라, 아래와 같은 상황에서 강력한 선택지가 된다.

  • 도메인에 따라 변화속도가 다를 때
    • 어떤 비즈니스는 자주 바뀌고, 어떤 기능은 거의 변하지 않을 수 있다. 이럴 경우 각기 다른 속도를 가진 팀이 충돌없이 독립적으로 일할 수 있도록 아키텍처를 분리하는 것이 효과적이다.
  • 독립적인 배포 단위가 필요할 때
    • 각 MSA 는 다른 서비스와 별도로 배포될 수 있기 때문에, 자율적인 개발 및 출시가 가능해진다.


<전략적 출발점: 모듈형 모놀리스>
모놀리스를 무작정 해체하기보다, 먼저 모듈화된 단일 애플리케이션으로 구조를 정비하는 것이 중요하다.
이는 아래와 같은 이유에서 전환의 기초가 된다.

  • 기능 간 경계가 명확해진다.
  • 서비스 분할 후보를 판별할 수 있다.
  • 일관된 리팩토링과 테스트가 가능하다.

즉, 모듈형 모놀리스는 점진적 MSA 전환을 위한 가장 좋은 출발점이다.


이 포스트에서는 아래 2가지 시나리오를 중심으로 모놀리스에서 MSA 로 전환하기 위한 실질적인 접근 전략을 정리한다.

  • 잘 모듈화된 모놀리스에서 MSA 로 점진 전환
    • 각 모듈을 MSA 로 독립시키는 방식
    • 단계별로 외부 API 또는 메시징 기반 통신으로 이동
    • 테스트 커버리지를 유지하면서 점진적 전환 가능
    • 레거시 코드를 일정 기간 보존하면서 신규 서비스와 병행 운영 가능
  • 빅볼 오브 머드 상태의 모놀리스를 MSA 로 강제 전환
    • 기술 부채가 심각하고, 현재 구조로는 유지보수가 불가능한 경우
    • 기능 단위로 리버스 엔지니어링 → 도메인 추출 → 별도 MSA 구축
    • 기존 시스템을 빠르게 대체해야 하는 상황에서 사용
    • 단, 이 방식은 위험 부담이 크고 실패 가능성도 높기 때문에 팀의 경험과 준비가 중요함


<전환 중 고려해야 할 요소>

  • 기존 모놀리스 일부를 새 MSA 와 함께 유지할지, 완전히 대체할지 여부
  • DB 분리 및 트랜잭션 처리 방식 (saga, outbox 등)
  • 점진적 전환 시의 API Gateway 구성, 라우팅 전략
 전략적 모놀리스강제 MSA 전환
적합 상황일정 수준 이상 모듈화됨기술 부채가 심각함
접근 방식점진적 추출기능 단위 재작성
위험도낮음높음
장점안정적이고 예측 가능빠른 기술적 리셋 가능
단점시간 소요안정적 확보 어려움

목차


1. MSA 로 전환 전 알아야 할 분산 장애와 회복 전략

모놀리스에서 MSA 로의 전환은 단순한 리팩토링이 아니다.
이것은 아키텍처 뿐 아니라 개발 문화, 장애 복원력, 조직 협업 체계 전반에 걸친 구조적 변화를 수반한다.

여기서는 MSA 전환 시 반드시 준비해야 할 분산 시스템에서의 장애 복원 전략을 정리한다.
전환 초기부터 이 점들을 인식하지 않으면 MSA 는 해결책이 아니라 혼란의 원천이 될 수 있다.


정신적 준비: MSA 는 쉽지 않다.

MSA 는 문제를 해결하는 것이 아니라, 문제의 위치를 바꾸는 것이다.

MSA 로 전환하면 새로운 종류의 복잡성이 생긴다.
특히 런타임 장애 가능성이 급격히 증가하며, 개발자와 운영팀 모두 아래와 같은 현실을 받아들여야 한다.


분산 시스템의 본질: 네트워크는 믿을 수 없다.

분산 시스템에서 네트워크는 신뢰할 수 있는 요소가 아니다. 아래는 분산 환경에서 흔히 발생하는 문제들이다.

  • 불안정한 네트워크
    • 시간이 지남에 따라 예고없이 변경되며, 복구 전략이 없으는 장애가 확산됨
  • 예측 불가능한 지연
    • 지연은 상황에 따라 크게 변하고, 미리 대응하지 않으면 타임아웃 발생
  • 토폴로지 변경
    • 경로가 바뀌고 병목이 생기며, 라우팅 오류가 발생할 수 있음
  • 대역폭 변동
    • 트래픽 피크 타이밍에 따라 대역폭이 비정상적으로 떨어짐
  • 정책 충돌
    • 다수의 네트워크 관리자가 설정한 상충되는 정책으로 트래픽이 차단됨
  • 구축 실수
    • 설정 실수 하나로 재정적, 품질적 손실을 초래할 수 있

기술 전략: MSA 장애 복원력을 높이는 패턴

  • 장애 감시자(Failure Supervision)
    • 문제 발생 시 격리하여 전파 방지
    • 격벽을 통해 하나의 컴포넌트 장애가 전체 시스템으로 확산되지 않도록 함
  • 회로 차단기(Circuit Breaker)
    • 일정 수준 이상 오류 발생 시 해당 경로 차단
    • 서비스가 복구될 때까지 차단 후 재시도 허용
  • 기하급수적 백오프 재시도(Exponential Backoff)
    • 실패 시 지수적으로 증가하는 간격으로 재시도
    • 서버 과부하 방지 + 점진적 복구 지원
  • 멱등적 수신자(Idempotent Receiver)
    • 동일한 요청이 여러 번 도착해도 한 번만 처리하도록 설계
    • 메시지 중복 수신 시도에도 일관된 결과 봅장
  • 순서 무시하고 총합 판단
    • 순서에 의존하지 않고 모든 메시지 수신 여부로 상태 판단
    • 네트워크 재정렬 문제 회피
  • 부하 제어 전략
    • 로드셰딩(Loadshedding)
      • 불필요한 요청 무시
    • 오프로딩(Offloading)
      • 다른 워커로 작업 분산
    • 백프레셔(Backpressure)
      • 소비자가 수용 가능한 수준만큼만 요청 수신

팀 조직과 문화적 각오

새로운 기술을 적용하는 팀과 레거시를 유지하는 팀의 마찰은 필연적이다.
끊임없는 마이그레이션 작업, 예상치 못한 이슈 대응 등은 단기 목표보다 장기적 투자로 접근해야 한다.
이해관계자의 지원과 리더십 없이는 절대 성공할 수 없다.


2. 모듈형 모놀리스에서 MSA 로 전환

MSA 는 모든 것을 한꺼번에 바꾸는 작업이 아니다.
특히 모듈형 모놀리스 구조를 잘 활용하면 점진적이고 전략적인 분리가 가능하다.

여기서는 어떤 기준으로 컨텍스트를 추출하는지, 그리고 SaaS 서비스 도입 시의 고려 사항까지 함께 살펴본다.


초기 시스템 구조: 모듈형 모놀리스

5개의 팀이 아래와 같은 7개의 컨텍스트 모듈로 구성된 모듈성 모놀리스를 운영 중이라고 가정하자.

  • 접수
  • 보험 계약 심사
  • 보험 갱신
  • 리스트
  • 보험료율
  • 보험 계약자
  • 보험금 청구

변화의 필요: 분리 기준의 등장

아래와 같은 변화가 생기며, MSA 전환의 필요성이 생긴다.

 변화 요인요구 사항
리스크요청 증가, 잦은 변경독립 배포, 수평 확장
보험료율복잡한 계산, 잦은 변경독립 배포, 성능 최적화 필요(많은 리소스 필요)
보험 계약자보상 상품 확장 필요도메인 세분화
보상확장 예정인 신규 기능독립 컨텍스트 필요
보험금 청구기능 부족, 유지 어려움SaaS 솔루션으로 전환 예정

이로 인해 5개의 바운디드 컨텍스트를 MSA 로 추출하기로 결정한다.


전환 방식: 점진적인 컨텍스트 추출

처음부터 모든 컨텍스트를 옮기는 것이 아니라, 아래와 같이 하나씩 추출하는 보수적인 전략을 사용한다.

  • 리스크: 가장 먼저 추출, 확장이 시급
  • 보험료율: 리스크와 함께 분리하여 성능 확보
  • 보험 계약자: 보상과의 결합을 고려하여 리팩토링
  • 보상: 신규 컨텍스트로 신설
  • 보험료 청구: SaaS 기반 솔루션으로 점진적 전환

이 과정에서 아래 요소들을 함께 고려한다.

  • 각 컨텍스트는 자체 DB 를 소유해야 한다.
  • 새로운 MSA 는 보안 및 권한 제어 체계를 자체적으로 관리해야 한다.
  • 기존 모놀리스와의 연동을 위한 이벤트 교환 계층이 필요하다.

SaaS 도입 시의 구성 전략: 보험료 청구 컨텍스트 사례

상황은

  • 기존 보험료 청구 컨텍스트는 오래되었고, 기능이 부족함
  • SaaS 기반 청구 솔루션으로 교체를 계획하고 있음
  • 하지만 시스템 중단 없이 점진적으로 기능을 전환해야 함
[기존 모놀리스 청구 컨텍스트]
        ↓ 이벤트 발행
[청구 교환 컨텍스트] → SaaS 청구 API 호출
                             ↑
                     이벤트 레코드 스트림 제공
                             ↓
          [메시지 버스에 발행 → 내부 시스템에서 소비]

청구 교환 컨텍스트는 SaaS 시스템과 이벤트/Command 를 번역하는 어댑터 역할을 한다.
이 과정을 통해 기존 시스템을 보존한 채, 각 기능을 하나씩 새로운 SaaS 청구 서비스로 이동할 수 있다.


‘작은 낭비’를 통한 큰 안정

보험료 청구 컨텍스트는 최종적으로 폐기될 예정이지만, 이전 단계에서 이 컨텍스트를 일단 추출함으로써 시스템 중단없이 마이그레이션이 가능한 지 검증할 수 있다.
이는 낭비적인 단계처럼 보일 수 있지만, 실제로는 전체 전환을 안전하게 만드는 보험 역할을 한다.


3. 빅볼 오브 머드 시스템에서 MSA 로 전환

빅볼 오브 머드 시스템은 시간이 지나면서 기능이 뒤엉키고, 명확한 계층도 없으며, 구조적 의도없이 발전된 시스템을 뜻한다.
이런 시스템은 유지보수, 새로운 기능 추가, 테스트가 매우 어렵다.

하지만 바로 폐기할 수 없다면?
MSA 로 점진적으로 전환하는 것이 해답일 수 있다.
여기서는 혼란스러운 레거시 시스템을 비즈니스 중심 전략과 애자일 기반의 반복적 접근으로 전환하는 방법을 정리한다.

빅볼 오브 머드를 MSA 로 전환하는 6단계는 아래와 같다.


1단계. 모든 비즈니스 역량 파악(시스템 전역에 걸쳐 흩어진 기능 식별)

시스템 곳곳에 흩어진 기능들을 정리한다.
UI, DB, 코드, API, 문서, 업무 담당자 인터뷰 등 다양한 방식으로 비즈니스 기능 맵을 작성한다.


2단계. 전략적 중요도 분류

각 비즈니스 기능을 아래 기준에 따라 분류한다.

  • 핵심 경쟁 우위
    • 회사만의 차별화된 가치, 내부 개발 유지
  • 지원 기능
    • 내부 프로세스 최적화용, MSA 후보
  • 범용 기능
    • 타사 솔류션으로 대체 가능, SaaS 검토

3단계. 비즈니스 규칙 정리(중요한 도메인 규칙과 불필요한 기능 구분)

여전히 중요한 비즈니스 규칙과 현재는 더 이상 사용되지 않거나 중복된 로직을 분리한다. (= 도메인 정제의 시작점)


4단계. 우선순위에 따라 기능 추출 결정(추출 우선순위 결정)

전략적 중요도와 기술 부채 정도, 의존도, 장애 빈도 등을 고려해 어디서부터 분리할 것인지 결정한다.


5단계. 첫 번째 기능 추출 계획 수립(추출 대상에 대한 구체적 분리 계획 수립)

  • 데이터 분리
  • 메시징/동기 API 설계
  • 변경 경로 및 리디렉션 설계
  • 실패 시 롤백 전략 포함

6단계. 반복적 추출과 전달(하나씩 반복하면서 점진적 전환 진행)

하나의 컨텍스트를 추출하고, 안정화하고, 다음 기능으로 넘어가는 반복적이고 점진적인 방식이다.
매 단계마다 테스트 자동화와 품질 확보 체계를 동반해야 한다.


3.1. 사용자 경험의 연속성을 위한 API GW 와 UI 분할 전략

모놀리스에서 MSA 로 점진적으로 전환하더라도 시스템은 사용자에게 항상 활성화되어 있어야 하며, UI 는 일관성있는 동작과 경험을 제공해야 한다.
이를 위해 가장 먼저 고려해야 할 것은 사용자 요청의 진입점이며, API GW 는 이 진입점에 위치한 퍼사드 역할을 한다.


퍼사드 역할의 API GW: 사용자와 시스템 간 계약 유지

단계동작
초기모든 사용자 요청이 API GW → 모놀리스로 전달
부분 분리특정 기능이 MSA 로 추출되면 API GW 는 해당 요청만 새로운 서비스로 전달
A/B 테스트일부 사용자 그룹만 새로운 MSA 를 사용하도록 라우팅 변경
되돌리기문제가 있는 경우 모든 요청을 다시 모놀리스로 라우팅
완전 전환MSA 가 안정화되면 모든 요청을 MSA 로 전달

API GW 는 사용자와 시스템 간 인터페이스 계약의 안정성을 보장한다.


복합 요청 처리: 단일 요청 → 다수 서비스로 분할될 때

하나의 사용자 요청이 여러 서비스(레거시 + MSA) 로 분산 처리되어야 하는 경우:

  • 쿼리(GET) 요청은 결과를 합산하거나 병합하여 반환 가능
  • 하지만 작성/갱신(POST/PUT) 요청은 일관성 보장 및 오류 처리가 복잡함

이러한 복잡한 처리를 API GW 또는 BFF(Backend for Frontend) 계층에서 담당하며, GraphQL 기반의 서버 측 리소스 집계 전략을 사용할 수 있다.

<GraphQL 을 통한 서버 측 집계>

  • GraphQL 은 여러 데이터 소스(레거시 API, MSA API 등) 에서 데이터를 가져와 단일 응답으로 병합 가능
  • UI 에서 필요한 필드만 쿼리하여 불필요한 데이터 전송을 줄임
  • REST 방식보다 유연한 응답 설계 가능

이런 방식은 서버 측 조합으로 사용자 경험의 일관성을 유지하면서 아키텍처 내부의 복잡성을 감춘다.


3.1.1. GraphQL 이 단일 응답으로 병합하는 구조

1단계. 클라이언트가 원하는 ‘결과 형태’를 직접 요청함
GraphQL 에서는 클라이언트가 응답 형태를 정확하게 지정할 수 있다.

query {
  policyholder(id: "123") {
    name
    riskProfile {
      score
    }
    rate {
      base
      discount
    }
  }
}

이 요청은 3개의 다른 출처에서 데이터를 받아야 한다고 가정한다.

  • policyholder: 레거시 모놀리스 DB 또는 API
  • riskProfile: MSA A
  • rate: MSA B


2단계. GraphQL 서버가 각 필드마다 ‘데이터 소스’를 호출함

GraphQL 서버에는 각 필드를 담당하는 resolver 가 등록되어 있다.

const resolvers = {
  Query: {
    policyholder: (_, { id }) => getPolicyholderById(id), // 레거시 호출
  },
  Policyholder: {
    riskProfile: (parent) => getRiskFromMSA(parent.id), // MSA A
    rate: (parent) => getRateFromMSB(parent.id),        // MSA B
  }
};

GraphQL 서버는 요청이 들어오면 이 resolver 들을 동시에 호출하거나 순차적으로 호출하여 각 필드에 해당하는 데이터를 수집한다.

resolver 가 DB 를 직접 호출할 수도 있지만, 일반적이지는 않다. (GraphQL 이 직접 DB 에 접근하는 것이 아니라 resolver 가 접근하는 것임)

// GraphQL 리졸버가 DB를 직접 호출하는 경우
const resolvers = {
  Query: {
    policyholder: async (_, { id }) => {
      return await db.query('SELECT * FROM policyholders WHERE id = ?', [id]);
    }
  }
};


3단계. 수집된 데이터를 ‘하나의 JSON 응답’ 으로 반환

GraphQL 서버는 응답 구조를 그대로 유지하면서 데이터를 아래처럼 병합한다.

{
  "data": {
    "policyholder": {
      "name": "김주현",
      "riskProfile": {
        "score": 87
      },
      "rate": {
        "base": 120000,
        "discount": 15000
      }
    }
  }
}

이렇게 여러 API 호출 결과를 자동으로 구조화하거나 하나의 JSON 객체로 합쳐서 반환하는 것이 GraphQL 의 가장 큰 장점으로, 다양한 프론트에 맞게 응답 구조 조절이 가능하기 때문에 BFF 패턴과 잘 어울린다.

<REST API vs GraphQL>

 REST APIGraphQL
요청 수여러 API 호출 필요한 번의 요청으로 필요한 필드만
응답 크기불필요한 데이터 포함 가능필요한 데이터만
병합 처리클라이언트가 직접 해야 함서버에서 자동 병합
동기/비동기 흐름명시적 제어 필요resolver 기반 추상화

컴포지트 UI: 화면도 점진적으로 분할

컴포지트 UI 패턴은 화면을 여러 개의 분리된 UI 구성 요소로 나누고, 각 구성 요소가 다른 백엔드 소스에서 독립적으로 데이터를 가져오는 방식이다.

 UI 구성 요소
 ├── 보험 계약 심사 → 레거시 시스템 쿼리
 ├── 리스크 정보     → 새로운 MSA 쿼리
 └── 보험료율 계산    → 새로운 MSA 쿼리

이 UI 는 하나의 페이지로 보이지만, 백엔드에서는 서로 다른 시스템이 각각의 데이터 조각을 제공하고 API GW 또는 GraphQL 레이어가 통합된 결과를 제공한다.

사용자에게는 하나의 서비스처럼 보이지만, 내부적으로는 레거시와 MSA 가 혼합된 이행 구조가 점진적으로 작동하고 있다.
이러한 구조적 분리는 시스템의 유연성과 진화를 가능하게 하면서도 사용자 경험을 해치지 않은 핵심 전략이다.


3.2. 데이터 일관성을 지키는 전략

레거시 시스템에서 MSA 로 점진적으로 전환하는 과정에서 가장 민감한 주제 중 하나는 데이터의 일관성이다.
양쪽 시스템이 동일한 데이터를 다룰 때, 변경이 한쪽에만 반영되면 사용자에게 혼란을 주고 결국 시스템 전반의 신뢰도가 하락한다.

여기서는 레거시 ↔ MSA 간 데이터 동기화를 위한 3가지 전략을 소개한다.

  • 데이터베이스 트리거
  • 이벤트 서피싱(Event Surfacing)
  • CDC(Change Data Capture)

분산 시스템은 언제나 일시적 불일치, 궁극적 일관성을 전제로 한다.

레거시 시스템에서 상태가 변경되면 MSA 에 반영이 되어야 하고, 그 반대도 마찬가지이다.
완벽한 실시간 일치는 불가능하며, 최종적으로 동기화되는 구조를 만들어야 한다.

목표는 단일 데이터 소스를 향한 단계적 접근이다.
전환기에는 레거시와 MSA 가 동일 데이터를 복제 보유하게 되며, 이를 점진적으로 하나의 MSA 로 완전히 이전하기 전까지는 양방향 동기화 체계가 필요하다.


3.2.1. 데이터베이스 트리거 기반 동기화

<작동 방식>

  • DB 트리거를 설정하여 데이터 변경 시 이벤트 레코드 생성
  • 해당 이벤트는 이벤트 테이블에 기록되고 메시지로 발행됨


<장점>

  • 변경과 이벤트 생성이 동일 트랜잭션 내에서 수행되기 때문에 강한 일관성이 보장됨
  • 애플리케이션 로직 수정 없이도 동작 가능


<단점>

  • RDBMS 종속성 (MongoDB, Cassandra 등에서는 불가능)
  • 고부하 트랜잭션 환경에서는 성능 저하 우려
  • 복잡한 트리거 로직은 디버깅과 유지보수가 어려움

3.2.2. 이벤트 서피싱(Event Surfacing)

<작동 방식>

  • 엔티티 변경 시 애플리케이션이 명시적으로 이벤트를 생성
  • 이벤트 테이블에 저장 → 백그라운드 작업으로 외부 시스템에 게시
[사용자 요청]
 → 엔티티 저장 (DB)
 → 이벤트 객체 생성 → 이벤트 테이블 저장
 → 비동기 Worker 가 이벤트 테이블 읽어 게시


<장점>

  • DB 트리거에 의존하지 않고, 애플리케이션 수준에서 유연하게 구현 가능
  • 테스트 및 트래킹 용이


<단점>

  • 이벤트 생성 위치가 불분명할 수 있음(= 이벤트를 생성하고 삽입할 적절한 위치를 찾기 어려움)
  • 서비스 계층 간 트랜잭션 경계가 명확하지 않으면 중복 이벤트 또는 누락 위험(= 여러 서비스 계층 구성 요소가 단일 트랜잭션 범위 내에서 관리되는 동안 데이터를 독립적으로 수정하기도 함)
  • 상위 오케스트레이터 없이 구현할 경우 다수의 이벤트로 분할 발생(= 각 개별 서비스 계층 구성 요소가 고유한 이벤트를 생성함)

데이터베이스 트리거는 하나 이상의 트리거가 실행될 때 이벤트를 실행하는 반면, 이벤트 서피싱은 애플리케이션 코드에서 이벤트를 명시적으로 생성해 엔티티와 함께 DB 에 영속시킨다.


3.2.3. CDC(Change Data Capture)

1.3.2.1. CDC(Change Data Capture, 변경 데이터 캡처) 와 디비지움 프로젝트 를 참고하세요.


3.2.4. 데이터베이스 트리거 vs 이벤트 서피싱 vs CDC

 DB 트리거이벤트 서피싱CDC
구현 위치DB 내부애플리케이션 코드DB 트랜잭션 로그
트랜잭션 일관성강함강함설정에 따라 보장 가능
성능 저하 영향높을 수 있음낮음매우 낮음
유지보수 난이도높음중간중간~높음
사용 제약RDBMS 만 가능앱 로직 영향 있음RDBMS 로그 접근 필요

MSA 전환기에는 필연적으로 데이터가 여러 시스템에 분산된다.
이 때 핵심은 완벽한 동기화가 아니라, 사용자 경험에 영향이 없도록 최종 일관성을 확보하는 것이다.

위의 3가지 전략은 시스템의 제약에 따라 조합하여 사용할 수 있다.

  • 초기 PoC 단계: 트리거 or 이벤트 서피싱
  • 실서비스 동기화: CDC + Kafka 스트리밍
  • 레거시 탈피 중장기 플랜: CDC 기반 이벤트 중심 아키텍처 구축

3.2.5. 데이터 동기화 이벤트 적용

위의 3가지 데이터 동기화 방식(트리거, 이벤트 서피싱, CDC)은 모두 ‘서로 다른 시스템 간 데이터 일관성 유지’라는 같은 목표를 지향한다.

여기서는 CDC 를 중심으로 레거시 → MSA, MSA → 레거시 간의 데이터 변경 전파 흐름을 구체적으로 설명한다.
이 때 사용되는 핵심 인프라는 메시지 버스(Kafka 등)이다.

CDC 를 사용해 로컬 데이터 수정 사항을 다른 시스템에 전달

[1~6] 레거시 → MSA
1. 사용자 요청 발생: 사용자가 데이터를 수정하거나 추가하는 요청을 보냄
2. API GW: 요청이 레거시 시스템으로 전달됨
3. 레거시 DB 에 데이터 저장: 데이터베이스 트랜잭션 로그에 변경사항이 기록됨
4. CDC: 로그를 테일링하여 변경 이벤트를 감지 → 메시지 생성
5. 메시지 버스 → 신규 MSA: 이벤트가 MSA 로 전달되어 동일한 변경을 수행함 (데이터 동기화)

[6~10] MSA → 레거시
6. 사용자 요청 발생: 사용자가 신규 MSA 를 통해 데이터를 수정
7. API GW: 요청이 신규 MSA 로 전달됨
8. 신규 MSA 에서 이벤트 발생: 데이터가 변경되며, 메시지 버스에 변경 이벤트 발생
9. 메시지 버스 → 레거시 시스템 수신기: 이벤트가 전달되며, 레거시 시스템에서도 같은 변경 수행
10. 레거시 DB 에 동기화된 데이터 저장

이 때 무한 동기화 루프 방지를 위해 상관관계 ID 가 필요하다.
이 구조에서 중요한 문제는 양쪽 시스템이 서로의 이벤트를 다시 방출하게 되면 무한 루프가 발생할 수 있다는 점이다.

레거시 → CDC 이벤트 → MSA → 이벤트 → 레거시 → CDC 이벤트 → MSA → ...

무한 루프에 대해 좀 더 자세히 설명하면 아래와 같다.
1~5: 사용자가 레거시 시스템을 통해 데이터 수정 → CDC → MSA 로 이벤트 전달
6: MSA 도 그 이벤트를 받아서 자기 DB 에 반영
7: 이 때 MSA 도 ‘내 데이터가 변경되었네?’ → 자신의 변경을 이벤트로 다시 발행
8: 이 이벤트가 메시지 버스를 통해 다시 레거시 시스템으로 전파됨
9: 레거시 DB 이 이벤트를 다시 받아서 수정 → 다시 CDC 발생…

이렇게 한 번의 요청이 양쪽 시스템에서 서로를 끝없이 자극하게 되어버리는 구조가 되어 버린다.

이러한 루프를 방지하기 위해 상관관계 ID(Correlation ID or Origin Tag) 를 이벤트에 포함시켜야 한다.

  • 이벤트마다 sourceId, correlationId, origin 등의 발신자 정보 추가
  • 수신자는 해당 필드를 확인하고, ‘내가 생성한 이벤트’ 인지 판단
  • 동일한 출처에서 온 이벤트는 무시함으로써 무한 루프 방지

3.3. 스트랭글러(Strangler) 패턴

스트랭글러 패턴은 기존 레거시 시스템을 점진적으로 현대화된 시스템으로 대체하는데 사용되는 대표적인 마이그레이션 전략이다.
‘숙주 나무를 감싸고 점차 숙주를 죽이는 덩굴 식물’에서 유래한 이 패턴은 기존 기능을 점진적으로 새 시스템으로 이동시키며, 결국에는 레거시를 제거하는 방식이다.

스트랭글러 패턴의 정의는 새로운 바운디드 컨텍스트(=스트랭글러)를 만들어, 레거시 컨텍스트의 기능을 점진적으로 마이그레이션 한 후 레거시 시스템을 폐기하는 전략이다.

  • 새로운 요구사항은 레거시가 아닌 새로운 컨텍스트(=스트랭글러)에 구현
  • 레거시 시스템에 대한 개발은 중단(핫픽스, 긴급 사항은 예외)
  • 모든 기능이 마이그레이션 되면 레거시(=숙주) 폐기

적용 전략은 작고 안전하게 시작하는 것이다.

  • 쉬운 승리부터 분리
    • 독립성이 높은 기능부터 스트랭글러에 구현
  • API GW 또는 BFF 를 퍼사드로 사용
    • 요청을 기존/신규 시스템으로 라우팅

레거시에서 현대화된 시스템으로 기능을 마이그레이션하는 상태에 따라 요청을 전달하는 퍼사드 레이어

  • 초기: 대부분 레거시로 전달
  • 일부 기능 분리 후: 해당 요청만 신규 시스템으로 전달
  • 최종: 모든 요청이 신규 시스템으로 전달


<빅볼 오브 머드의 위험한 복제 방지>
스트랭글러 패턴은 단순한 기능 복사가 아니라, 모델 재설계의 기회이어야 한다.
잘못 설계된 시스템을 그대로 복제하는 실수를 하지 말자.

  • 잘못된 행동을 MSA 에 그대로 가져오지 말자.
    • 빅볼 오브 머드는 암묵적 규칙과 잘못된 구현에 대해 사용자가 우회하여 사용하는 기능이 쌓여 있음
    • 기존의 불명확한 설계나 기술 부채까지 함께 복제하지 않도록 주의해야 함
  • 모놀리스에 새로운 기능을 추가하지 말자.
    • 진행 중인 마이그레이션 기간에도 비즈니스는 계속 요청을 함
    • 하지만 새 기능은 반드시 스트랭글러 컨텍스트에 구현해야 함
    • 레거시에 새 기능을 넣으면 나중에 MSA 에 이중 구현해야 하는 낭비 발생


처음에는 혼란스럽고 어려울 수 있다.
보잘 것 없는 출발점에서 더 나빠질 것처럼 느껴질 수 있다.
그러나 시작하는 것이 무엇보다 중요하다.

MSA 전환은 완벽하게 시작하는 것이 아니라, 작게 시작해서 점진적으로 성장시키는 것이다.


참고 사이트 & 함께 보면 좋은 사이트

본 포스트는 반 버논, 토마스 야스쿨라 저자의 전략적 모놀리스와 마이크로서비스를 기반으로 스터디하며 정리한 내용들입니다.






© 2020.08. by assu10

Powered by assu10