모놀리식에서 MSA로 전환하는 방법

반응형

모놀리식으로 개발된 제품을 마이크로서비스 아키텍처(MSA)로 전환한다고 하면,흔히 도메인 단위로 코드를 분리하고, 서비스를 나누는 작업부터 떠올리기 쉽습니다. 실제로 복사·붙여넣기 방식으로도 분리는 가능할 수 있습니다. 하지만 문제는 가능하냐가 아니라 안전하냐 입니다. 이미 운영 중인 서비스를 전제로 한다면, MSA 전환은 단순한 구조 개선이 아니라 서비스를 멈추지 않고 구조를 바꾸는 과정이 됩니다. 이 글에서는 모놀리스에서 MSA로 전환할 때 도메인 설계 이전에 반드시 고민해야 할 전환 전략과, 서비스 중단 없이 점진적으로 구조를 바꿀 수 있는 대표적인 패턴들을 살펴보려 합니다.

어째서 모놀리식에서 MSA로 가야 하는가?

이야기에 앞서 어째서 모놀리식에서 MSA로 가야하는지 이해를 해야할거 같습니다. 모놀로식의 장점은 단순성, 빠른 개발, 쉬운 배포를 제공합니다. 하지만 애플리케이션이 커지면서 단순함은 양날의 검이 되어 다음과 같은 문제를 발생 시킵니다.

확장성 병목 현상

한 기능만 부하가 증가했음에도 불구하고, 애플리케이션 전체를 함께 확장해야 하는 문제를 의미합니다.

이러한 문제가 발생하는 이유는 모놀리식 애플리케이션이 단일 프로세스이자 단일 배포 단위이기 때문입니다.
즉, 스케일의 최소 단위가 개별 기능이 아니라 애플리케이션 전체가 됩니다.

이 구조에서는 특정 기능에서 병목이 발생하더라도 해당 기능만 독립적으로 확장하거나 배포할 수 없고,
결국 작은 변경이나 개선에도 전체 애플리케이션을 다시 배포해야 하는 상황이 발생합니다.

유지 관리 문제

코드베이스가 커질수록, 하나의 변경이 어떤 영역에 영향을 미칠지 예측하기 어려워지는 상황을 의미합니다.

이러한 문제는 시간이 지날수록 공통 유틸, 공통 도메인 모델, 공통 설정들이 점점 늘어나면서 발생합니다.
여러 기능이 동일한 코드에 의존하게 되고, 그 결과 내부 호출 구조가 마치 하나의 거대한 메서드 호출 체인처럼 얽히게 됩니다.

이 상태에서는 특정 기능만 수정하더라도 의도하지 않은 영역에서 부작용이 발생할 가능성이 높아지고,
변경의 영향 반경이 점점 커지게 됩니다.

배포 복잡성

모놀리식 애플리케이션의 배포 과정 자체가 기술적으로 어렵다는 의미는 아닙니다.
오히려 모놀리스의 배포는 초기에는 단순하고 직관적인 편입니다.

문제가 되는 지점은, 작은 변경 하나에도 애플리케이션 전체를 다시 배포해야 한다는 구조적 특성에 있습니다.
기능 단위로 배포를 분리할 수 없기 때문에, 영향 범위가 매우 작은 수정이라 하더라도 전체 서비스가 배포 대상이 됩니다.

이로 인해 배포 중 장애가 발생할 경우, 장애의 영향 범위가 커지고 MTTR(Mean Time To Recovery) 또한 증가하게 됩니다.
결과적으로 배포는 점점 위험한 작업이 되고, 배포 빈도는 줄어들며 릴리즈 단위는 점점 커지는 악순환으로 이어질 수 있습니다.

제한된 기술 선택

모놀리식 애플리케이션이 하나의 언어와 하나의 프레임워크, 그리고 동일한 실행 환경을 공유하는 구조에서 발생하는 문제입니다.

모놀리스 환경에서는 특정 문제에 더 적합한 기술이 존재하더라도, 이미 선택된 기술 스택에 맞춰 구현해야 하는 상황이 자주 발생합니다.
예를 들어 실시간 처리에는 Node.js나 Go가 더 적합할 수 있고, 데이터 파이프라인이나 분석 작업에는 Python이 유리한 경우도 있습니다.

하지만 모놀리식 시스템에서는 이러한 기술들을 기능 단위로 선택하기 어렵고,
결국  기술 선택의 기준이 최적화가 아닌 일관성이 되기 쉽습니다.

마이크로서비스 아키텍처의 목적은 무분별한 기술 도입이 아니라,
각 문제의 특성에 맞는 도구를 선택할 수 있는 구조를 만드는 것에 있습니다.

복원력 문제

특정 기능에서 발생한 장애가 전체 시스템 장애로 확산되는 상황을 의미합니다.

이러한 문제는 모놀리식 애플리케이션이 하나의 프로세스, 하나의 메모리 공간, 하나의 스레드 풀에서
모든 기능을 함께 처리하는 구조에서 발생합니다. 즉, 기능 간에 명확한 장애 격리 지점이 존재하지 않습니다.

예를 들어 주문이나 결제 기능에서 응답 지연이 발생할 경우, 동일한 리소스를 공유하는 다른 기능들까지 영향을 받게 되고,
결과적으로 서비스 전체 장애로 이어질 수 있습니다.

이는 곧 장애가 기능 단위로 격리되지 못하고 전파되는 구조라는 의미이며,
이러한 문제를 해결하기 위해 마이크로서비스 아키텍처에서는 Bulkhead, Circuit Breaker와 같은
복원성 패턴들이 등장하게 되었습니다.


마이크로서비스 아키텍처(MSA)는 애플리케이션을 더 작은 독립적인 서비스로 세분화함으로써, 앞서 살펴본 문제들을 해결하고자 합니다. 각 서비스는 특정 기능을 담당하며, 독립적으로 개발·배포·확장될 수 있는 구조를 가집니다.
하지만 모놀리식 애플리케이션을 MSA 구조로 전환하는 과정은 결코 단순하지 않습니다. 이미 운영 중인 서비스를 전제로 할 경우, 서비스를 중단하지 않은 채 구조를 바꿔야 하기 때문입니다.

즉, MSA 전환은 새로운 아키텍처를 설계하는 문제가 아니라, 기존 시스템을 어떻게 안전하게 변화시킬 것인가에 대한 문제라고 볼 수 있습니다.

스트랭거 무화가 패턴

이 패턴은 스트랭거 무화과라가는 나무를 보고 마틴 파울러가 도입한 설계 패턴입니다. 스트랭거 무화과는 나무의 틈새에서 싹을 틔우는 덩쿨 식물닙니다. 자라면서 숙주 나무에서 영양분을 흡수하다가 땅에 닿아 뿌리를 내리면 햇빛을 받기 위해 가지를 뻗는 다고 합니다. 그렇게 되면 스스로 생존할 수 있게 되고, 원래 숙주 나무는 죽게 되며, 무화과 나무는 그 나무의 모양 그대로 간직한채 남게 됩니다. 

이것을 소프트웨어에 접목을 시킨다면 기존의 서비스에 덮어씌우면서 점진적으로 발전을 시키는 패턴입니다.
이 패턴을 구현하여 모놀로식 애플리케이션에서 MSA로 전환하는 프로세스는 변환, 공존, 제거라는 세 단계로 구성됩니다.

  • 변환(Extract)
    - 기존 모놀리식 애플리케이션에서 전환하고자 하는 특정 기능을 식별하고 분리합니다.
    - 이 단계에서는 새로운 마이크로서비스가 구현되지만, 아직 전체 트래픽을 처리하지는 않습니다.
  • 공존(Coexist)
    - 새로 분리된 마이크로서비스가 완전히 안정화되기 전까지는 기존 모놀리식 시스템을 그대로 유지합니다.
    이 단계에서는 모놀리식 시스템과 마이크로서비스가 동시에 공존하며, 일부 트래픽만 마이크로서비스로 전달하거나
    정합성 검증을 위해 병렬 실행될 수 있습니다.

    - 이러한 공존을 가능하게 하기 위해 요청을 제어하고 분기할 수 있는 단일 진입점, 즉 API Gateway가 필요합니다. 게이트웨이는 요청을 조건에 따라 모놀리식 시스템 또는 마이크로서비스로 라우팅하고, 장애가 발생한 경우에는 즉시 기존 모놀리식 시스템으로 우회할 수 있도록 도와줍니다.

    - 즉, 공존 단계에서 게이트웨이는 두 시스템을 단순히 함께 두는 것이 아니라, 어떤 요청을 누가 처리할지 통제하는 핵심 제어 지점의 역할을 수행합니다.
  • 제거(Retire)
    - 마이크로서비스가 충분히 안정화되고 모든 트래픽을 정상적으로 처리할 수 있는 상태가 되면, 모놀리식 애플리케이션에서 해당 기능을 제거하고 마이크로서비스를 공식적인 처리 주체로 전환합니다.

 

병렬 실행 패턴

스트랭거 무화가 패턴이 공존 이후 교체를 선택하는 전환 전략이라면, 공존 이후의 판단을 위해 검증을 목표로 하는 패턴입니다.

이 패턴에서는 모놀리식 애플리케이션으로 구현된 기능과 새롭게 구현된 마이크로서비스가 동시에 공존합니다.
다만 두 구현이 동일한 책임을 가지는 것은 아닙니다.

병렬 실행 패턴에서의 Source of Truth는 모놀리식 서비스에 있으며, MSA는 사용자에게 직접 응답하지 않고
그림자(shadow)처럼 동일한 요청을 처리해 결과를 생성합니다. 이 결과는 비교·검증을 위한 용도로만 사용되며,
서비스의 실제 동작과 책임은 여전히 모놀리식 시스템에 남아 있습니다.

이 패턴에 대해 이러한 의문이 들수 있습니다. 추가 개발은 어떻게 하는가? 

병렬 실행 패턴의 핵심은 새로운 기능을 빠르게 확장하는 것이 아니라,
기존 기능이 동일하게 동작하는지를 검증하는 데 목적이 있다는 점입니다. 따라서 이 기간 동안의 추가 개발에는 명확한 원칙이 필요합니다.

병렬 실행 단계에서는 기능 변경과 신규 개발의 기준점을 반드시 하나로 유지해야 합니다.
이 기준점은 모놀리식 시스템이며, 모든 비즈니스 변경은 모놀리식 코드에 먼저 반영됩니다.
마이크로서비스는 해당 변경을 따라가며, 기존 구현과 동일한 결과를 생성하는지만을 검증합니다.

즉, 병렬 실행 중에는 모놀리식과 마이크로서비스 양쪽에 서로 다른 기능을 추가하거나 의미가 다른 로직을 넣는 것은 허용되지 않습니다.
이 경우 두 구현의 결과를 비교할 수 없게 되고, 패턴의 목적 자체가 무너지게 됩니다.

이러한 이유로 병렬 실행 패턴은 장기적으로 유지되는 구조가 아니라, 검증이 완료되면 반드시 종료되어야 하는 임시 단계로 사용됩니다.

그러면 MSA는 어떻게 그림자처럼 모놀로식을 검증할 수 있을까?

그림자처럼 동작한다는 말은 3가지를 의도적으로 만족시킨다는 뜻입니다.

  1. 사용자는 마이크로서비스의 존재를 인식하지 않습니다.
    모든 요청에 대한 최종 응답은 항상 모놀리식 애플리케이션이 담당하며,
    마이크로서비스의 결과는 사용자에게 노출되지 않습니다.
  2. 입력은 반드시 동일해야 합니다.
    동일한 요청과 파라미터가 전달되지 않으면 두 시스템의 결과를 비교할 수 없기 때문에,
    병렬 실행 패턴에서 입력의 일관성은 필수 조건입니다.
  3. 마이크로서비스의 결과는 상태를 변경하지 않습니다.
    마이크로서비스는 결과를 생성하지만 이를 저장하지 않으며, 데이터 변경이나 외부 부수 효과를 발생시키지 않습니다.
    이 단계에서의 마이크로서비스는 오직 검증을 위한 실행 주체로만 동작합니다.

동작 방식

요청이 들어오면 게이트웨이 또는 중간 계층에서 요청을 복제합니다. 이때 모놀리식 애플리케이션과 마이크로서비스가 동시에 실행됩니다.

요청에 대한 최종 응답의 책임은 모놀리식 시스템에 있으며, 마이크로서비스의 응답은 사용자에게 전달되지 않습니다.
대신 마이크로서비스의 실행 결과는 로그로 남기거나, 메시지 큐로 전달하거나, 별도의 비교 시스템으로 전달되어 검증에 활용됩니다.

비교의 대상은 단순한 응답 바이트가 아니라, 다음과 같은 비즈니스적으로 의미 있는 결과입니다.

  • 상태 코드
  • 핵심 필드 값
  • 계산 결과
  • 비즈니스 규칙에 따른 상태 변화

사이드 이펙트가 발생한다면 어떻게 해야 할까?

병렬 실행 패턴에서 사이드 이펙트는 허용되지 않습니다. 마이크로서비스가 데이터베이스에 쓰기를 수행하거나,
외부 시스템에 요청을 보내거나, 이벤트를 발행하는 순간, 검증을 위한 실행이 아닌 실제 동작이 되어버리기 때문입니다.

이를 방지하기 위해 병렬 실행 단계의 마이크로서비스는 다음과 같은 제약을 의도적으로 가집니다.

  • 데이터베이스 쓰기 차단 또는 롤백
  • 외부 API 호출 비활성화 또는 Mock 처리
  • 이벤트 발행 비활성화
  • 동일 요청에 대해 중복 실행되더라도 문제가 없도록 멱등 처리

즉, 마이크로서비스는 계산은 수행하지만, 시스템 외부에 어떠한 흔적도 남기지 않도록 설계됩니다.

협력자 데코레이션 패턴

새로운 MSA가 기존 모놀리식 구성 요소의 기능을 즉시 대체하지 않고,
기존 요청 흐름에 협력자(collaborator) 로 참여해 기능을 보강하는 전환 전략입니다.

이 패턴에서 마이크로서비스는 모놀리식의 핵심 책임을 가져오지 않으며,
특정 지점에서 동작을 장식(decorate) 하듯 추가하거나 보완하는 역할을 수행합니다.
즉, 요청 흐름의 주도권은 여전히 모놀리식에 있고, 마이크로서비스는 보조적인 책임만을 제한적으로 담당합니다.

이러한 특성 때문에 협력자 데코레이션 패턴은 동일한 애플리케이션 흐름 안에서 모놀리식과 MSA가 공생하며,
책임이 점진적으로 이동하는 전환 과정으로 해석할 수 있습니다.

격벽(벌크헤드) 패턴

벌크헤드 패턴은 다른 전환 패턴들과 달리, 시스템을 전환하기 위한 목적을 직접적으로 가지지는 않습니다.
이 패턴의 핵심 목적은 장애 전파를 차단하여 시스템 전체의 안정성을 확보하는 것입니다.

이 패턴은 선박 설계에서 사용되는 방수 구획(bulkhead) 개념에서 유래했습니다. 선박 내부를 여러 구획으로 나누어 한 구획이 침수되더라도 다른 구획은 영향을 받지 않도록 함으로써, 선박 전체가 침몰하는 상황을 방지합니다.

소프트웨어에서의 벌크헤드 패턴

시스템 구성 요소를 격리하여 한 부분의 장애가 다른 부분으로 전이되지 않도록 하는 설계 방식을 의미합니다.

이는 특히 마이크로서비스와 같은 분산 시스템 환경에서 매우 중요합니다. 분산 시스템에서는 네트워크 지연, 타임아웃, 외부 의존성 장애 등 부분 실패(partial failure)가 빈번하게 발생하기 때문에, 장애가 전파되지 않도록 구조적으로 차단하는 것이 필수적입니다.

벌크헤드 패턴을 적용하면

  • 특정 기능의 장애가 전체 시스템 장애로 확산되는 것을 막을 수 있고
  • 복원력(resilience)과 신뢰성(reliability)을 크게 향상시킬 수 있습니다.

MSA 전환과의 관계

벌크헤드 패턴은 모놀리식에서 마이크로서비스로 전환하기 위한 전략 그 자체는 아닙니다.
다만, 전환 과정과 전환 이후를 안전하게 지탱해주는 기반 패턴의 역할을 합니다.

모놀리식 시스템에서는 보통

  • 스레드 풀
  • 커넥션 풀
  • 메모리
  • 외부 연동 자원
    등이 공용으로 사용되며, 이로 인해 하나의 장애가 전체로 확산되기 쉽습니다.

벌크헤드 패턴은 이러한 구조에서 기능적 경계를 식별하고, 장애의 영향을 국소화할 수 있는 단위로 분리하도록 유도합니다.
이 과정에서 자연스럽게 마이크로서비스 후보가 드러나기도 합니다.

결론

이번 글에서는 모놀리식 애플리케이션에서 MSA로 전환하는 여러 접근 방식에 대해 학습했습니다.
이전 회사에서는 애플리케이션을 새로 만들어 전환을 진행했었고, 당시에는 그것이 가장 올바른 방법이라고 생각했습니다.

하지만 시스템의 규모가 커질수록 기존 서비스를 유지한 채 새로운 애플리케이션으로 전환하는 방식은
현실적으로 매우 어렵고 비용이 큰 선택이라는 점을 알게 되었습니다. 서비스는 멈출 수 없고, 리스크는 통제되어야 하기 때문입니다.

모놀리식에서 MSA로 전환하는 방법은 단일한 정답이 있는 문제가 아니라,
상황에 따라 선택할 수 있는 여러 전략의 집합이라는 것을 이해하게 되었습니다.

이번에 정리한 전환 관련 패턴은 다음 네 가지입니다.

  • 스트랭거 무화과 패턴
    기존 모놀리식 기능을 점진적으로 MSA 기능으로 교체해 나가는 전환 전략
  • 병렬 실행 패턴
    MSA를 별도의 애플리케이션으로 띄우고,
    기존 모놀리식과 동일한 요청을 처리하며 검증을 목적으로 공존하는 패턴
  • 협력자 데코레이션 패턴
    동일한 애플리케이션 흐름 안에서
    모놀리식과 MSA가 공생하며 책임을 점진적으로 이전하는 패턴
  • 벌크헤드 패턴
    전환 자체를 목적으로 하기보다는,
    MSA 환경에서 발생할 수 있는 장애 전파를 차단하고 안정성을 확보하기 위한 패턴

정리해보면, MSA 전환은 단순히 구조를 바꾸는 문제가 아니라
리스크를 어떻게 관리하고, 언제 어떤 책임을 이동시킬 것인가에 대한 선택의 문제에 가깝습니다.

이번 학습을 통해 "처음부터 새로 만드는 것"이 항상 정답은 아니며, 기존 시스템과 공생하면서 점진적으로 변화시키는 전략들이
현실적인 대안이 될 수 있다는 점을 명확히 이해하게 되었습니다.

출처
https://blog.bytebytego.com/p/from-monolith-to-microservices-key?utm_source=publication-search   
https://docs.aws.amazon.com/ko_kr/prescriptive-guidance/latest/modernization-decomposing-monoliths/strangler-fig.html  
https://martinfowler.com/bliki/StranglerFigApplication.html  

반응형

댓글

Designed by JB FACTORY