
Strangler Fig 패턴은 레거시 모놀리스 시스템을 마이크로서비스 아키텍처로 전환할 때 가장 널리 검증된 접근법 중 하나입니다. 마틴 파울러(Martin Fowler)가 2004년 처음 소개한 이 패턴은, 열대우림의 교살목(strangler fig)이 숙주 나무를 서서히 감싸며 결국 대체하는 자연 현상에서 이름을 따왔습니다. 기존 시스템을 한 번에 교체하는 이른바 "빅뱅(Big Bang)" 방식과 달리, Strangler Fig 패턴은 새 기능을 점진적으로 별도 서비스로 분리하면서 레거시 코드를 자연스럽게 소멸시킵니다. 이 글에서는 패턴의 동작 원리부터 실제 프로젝트에 적용하는 방법, 그리고 운영 환경에서 주의해야 할 트레이드오프까지 구체적으로 살펴보겠습니다.
많은 팀이 레거시 모놀리스를 마주할 때 두 가지 극단 사이에서 갈등합니다. 하나는 아무것도 건드리지 않고 기존 코드 위에 기능을 계속 쌓는 것이고, 다른 하나는 모든 것을 멈추고 새 아키텍처로 전면 재작성하는 것입니다. 전자는 기술 부채를 누적시키고, 후자는 수개월에서 수년에 걸친 개발 중단과 막대한 리스크를 초래합니다. Strangler Fig 패턴은 이 두 극단 사이의 현실적인 중간 경로를 제시합니다.
Strangler Fig 패턴의 동작 원리
Strangler Fig 패턴의 핵심 아이디어는 프록시 레이어(Facade)를 중간에 두고, 클라이언트의 요청을 레거시 시스템 또는 새로운 마이크로서비스로 라우팅하는 것입니다. 이 프록시가 바로 "교살목"의 역할을 합니다. 초기에는 모든 트래픽이 레거시 모놀리스로 흘러가지만, 새로운 서비스가 준비될수록 라우팅 규칙이 변경되어 점점 더 많은 요청이 새 서비스로 전달됩니다. 레거시 시스템은 담당하는 기능이 줄어들수록 점차 "말라죽고", 최종적으로는 완전히 대체됩니다.
이 패턴이 동작하는 방식을 구체적으로 분해하면 세 단계로 나눌 수 있습니다.
첫째, 변환(Transform) 단계입니다. 레거시 시스템과 병렬로 새로운 서비스를 구축합니다. 이 단계에서는 레거시 코드를 건드리지 않으며, 새 서비스는 독립적으로 개발·테스트됩니다. 도메인 경계를 명확히 식별하고, 도메인 주도 설계(DDD, Domain-Driven Design)의 바운디드 컨텍스트(Bounded Context) 개념을 활용하여 어떤 기능을 먼저 추출할지 결정합니다.
둘째, 공존(Coexist) 단계입니다. 새 서비스가 준비되면 API 게이트웨이나 리버스 프록시를 통해 특정 엔드포인트 또는 특정 비율의 트래픽을 새 서비스로 전환합니다. 이 시점부터 레거시와 새 서비스가 동시에 운영됩니다. 두 시스템 간 데이터 일관성 유지가 중요한 과제로 부상하며, 이벤트 기반 동기화나 이중 쓰기(dual-write) 전략이 흔히 사용됩니다.
셋째, 제거(Eliminate) 단계입니다. 새 서비스가 안정적으로 운영됨을 확인한 후, 레거시 시스템에서 해당 기능 코드를 제거합니다. 라우팅 규칙도 단순해지고, 레거시 시스템의 범위가 점점 좁아집니다. 이 세 단계를 도메인별로 반복하면 결국 레거시 모놀리스 전체가 마이크로서비스 군(群)으로 대체됩니다.
이 과정에서 중요한 점은 비즈니스 연속성을 절대 희생하지 않는다는 원칙입니다. 각 단계는 독립적으로 배포하고 롤백할 수 있어야 하며, 문제가 생기면 즉시 레거시 시스템으로 트래픽을 되돌릴 수 있어야 합니다.
API 게이트웨이 기반 라우팅 구현
Strangler Fig 패턴을 구현하는 가장 일반적인 방법은 API 게이트웨이를 프록시 레이어로 사용하는 것입니다. Spring Cloud Gateway, Kong, AWS API Gateway 등 다양한 옵션이 있으며, 여기서는 Spring Cloud Gateway를 활용한 예제를 살펴봅니다.
기존 모놀리스는 http://legacy-monolith:8080에서 모든 API를 처리하고 있다고 가정합니다. 새롭게 분리된 주문(Order) 마이크로서비스는 http://order-service:9090에서 운영됩니다. 아래 설정은 /api/orders/** 경로로 들어오는 요청은 새 주문 서비스로, 나머지 모든 요청은 여전히 레거시 시스템으로 라우팅하는 방법을 보여줍니다.
# application.yml — Spring Cloud Gateway 라우팅 설정
spring:
cloud:
gateway:
routes:
# 주문 도메인: 새 마이크로서비스로 라우팅
- id: order-service
uri: http://order-service:9090
predicates:
- Path=/api/orders/**
filters:
- StripPrefix=0
# 헤더에 서비스 출처 표시 (모니터링용)
- AddResponseHeader=X-Served-By, order-service
# 상품 도메인: 새 마이크로서비스로 라우팅 (2차 전환 완료)
- id: product-service
uri: http://product-service:9091
predicates:
- Path=/api/products/**
filters:
- AddResponseHeader=X-Served-By, product-service
# 나머지 모든 요청: 레거시 모놀리스로 라우팅
- id: legacy-monolith
uri: http://legacy-monolith:8080
predicates:
- Path=/**
filters:
- AddResponseHeader=X-Served-By, legacy-monolith# 요청 흐름 확인 (curl 테스트)
$ curl -I http://gateway/api/orders/123
HTTP/1.1 200 OK
X-Served-By: order-service ← 새 마이크로서비스가 처리
$ curl -I http://gateway/api/users/456
HTTP/1.1 200 OK
X-Served-By: legacy-monolith ← 아직 레거시가 처리이 설정의 핵심 포인트는 라우팅 순서입니다. Spring Cloud Gateway는 routes 목록을 위에서 아래로 순서대로 평가하므로, 더 구체적인 경로 규칙을 먼저 배치하고 와일드카드(/**) 규칙을 마지막에 두어야 합니다. X-Served-By 헤더를 추가하면 Datadog이나 Grafana 같은 모니터링 도구에서 어느 서비스가 요청을 처리했는지 쉽게 추적할 수 있어, 전환 진행 상황을 정량적으로 파악하는 데 도움이 됩니다.
데이터 분리 전략 — 이중 쓰기와 이벤트 동기화
Strangler Fig 패턴에서 가장 까다로운 문제는 데이터베이스 분리입니다. 레거시 모놀리스는 보통 단일 거대 데이터베이스를 공유하며, 여러 도메인의 테이블이 복잡한 외래 키 관계로 얽혀 있습니다. 새 마이크로서비스가 자체 데이터베이스를 갖도록 분리하는 동안에도 데이터 일관성을 보장해야 합니다.
가장 많이 사용되는 두 가지 접근법은 이중 쓰기(Dual Write)와 CDC(Change Data Capture) 기반 이벤트 동기화입니다.
이중 쓰기는 애플리케이션 레이어에서 레거시 DB와 새 서비스 DB 양쪽에 동시에 데이터를 기록하는 방법입니다. 구현이 단순하지만, 두 쓰기 중 하나가 실패했을 때 불일치가 발생할 수 있습니다. 이를 보완하기 위해 레거시 DB 쓰기를 "진실의 원천(source of truth)"으로 두고, 실패 시 재시도 큐에 넣는 방식을 함께 사용합니다.
CDC 기반 동기화는 Debezium 같은 도구를 활용하여 레거시 DB의 변경 로그(binlog, WAL)를 Kafka 토픽으로 발행하고, 새 서비스가 이를 구독하여 자신의 DB에 반영하는 방식입니다. 애플리케이션 코드 변경이 최소화되고, 레거시 시스템을 건드리지 않아도 된다는 장점이 있습니다. 다만 약간의 지연(eventual consistency)이 발생하므로, 실시간 강일관성(strong consistency)이 필요한 도메인에서는 추가적인 설계가 필요합니다.
아래 예제는 주문 서비스가 Kafka 이벤트를 발행하여 레거시 시스템과 새 서비스 데이터베이스가 동기화되도록 하는 Java 코드입니다.
// OrderService.java — 이중 쓰기 + 이벤트 발행 패턴
@Service
@RequiredArgsConstructor
public class OrderService {
private final LegacyOrderRepository legacyOrderRepository; // 레거시 DB
private final NewOrderRepository newOrderRepository; // 새 마이크로서비스 DB
private final KafkaTemplate<String, OrderEvent> kafkaTemplate;
@Transactional
public Order createOrder(CreateOrderRequest request) {
// 1단계: 레거시 DB에 우선 기록 (진실의 원천)
LegacyOrder legacyOrder = legacyOrderRepository.save(
LegacyOrder.from(request)
);
try {
// 2단계: 새 서비스 DB에 동기 기록 시도
newOrderRepository.save(NewOrder.from(legacyOrder));
} catch (Exception e) {
// 새 DB 쓰기 실패 시: 이벤트 큐에 적재하여 재시도 보장
log.warn("New DB write failed, publishing to retry topic: {}", e.getMessage());
kafkaTemplate.send("order.sync.retry",
new OrderEvent(legacyOrder.getId(), EventType.SYNC_RETRY));
}
// 3단계: 다른 서비스에 알릴 도메인 이벤트 발행
kafkaTemplate.send("order.created",
new OrderEvent(legacyOrder.getId(), EventType.CREATED));
return Order.from(legacyOrder);
}
}# Kafka 토픽 메시지 확인
$ kafka-console-consumer --topic order.created --from-beginning
{"orderId": "ORD-001", "eventType": "CREATED", "timestamp": "2026-03-23T10:00:00Z"}
{"orderId": "ORD-002", "eventType": "CREATED", "timestamp": "2026-03-23T10:00:05Z"}
$ kafka-console-consumer --topic order.sync.retry --from-beginning
{"orderId": "ORD-003", "eventType": "SYNC_RETRY", "timestamp": "2026-03-23T10:01:00Z"}이 코드에서 주목할 부분은 레거시 DB 트랜잭션이 먼저 커밋된 후에 새 DB 쓰기를 시도한다는 점입니다. 새 DB 쓰기가 실패해도 레거시 데이터는 안전하게 보존되며, 재시도 큐를 통해 결과적 일관성을 달성합니다. 이처럼 레거시 시스템을 진실의 원천으로 유지하면 전환 중 발생하는 데이터 불일치 리스크를 크게 낮출 수 있습니다.
빅뱅 재작성과의 비교 — 언제 어떤 방식을 선택할까
Strangler Fig 패턴만이 레거시 마이그레이션의 유일한 답은 아닙니다. 상황에 따라 다른 전략이 더 적합할 수 있으므로, 주요 접근법들의 트레이드오프를 이해하는 것이 중요합니다.
빅뱅 재작성(Big Bang Rewrite)은 레거시 시스템을 완전히 동결하고 새 시스템을 처음부터 구축한 뒤 특정 시점에 한 번에 전환하는 방식입니다. 이 방식은 새 아키텍처를 깔끔하게 설계할 수 있다는 매력이 있습니다. 그러나 조엘 스폴스키(Joel Spolsky)가 일찌감치 지적했듯, 빅뱅 재작성은 기업 역사상 가장 큰 실수 중 하나가 될 수 있습니다. 수년에 걸쳐 축적된 레거시 코드에는 명시적 로직뿐 아니라 수많은 버그 픽스, 엣지 케이스 처리, 암묵적 비즈니스 규칙이 담겨 있습니다. 이를 새로 재현하기란 생각보다 훨씬 어렵고, 그 사이 비즈니스는 멈추지 않습니다.
모듈형 모놀리스(Modular Monolith)는 단일 배포 단위를 유지하면서 내부를 도메인 모듈로 명확히 분리하는 방식입니다. 마이크로서비스의 분산 시스템 복잡도 없이 코드 품질과 유지보수성을 높일 수 있어, 팀 규모가 작거나 도메인이 아직 불명확한 초기 단계에 유리합니다. 실제로 마이크로서비스로 전환하기 전에 모듈형 모놀리스를 중간 단계로 두는 "모놀리스 → 모듈형 모놀리스 → 마이크로서비스" 경로도 현업에서 자주 채택됩니다.
Strangler Fig 패턴은 이미 프로덕션에서 수천 건의 요청을 처리하고 있는 시스템, 비즈니스 연속성이 절대적인 시스템, 도메인 경계가 서서히 명확해지고 있는 팀에 특히 적합합니다. 단, 이 패턴을 도입하면 전환 기간 동안 두 시스템을 동시에 운영해야 하므로 운영 복잡도가 일시적으로 높아집니다. 레거시와 새 서비스 양쪽에 대한 모니터링, 인프라 비용, 팀 인지 부하가 모두 증가한다는 점을 고려해야 합니다.
정리하면, 빅뱅 재작성은 리스크가 너무 크고, 아무것도 바꾸지 않는 것은 기술 부채를 방치하는 것입니다. Strangler Fig 패턴은 점진적이고 되돌릴 수 있는 전환을 가능하게 하여, 비즈니스 리스크를 최소화하면서 아키텍처를 현대화할 수 있는 현실적인 경로를 제공합니다.
운영 환경 적용 시 고려사항
Strangler Fig 패턴은 이론적으로 우아하지만, 운영 환경에서는 설계 단계에서 예상하지 못한 다양한 문제가 나타납니다. 아래는 실제 프로젝트에서 자주 마주치는 함정과 대응 방법입니다.
도메인 경계 식별의 어려움이 가장 흔한 초기 장애물입니다. 레거시 모놀리스에서는 서비스 간 경계가 없이 모든 코드가 서로를 직접 호출하는 경우가 많습니다. 어디서 분리해야 할지 명확하지 않은 상태에서 급하게 추출하면, 분리 이후에도 두 서비스 간 강결합이 남아 오히려 "분산 모놀리스(Distributed Monolith)"가 탄생하는 최악의 결과를 초래할 수 있습니다. 분리 전에 충분한 도메인 분석(이벤트 스토밍, DDD 워크샵 등)을 통해 경계를 명확히 하는 것이 필수입니다.
트랜잭션 경계 파괴도 심각한 문제입니다. 모놀리스에서 단일 DB 트랜잭션으로 처리하던 작업이 분리 후에는 여러 서비스에 걸친 분산 트랜잭션이 됩니다. 2PC(Two-Phase Commit)는 성능 및 가용성 측면에서 권장되지 않으며, 대신 Saga 패턴(코레오그래피 또는 오케스트레이션 방식)을 활용하여 분산 트랜잭션을 보상(compensation) 기반으로 처리하는 것이 현업에서 선호됩니다.
테스트 전략 역시 새로 수립해야 합니다. 레거시 시스템과 새 서비스 간의 계약(contract)을 검증하는 컨슈머 주도 계약 테스트(Consumer-Driven Contract Testing, CDCT)를 도입하면 API 인터페이스 불일치를 조기에 발견할 수 있습니다. Pact 같은 도구가 이를 지원합니다.
모니터링 및 관찰 가능성(Observability) 강화도 필수입니다. 레거시와 새 서비스가 혼재하는 기간에는 특정 요청이 어느 서비스를 거쳤는지 추적하기 어려워집니다. 분산 추적(Distributed Tracing)을 지원하는 OpenTelemetry를 도입하고, 각 서비스에 동일한 Trace ID를 전파하도록 설정해야 합니다. API 게이트웨이에서 X-Served-By 같은 커스텀 헤더를 추가하여 메트릭 대시보드에서 "레거시 처리 비율 vs 새 서비스 처리 비율"을 추적하면 전환 진행 상황을 시각적으로 관리할 수 있습니다.
마지막으로 페이처 플래그(Feature Flag)를 적극 활용하길 권장합니다. 특정 사용자 그룹이나 트래픽 비율에 대해서만 새 서비스로 라우팅하는 카나리 배포(Canary Deployment)를 지원하면, 문제 발생 시 코드 배포 없이 라우팅 설정만 변경하여 즉시 롤백할 수 있습니다.
맺음말
Strangler Fig 패턴은 레거시 모놀리스를 마이크로서비스로 전환하는 데 있어 점진성과 안전성을 핵심 가치로 삼는 아키텍처 전략입니다. 빅뱅 재작성의 위험을 피하면서도 아키텍처를 현대화할 수 있으며, 각 전환 단계가 독립적으로 배포·검증·롤백 가능하다는 점이 가장 큰 강점입니다.
이 패턴은 비즈니스 연속성이 중요한 프로덕션 시스템, 팀 역량이 점진적으로 성장하고 있는 조직, 도메인 경계가 아직 완전히 정립되지 않은 상황에서 특히 빛을 발합니다. 단, 전환 기간의 이중 운영 복잡도를 수용할 준비가 되어 있어야 하며, 도메인 분석과 데이터 마이그레이션 전략을 충분히 수립한 뒤 시작하는 것이 중요합니다.
Strangler Fig 패턴과 함께 살펴볼 관련 기술로는 Saga 패턴(분산 트랜잭션 처리), 이벤트 소싱(Event Sourcing)(상태 변경 이력 추적), CQRS(읽기/쓰기 분리)가 있습니다. 이들을 조합하면 마이크로서비스 전환 이후의 시스템을 더욱 견고하게 설계할 수 있습니다.
참고 자료
- Martin Fowler, Strangler Fig Application
- Microsoft, Strangler Fig pattern
- Sam Newman, Building Microservices (O'Reilly, 2021)
'프로그래밍 PROGRAMMING > 아키텍쳐' 카테고리의 다른 글
| Sidecar 패턴으로 크로스커팅 관심사 분리하기 (0) | 2026.04.06 |
|---|---|
| 이벤트 소싱 패턴 실전 적용하기 (0) | 2026.04.03 |
| Transactional Outbox 패턴으로 분산 시스템 이벤트 신뢰성 보장하기 (0) | 2026.04.03 |
| [SPRING/JAVA] Spring Boot 멀티모듈 프로젝트 구조 설계하기 (0) | 2026.04.02 |
| Event-Driven Architecture 적용하기 (0) | 2026.03.31 |