레거시 시스템 코어 재작성 없이 스트랭글러 패턴으로 AI 추가하기
요약
레거시 시스템에 AI 기능을 도입할 때, 전체 코어를 재작성하는 대신 스트랭글러 패턴(Strangler Fig pattern)을 활용하여 점진적으로 접근하는 것이 안전합니다. 핵심은 AI 컴포넌트가 단순히 제안만 하고, 기존 애플리케이션이 검증하고 실행하도록 명확한 경계를 설정하는 것입니다.
핵심 포인트
- 스트랭글러 패턴으로 위험도를 낮추고 점진적 현대화 가능
- AI는 요청 해석 및 제안에 사용하며, 비즈니스 상태 변경 권위는 기존 시스템 유지
- 어댑터(Adapter)를 통해 통제된 경계를 설정하고 데이터베이스 직접 접근 방지
레거시 시스템은 수년간 축적된 비즈니스 규칙, 데이터 관계, 운영 동작을 포함하고 있어 전체를 재작성하는 과정에서 아무도 다시 발견하고 싶어 하지 않는 것들이 많습니다. 하지만 같은 시스템이라 할지라도 배치 인터페이스(batch interfaces), 경직된 API(rigid APIs) 또는 워크플로우만 노출하여 새로운 AI 기능을 도입하기 어렵게 만들기도 합니다.
해결책이 반드시 코어 애플리케이션을 교체하는 것은 아닙니다. 더 안전한 접근 방식은 명확하게 정의된 경계에서 새로운 기능을 도입하고, 제한된 워크플로우를 그곳으로 라우팅하며, 기존 시스템을 중요한 비즈니스 운영의 권위자로 유지하는 것입니다.
이 지점에서 스트랭글러 피그 패턴(Strangler Fig pattern)이 유용해집니다. Martin Fowler는 이 패턴을 현대화에 대한 점진적 접근 방식으로 설명합니다: 기존 시스템 주변에 새로운 기능을 도입하고, 컴포넌트 간의 경계(seams)를 설정하며, 명확한 이유가 있을 때 행동을 점진적으로 이동시키는 것입니다. 이 패턴은 단일하고 위험도가 높은 교체 이벤트를 줄여주지만, 일시적인 아키텍처 복잡성을 야기합니다.
AI를 추가하면 이러한 경계는 더욱 중요해집니다. AI 컴포넌트는 요청을 해석하거나, 정보를 분류하거나, 기록을 요약하거나, 행동을 제안할 수 있습니다. 단순히 그럴듯한 행동을 생성할 수 있다는 이유만으로 비즈니스 상태를 변경할 권위를 자동으로 상속받아서는 안 됩니다.
모델이 아닌 워크플로우부터 시작하기
이미 고객 기록, 재고, 가격 책정 및 주문 업데이트를 처리하는 가상의 주문 관리 애플리케이션을 고려해 봅시다. 이 시스템은 작동하지만, 지원 직원이 요청을 해석하고 관련 정보를 찾는 데 시간을 소비합니다.
제안된 AI 기능은 들어오는 요청을 분류하고, 관련 주문 정보를 검색하며, 행동을 제안할 수 있습니다. 이것이 주문 관리 코어를 교체할 필요는 없습니다. 대신, 명확한 입력, 출력 및 운영 경계를 가진 하나의 워크플로우를 격리합니다.
예를 들어:
기존 애플리케이션은 고객 식별자, 주문 상태, 재고 및 비즈니스 규칙을 담당합니다.
- 어댑터(adapter)는 새로운 기능이 필요로 하는 특정 작업을 노출합니다.
- AI 컴포넌트는 요청을 해석하고 구조화된 제안을 생성합니다.
- 결정론적 검증 계층(deterministic validation layer)은 제안된 작업이 허용되는지 확인합니다.
- 기존 애플리케이션이 승인된 작업을 실행합니다.
- 로그와 추적(logs and traces)은 요청, 결정, 검증 결과 및 실행 결과를 기록합니다.
이 구분은 중요합니다. AI가 제안하고, 애플리케이션이 검증하고 실행하는 것입니다. 이렇게 함으로써 초기 현대화 범위는 모델을 핵심 비즈니스 로직의 두 번째 구현체로 만드는 것을 방지하면서 테스트할 수 있을 만큼 좁게 유지됩니다.
AI와 기존 시스템 사이에 어댑터 배치하기
흔한 실수는 인터페이스가 편리하다는 이유로 AI 컴포넌트를 데이터베이스 테이블이나 내부 함수에 직접 연결하는 것입니다. 이렇게 하면 모델이 생성한 결정이 공개 계약(public contracts)으로 설계되지 않았던 구현 세부 사항과 결합될 수 있습니다. 어댑터는 더 통제된 경계(boundary)를 제공합니다. 이는 새로운 기능의 요청 형식과 기존 애플리케이션이 지원하는 작업 간에 번역을 수행합니다.
예를 들어, 어댑터는 다음과 같은 개념적 작업을 노출할 수 있습니다:
get_order_status(order_id)
이 작업은 주문 식별자, 현재 상태 및 관련 타임스탬프를 포함하는 정의된 응답을 반환합니다. 무제한의 데이터베이스 접근을 노출하지 않습니다. 상태 변경 작업을 위해서는 경계가 훨씬 더 좁아야 합니다. 임의의 지침을 수락하기보다는, 어댑터는 명시적인 필드와 인증된 호출자(authenticated caller), 그리고 허용되는 작업의 정의된 세트를 가진 검증된 명령(validated command)을 받아야 합니다.
어댑터는 다음을 처리해야 합니다:
– 입력 유효성 검사 (Input validation): 핵심 기능을 호출하기 전에 누락된 식별자, 유효하지 않은 값 및 예상치 못한 필드를 거부합니다.
– 신원 확인 및 권한 부여 (Identity and authorization): 모델 출력에서 추론된 권한 수준이 아닌 실제 호출자의 권한을 사용하여 접근을 강제합니다.
– 계약 번역 (Contract translation): 새로운 요청 및 응답 구조를 기존 시스템의 확립된 인터페이스에 매핑합니다.
– 시간 초과 및 오류 (Timeouts and errors): AI 구성 요소가 무슨 일이 일어났는지 추측하도록 장려하는 대신 명시적인 실패 상태를 반환합니다.
– 감사 컨텍스트 (Audit context): 상관관계 식별자(correlation identifiers)와 해당 작업을 수행한 주체의 신원을 보존합니다.
이 계층은 단순한 호환성 래퍼가 아닙니다. 새로운 기능이 확립된 애플리케이션 제어를 우회하는 것을 방지하는 곳입니다.
비즈니스 결정과 AI 출력을 분리하기
구조화된 출력(Structured output)은 AI 통합을 검증하기 더 쉽게 만들 수 있지만, 유효한 구조가 어떤 동작이 올바르거나 승인되었음을 증명하지는 않습니다. 예를 들어, AI가 주문의 배송 주소 변경을 제안한다고 가정해 봅시다. 응답은 예상 스키마(schema)에 완벽하게 부합할 수 있지만, 잘못된 주문을 참조하거나 무단 변경을 시도할 수 있습니다.
따라서 실행 경로는 별도의 유효성 검사 단계를 가져야 합니다:
- – 출력 구조 및 필수 필드 유효성 검사.
- – 기존 애플리케이션을 통해 참조된 엔티티(entity) 해결.
- – 요청된 작업에 대한 호출자의 권한 확인.
- – 주문이 수정 자격이 되는지 여부와 같은 비즈니스 불변 조건(business invariants) 검사.
- – 위험 기반 승인 규칙 적용.
- – 확립된 애플리케이션 인터페이스를 통해 명령 실행.
- – 결과 기록 및 확인된 상태 반환.
이러한 검사는 모델이 규칙을 따랐다는 보장에 의존해서는 안 됩니다.
개념적인 실행 경계(execution boundary)는 다음과 같을 수 있습니다:
request = receive_user_request()
proposal = ai_component.propose_action(request)
if not schema_is_valid(proposal):
reject("invalid proposal")
if caller_is_authorized(proposal, caller):
reject("unauthorized")
if business_rules_allow(proposal):
reject("business rule violation")
if requires_human_approval(proposal):
return request_approval(proposal)
result = legacy_adapter.execute(proposal)
record_execution_result(result)
return result
이것은 의사 코드(pseudocode)이며, 즉시 적용 가능한 구현체는 아닙니다. 각 유효성 검사 함수는 애플리케이션의 실제 식별자 모델, 비즈니스 규칙 및 트랜잭션 의미론에 맞게 구현되어야 합니다.
중요한 속성은 액션을 생성하는 것과 그것을 실행할 권한을 부여하는 것 사이의 분리입니다.
트래픽 점진적 라우팅하기
스트랭글러 피그(Strangler Fig) 접근 방식은 기존 시스템의 모든 소비자(consumer)를 변경하지 않고 도입할 수 있는 경계(boundary)를 선택하는 것에 달려 있습니다. 라우팅 계층은 선택된 워크플로우를 새로운 기능으로 안내하는 동시에 다른 워크플로우는 기존 경로에 머무르게 할 수 있습니다. 라우팅 결정은 명시적이고 관찰 가능해야 합니다.
실제 배포(rollout)는 여러 단계로 진행될 수 있습니다:
1단계: 읽기 전용 지원 (Read-only assistance)
AI 기능이 승인된 인터페이스를 통해 요청을 분류하거나, 기록을 요약하거나, 정보를 검색하도록 허용합니다. 상태 변경 작업은 비활성화 상태로 유지합니다.
2단계: 섀도우 평가 (Shadow evaluation)
새로운 경로가 변경 사항을 커밋(commit)할 수 없도록 기존 워크플로우와 병행하여 실행됩니다. 대표적인 사례와 정의된 승인 기준을 사용하여, 그 제안된 출력을 확립된 프로세스와 비교합니다.
3단계: 제한적 실행 (Limited execution)
제어된 일련의 요청에 대해 위험도가 낮고 되돌릴 수 있는(reversible) 하나의 작업을 활성화합니다. 명시적인 권한 부여, 경계가 지정된 권한(bounded permissions), 그리고 정의된 승인 정책을 사용해야 합니다.
4단계: 기능별 확장 (Expand by capability)
현재의 경계가 실패 처리 및 운영 가시성을 포함하여 허용 가능한 동작을 입증한 후에만 추가 워크플로우를 추가합니다. 기능 플래그(feature flag)는 노출을 제어할 수 있지만, 이는 권한 부여 메커니즘이 아닙니다. 어댑터는 트래픽이 어떤 경로로 도달하든 관계없이 권한을 강제해야 합니다.
목표는 모든 요청을 새로운 경로로 가능한 한 빨리 옮기는 것이 아닙니다. 기존 시스템의 보장을 약화시키지 않으면서, 새로운 기능이 어디에 가치를 더하는지 학습하는 것입니다.
쓰기 기능을 활성화하기 전에 실패 경로를 설계하세요
AI 서비스는 사용 불가능해지거나(unavailable), 타임아웃을 초과하거나, 사용할 수 없는 결과를 반환하거나, 핵심 애플리케이션이 거부하는 동작을 제안할 수 있습니다. 통합은 각 조건에 대해 의도적인 대응책을 필요로 합니다. 읽기 전용 지원의 경우, AI가 생성한 요약 없이 기존 애플리케이션의 정상 응답을 반환하는 것이 유용한 폴백(fallback)일 수 있습니다. 상태를 변경하는 워크플로우의 경우, 폴백은 대체 동작을 자동으로 시도하기보다는 확립된 수동 프로세스로 제어권을 돌려주는 것을 의미할 수 있습니다.
재시도(Retries)에는 특별한 주의가 필요합니다. 쓰기 요청이 레거시 시스템에 도달했지만 응답이 손실된 경우, 호출자는 작업이 성공했는지 알지 못할 수 있습니다. 작업을 반복하면 해당 작업이 적절한멱등성(idempotency) 또는 중복 제거 의미론을 갖지 않는 한 중복적인 부작용을 초래할 수 있습니다.
상태 변경 작업의 경우 다음 사항들을 고려하세요:
- 재시도에도 살아남는 안정적인 작업 식별자(stable operation identifier).
- 적절한 경우, 수신 애플리케이션이 지원하는 멱등성 메커니즘.
- 거부됨(rejected), 실패함(failed), 알 수 없음(unknown) 실행 결과 간의 명시적 구분.
- 결과가 불확실할 때 권위 있는 시스템과의 조정(Reconciliation).
- 자동으로 안전하게 해결될 수 없는 작업에 대한 통제된 수동 에스컬레이션 경로.
모든 타임아웃을 작업 실패의 증거로 취급하지 마십시오. 응답이 호출자에게 도달하지 않았더라도 시스템은 변경 사항을 커밋했을 수 있습니다. 마찬가지로, 폴백이 첫 번째 경로의 결과가 알 수 없는 상태에서 같은 비즈니스 작업을 조용히 두 번째 경로를 통해 실행해서는 안 됩니다.
데이터 경계를 명시적으로 유지하세요
기존 애플리케이션 주변에 AI를 도입하는 것은 데이터 흐름(data-flow) 질문을 야기합니다. 즉, 어떤 정보가 코어 시스템에서 빠져나갈 수 있는지, 어디서 처리되는지, 그리고 무엇이 보존되어야 하는지에 대한 문제입니다. 어댑터는 워크플로우에 필요한 필드만 반환해야 합니다. 단순히 기본 레코드(underlying record)에 존재하는 필드라는 이유로 민감한 필드를 노출해서는 안 됩니다.
통합 과정에서는 프롬프트, 검색된 레코드, 모델 응답, 실행 메타데이터에 대해 어떤 일이 발생하는지 정의해야 합니다. 보존, 접근, 마스킹(redaction) 요구 사항은 데이터와 모델 제공업체에 따라 다르므로, 실제 환경에서 반드시 검증되어야 합니다.
관측 가능성(Observability)은 민감한 페이로드(payload)를 무분별하게 기록하지 않으면서도 실패를 재구성할 수 있을 만큼 충분한 정보를 포착해야 합니다. 유용한 필드에는 상관관계 ID(correlation ID), 워크플로우 이름, 정책 결과, 어댑터 지연 시간(adapter latency), 실행 상태, 에스컬레이션 사유 등이 포함됩니다. 추적(trace)은 AI 실패와 권한 거부(authorization rejection), 레거시 시스템 오류, 또는 불확실한 결과를 가진 타임아웃을 구별할 수 있도록 해야 합니다.
경계만으로는 충분하지 않은 경우를 알기
이 패턴이 레거시 시스템의 모든 부분을 무기한으로 보존해야 할 이유가 되는 것은 아닙니다. 좁은 어댑터(narrow adapter)는 기존 애플리케이션의 비즈니스 동작이 안정적이고 관리 가능한 인터페이스를 구축할 수 있을 때 유용합니다. 하지만 통합 과정에서 광범위하게 비즈니스 규칙을 중복하거나, 충돌하는 상태를 지속적으로 동기화해야 하거나, 신뢰성 있게 격리될 수 없는 동작에 대한 우회책(workarounds)이 필요하다면 그 효용성이 떨어집니다.
그 지점에서는 현대화 결정 자체를 변경해야 할 수도 있습니다. 도메인 기능(domain capability)을 더 추출하거나, 계약(contract)을 재설계하거나, 코어 시스템의 일부를 교체하는 것이 더 나을 수 있습니다. 이러한 결정은 대체 또는 보존이 항상 올바르다는 가정보다는, 경계에서 발견된 제약 조건(constraints)을 따라야 합니다.
전환 아키텍처(transitional architecture)에도 비용이 따릅니다. 추가되는 어댑터, 라우팅 규칙, 정책 경계는 팀이 테스트하고, 관찰하고, 보안하며, 궁극적으로 유지하거나 제거해야 하는 무언가가 됩니다. 팀은 해당 구성 요소에 대한 소유권을 정의하고, 주기적으로 그 경계가 여전히 목적을 수행하는지 재평가해야 합니다.
되돌릴 수 있을 만큼 작은 변화부터 시작하기
이 접근 방식의 유용한 결과는 단순히 AI 기능이 오래된 애플리케이션에 부착되었다는 사실만이 아닙니다. 조직이 비즈니스 상태(business state), 권한 부여(authorization), 실패 처리(failure handling), 운영 소유권(operational ownership)을 통제하면서 새로운 기능을 도입할 수 있다는 것입니다.
하나의 워크플로우, 하나의 명시적 인터페이스(explicit interface), 그리고 제한된 허용 액션 세트로 시작하십시오. 새로운 경로가 올바르게 작동하는지 측정하고, 가정(assumption)이 실패하는 지점을 파악하며, 이러한 질문들이 해결되는 동안 기존 경로는 유지합니다. 새로운 기능이 주변 시스템의 소유권을 조용히 가져가지 않으면서 독립적으로 진화할 수 있을 때 현대화는 이해하기 쉬워집니다.
핵심 코어가 사라지기 전에 주변 시스템이 개선될 필요는 없습니다. 그 경계가 바로 개선 사항을 테스트 가능하고, 제어 가능하며, 되돌릴 수 있는 지점입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기