Strangler Fig 패턴: 모놀리식 애플리케이션을 마이그레이션하는 안전한 방법

Strangler Fig 패턴: 모놀리식 애플리케이션을 마이그레이션하는 안전한 방법

현대 소프트웨어 엔지니어링에서는 레거시 모놀리식 애플리케이션이 일반적인 과제입니다. 시간이 지남에 따라 성공적인 코드베이스는 너무 커지고 상호 연결되어 단순한 변경이 위험해지고 배포에 몇 시간이 걸리며 개별 기능을 확장하는 것이 사실상 불가능합니다.

팀이 마이크로서비스로 마이그레이션하여 시스템을 현대화하기로 결정하면 다음과 같은 중요한 질문에 직면하게 됩니다. 현재 비즈니스를 중단하지 않고 시스템을 어떻게 다시 작성할 수 있습니까?

한 가지 옵션은 “빅뱅” 재작성입니다. 즉, 비공개로 처음부터 새 시스템을 구축하고 하루 만에 모든 것을 전환하는 것입니다. 그러나 이는 매우 위험하며 종종 실패로 이어집니다.

다행히 더 안전하고 신뢰할 수 있는 대안이 있습니다. 바로 Strangler Fig Pattern입니다. 이 가이드에서는 Strangler Fig Pattern이 무엇인지, 왜 작동하는지, 그리고 명확한 다이어그램과 실제 코드를 사용하여 단계별로 적용하는 방법을 살펴보겠습니다.


실제 비유: 교살자 무화과 식물

이 패턴의 이름은 열대 우림에 서식하는 식물인 교살자 무화과의 이름을 따서 명명되었습니다.

교살자 무화과 씨앗은 기존 “숙주” 나무의 위쪽 가지에서 발아합니다. 무화과는 처음부터 자라는 대신 아래쪽으로 자랍니다.

  1. 뿌리가 숲 바닥에 닿아 토양에 고정될 때까지 숙주 나무의 줄기 아래로 뿌리를 내립니다.
  2. 시간이 지남에 따라 더 많은 뿌리가 자라서 숙주를 감싸고 융합됩니다.
  3. 무화과에는 빛이 숙주 나무에 도달하는 것을 차단하는 잎이 자랍니다.
  4. 결국 숙주나무는 죽어서 썩어가고, 속이 빈 교살무화과나무가 그 자리에 굳건히 서 있습니다.

소프트웨어 아키텍처에서 레거시 모놀리스는 호스트 트리이고 새로운 마이크로서비스는 교살자 그림입니다. 우리는 모놀리스 가장자리 주변에 새로운 서비스를 구축하여 모놀리스가 완전히 종료될 때까지 점차적으로 레거시 시스템에서 트래픽을 이동시킵니다.


‘빅뱅’ 재작성이 실패하는 이유

Strangler Fig 패턴의 메커니즘을 살펴보기 전에 대체 방법(완전히 재작성)이 왜 그렇게 위험한지 이해해 보겠습니다.

  • 월(또는 연)에 대한 가치 없음: 개발자는 코드를 작성하는 데 오랜 시간을 소비하지만 전체 프로젝트가 완료될 때까지 코드 중 어느 것도 실행되지 않습니다.
  • 범위 확장: 2년간의 재작성 동안 비즈니스 요구 사항이 변경됩니다. 대상이 이동하고 새 시스템은 재작성이 시작될 때 존재하지 않았던 기능을 지원해야 합니다.
  • 암시적 동작 누락: 모놀리스에는 수년간의 문서화되지 않은 버그 수정 및 극단적인 경우 처리가 포함되어 있습니다. 처음부터 다시 작성하다 보면 이러한 세부 사항을 잊어버리는 경우가 많습니다.
  • 높은 배포 위험: 대규모 레거시 시스템을 끄고 새 시스템을 동시에 켜면 문제가 발생할 경우 엄청난 폭발 반경이 생성됩니다.

교살자 무화과 패턴의 작동 방식

Strangler Fig Pattern의 핵심 아이디어는 증분 마이그레이션입니다. 전체 시스템을 마이그레이션하는 대신 한 번에 하나의 작은 기능 또는 “슬라이스"를 마이그레이션합니다.

Strangler Fig 패턴 마이그레이션 단계 다이어그램

마이그레이션 프로세스는 다음과 같은 5가지 주요 단계로 실행됩니다.

1. 제한된 컨텍스트 식별

모놀리스를 살펴보고 추출하기 쉬운 단일 독립형 비즈니스 기능을 식별하세요. 좋은 후보자는 다음과 같습니다.

  • 자주 변경되는 기능(그래서 팀은 독립적인 배포를 통해 빠르게 이점을 얻습니다)
  • 마이그레이션 파이프라인을 테스트하기 위한 간단하고 위험도가 낮은 기능(예: 정적 FAQ 또는 사용자 기본 설정 섹션)입니다.
  • 깨끗하고 잘 정의된 데이터베이스 경계가 있는 기능입니다.

2. 새로운 마이크로서비스 구현

식별된 기능을 완전히 새로운 최신 마이크로서비스로 구축합니다. 이 서비스에는 자체 데이터베이스와 배포 파이프라인이 있으며 최신 기술 스택을 사용하여 구축되었습니다. 결정적으로, 모놀리스의 이전 기능은 현재로서는 활성 상태로 유지되고 변경되지 않습니다.

3. 차단 레이어 도입

사용자에게 마이그레이션을 투명하게 만들기 위해 애플리케이션 앞에 가로채기 레이어(예: API 게이트웨이 또는 역방향 프록시)를 도입합니다. 이제 모든 클라이언트 트래픽이 먼저 이 게이트웨이로 이동합니다.

처음에 게이트웨이는 모든 요청의 100%를 레거시 모놀리스로 라우팅합니다.

4. 트래픽을 점진적으로 전환

새로운 마이크로서비스가 완전히 테스트되고 준비되면 차단 계층에서 라우팅 규칙을 업데이트합니다. 게이트웨이는 마이그레이션된 기능(예: /api/users)에 대한 요청을 모놀리스로 라우팅하는 대신 새 마이크로서비스로 리디렉션합니다.

다른 모든 요청은 계속해서 모놀리스로 이동됩니다. 새 서비스가 실패하거나 버그가 나타나는 경우 게이트웨이를 신속하게 업데이트하여 트래픽을 모놀리스로 다시 라우팅하여 중단을 최소화할 수 있습니다.

5. 폐기 및 반복

새로운 마이크로서비스가 일정 기간 동안 안정적으로 실행된 후 레거시 모놀리스 내부의 해당 코드를 안전하게 삭제할 수 있습니다.

그런 다음 다음 기능을 선택하고 프로세스를 반복합니다. 시간이 지남에 따라 모놀리스는 남은 트래픽이 없을 때까지 축소되며 레거시 서버를 완전히 폐기할 수 있습니다.


차단 계층: 익스프레스 라우팅 예

Strangler Fig 패턴의 핵심은 차단 레이어입니다. 클라이언트 애플리케이션(웹 앱, 모바일 앱)을 수정하지 않고도 요청을 리디렉션할 수 있습니다.

다음은 Express 기반 게이트웨이 프록시를 사용하는 실용적인 Node.js 예제입니다. 요청을 동적으로 라우팅합니다. 들어오는 요청이 마이그레이션된 경로와 일치하면 새 서비스로 이동합니다. 그렇지 않으면 레거시 모놀리스로 돌아갑니다.

const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
const PORT = 8080;

// Configuration: Target server URLs
const LEGACY_MONOLITH_URL = 'http://legacy-monolith-server:3000';
const NEW_USER_SERVICE_URL = 'http://new-user-service:3001';
const NEW_PAYMENT_SERVICE_URL = 'http://new-payment-service:3002';

// Simple logging middleware to track traffic distribution
app.use((req, res, next) => {
    console.log(`[ROUTE LOG] Incoming request: ${req.method} ${req.url}`);
    next();
});

// 1. MIGRATED: User registration & profile requests route to the new microservice
app.use('/api/users', createProxyMiddleware({
    target: NEW_USER_SERVICE_URL,
    changeOrigin: true,
    pathRewrite: {
        '^/api/users': '/v1/users', // Translate path format if necessary
    }
}));

// 2. MIGRATED: Payment transactions route to the new payment microservice
app.use('/api/payments', createProxyMiddleware({
    target: NEW_PAYMENT_SERVICE_URL,
    changeOrigin: true
}));

// 3. FALLBACK: All other legacy routes automatically default to the Monolith
app.use('/', createProxyMiddleware({
    target: LEGACY_MONOLITH_URL,
    changeOrigin: true
}));

app.listen(PORT, () => {
    console.log(`Interception Gateway routing traffic successfully on port ${PORT}`);
});

데이터베이스 처리: 가장 어려운 부분

API 트래픽 라우팅은 상대적으로 쉽지만 데이터 관리는 모놀리식 마이그레이션의 가장 어려운 측면입니다. 모놀리스에는 일반적으로 테이블이 고도로 조인된 단일의 대규모 데이터베이스가 있습니다.

서비스를 추출할 때 해당 데이터도 추출해야 합니다. 이를 처리하는 데는 두 가지 일반적인 접근 방식이 있습니다.

  1. 이중 쓰기: 차단 계층 또는 애플리케이션은 전환 단계에서 레거시 데이터베이스와 새 서비스 데이터베이스 모두에 동시에 데이터를 씁니다. 이렇게 하면 두 데이터베이스가 모두 동기화 상태로 유지됩니다.
  2. 변경 데이터 캡처(CDC): Debezium과 같은 도구는 레거시 데이터베이스 트랜잭션 로그를 모니터링하고 거의 실시간으로 새 마이크로서비스 데이터베이스에 대한 변경 사항을 자동으로 스트리밍합니다.

데이터베이스가 완전히 동기화되고 새 데이터베이스가 올바른 것으로 확신되면 읽기 트래픽을 새 서비스로 전환하고 레거시 테이블을 종료합니다.


비교: Big Bang Rewrite와 Strangler Fig

기능/측정항목 빅뱅 리라이트 교살자 무화과 패턴
위험 수준 매우 높음 낮고 관리됨
피드백 루프 매우 느림(마지막에만) 신속함(지속적인 생산 테스트)
롤백 전략 하드(백업 복원 필요) 쉬움(게이트웨이에서 경로 규칙 변경)
비즈니스 영향 파괴적인 다운타임 제로
시스템 복잡성 높음(빌드 단계 중) 높음(마이그레이션 단계 중)
배포 시간 대규모 싱글 출시 작고 빈번한 업데이트

결론

Strangler Fig Pattern은 모놀리스를 마이크로서비스로 마이그레이션하기 위한 업계 표준입니다. 레거시 코드를 한꺼번에 교체하지 않고 점진적으로 교체함으로써 “빅뱅” 오류의 위험을 제거할 수 있습니다.

배포를 소규모로 유지하고, 실제 프로덕션 트래픽에서 즉각적인 피드백을 제공하며, 팀이 마이그레이션 수명 주기 전반에 걸쳐 비즈니스 가치를 계속 제공할 수 있도록 해줍니다.

데이터베이스 마이그레이션을 관리하고 두 개의 병렬 시스템을 실행하면 운영 복잡성이 추가되지만 이것이 제공하는 안전성, 예측 가능성 및 안정성은 최신 클라우드 마이그레이션에서 선호되는 선택입니다.


Ghaznix 블로그에서 더 많은 소프트웨어 개발 및 백엔드 엔지니어링 통찰력을 살펴보세요 →