188. 부패 방지 계층 (ACL, Anti-Corruption Layer) 패턴
⚠️ 이 문서는 도메인 주도 설계(DDD)와 마이크로서비스 아키텍처(MSA) 도입 시, 더럽고 얽혀있는 구형 레거시 시스템(Monolithic)과 깔끔하게 잘 설계된 신규 마이크로서비스가 서로 통신해야 할 때, 레거시의 낡은 데이터 모델과 나쁜 냄새(부패)가 신규 시스템의 순수한 도메인 모델을 오염시키지 않도록 중간에 세워두는 튼튼한 '통역관 겸 방호벽' 패턴을 다룹니다.
핵심 인사이트 (3줄 요약)
- 본질: 신규 시스템이 레거시 시스템의 API나 DB를 직접 호출하지 못하게 하고, 중간에 두 시스템의 데이터 포맷을 변환(Translation)하고 개념의 차이를 메워주는 얇은 중개 계층(Adapter/Facade)을 두는 설계 기법이다.
- 가치: 낡은 레거시 시스템의 기괴한 컬럼명(
cust_nm_tmp2)이나 비합리적인 로직에 맞추기 위해 신규 시스템의 예쁜 코드(CustomerName)가 더러워지는 것(개념 오염)을 완벽하게 차단하여, 신규 도메인의 순수성(Purity)을 지켜낸다.- 기술 체계: GoF 디자인 패턴의 퍼사드(Facade)와 어댑터(Adapter) 패턴을 조합하여 구현되며, 훗날 레거시 시스템이 수명을 다해 폐기(Retire)될 때 이 ACL 계층만 똑 떼어내어 버리면 신규 시스템은 아무런 타격 없이 독자 생존이 가능하다.
Ⅰ. 레거시 통합의 저주: 오염은 전염된다
새 집을 지어놓고 헌 집의 쓰레기를 그대로 가져오면 새 집도 쓰레기장이 된다.
- 레거시 시스템의 기괴한 모델:
- 20년 된 은행의 레거시 DB에는 '고객' 테이블이 없고, 대신
TBL_USER_DUMP_01이라는 테이블에 이름, 나이, 차 번호, 강아지 이름까지 100개의 컬럼이 스파게티처럼 뭉쳐있다.
- 20년 된 은행의 레거시 DB에는 '고객' 테이블이 없고, 대신
- 직접 통합(Direct Integration)의 끔찍한 결과:
- 신규 MSA로 '대출 서비스'를 예쁘게 짰다. 그런데 레거시에서 고객 정보를 가져오려다 보니, 새 코드 안에 레거시의 이상한 컬럼명(
cust_nm_tmp2)을 매핑하는 더러운 코드가 수백 줄 섞여 들어가게 된다. - 시간이 지나면 레거시의 더러운 데이터 구조가 신규 MSA 코드를 스멀스멀 지배하기 시작한다. 이를 '도메인 모델의 부패(Corruption)'라고 부른다.
- 신규 MSA로 '대출 서비스'를 예쁘게 짰다. 그런데 레거시에서 고객 정보를 가져오려다 보니, 새 코드 안에 레거시의 이상한 컬럼명(
📢 섹션 요약 비유: 깨끗한 1급수 저수지(신규 MSA)를 만들었는데, 옆에 있는 썩은 구정물 웅덩이(레거시)에서 물을 끌어다 쓰기 위해 그냥 파이프를 직결해 버리면, 1급수 저수지 전체가 순식간에 썩은 구정물로 오염(부패)되어 버리는 끔찍한 상황입니다.
Ⅱ. 부패 방지 계층(ACL)의 통역 및 방어 메커니즘
구정물을 정수기(ACL)에 통과시켜 깨끗한 물만 새 저수지에 넣어야 한다.
- ACL의 위치와 역할:
- 신규 마이크로서비스와 레거시 시스템 **사이의 경계(Boundary)**에 배치되는 논리적, 물리적 컴포넌트(마이크로서비스나 라이브러리)다.
- 신규 서비스는 레거시의 존재를 아예 모른 채, 오직 ACL에게만 자신들의 예쁜 표준 포맷(예: JSON)으로 데이터를 달라고 요청한다.
- 어댑터(Adapter)를 통한 변환 (Translation):
- 요청을 받은 ACL은 즉시 작업복을 입고 더러운 레거시 시스템으로 들어가 구형 SOAP 프로토콜이나 기괴한 DB 쿼리를 날려 데이터를 퍼 온다.
- 그리고 ACL 내부에서 레거시의
cust_nm_tmp2값을 신규 모델의CustomerName으로 예쁘게 씻고 닦아 변환(Mapping)한 뒤 신규 서비스에 전달한다. - ┌─────────────────────────────────────────────────────────┐ │ [신규 MSA] <--(예쁜 JSON 통신)--> [ACL 통역관] <--(더러운 SOAP 통신)--> [레거시] │ └─────────────────────────────────────────────────────────┘
- 독자 생존과 손절의 용이성:
- 3년 뒤, 회사가 마침내 낡은 레거시 시스템의 전원을 뽑고(폐기) 새로운 2세대 시스템을 도입하기로 했다.
- 이때 신규 MSA의 소스코드는 단 한 줄도 고칠 필요가 없다. 중간에 낀 통역관(ACL)의 뒷단 연결 플러그만 새로운 2세대 시스템으로 바꿔 끼우면 끝이다.
📢 섹션 요약 비유: 미국인(신규 MSA)이 한국 시골의 할머니(레거시 시스템)와 거래를 해야 하는데, 미국인이 직접 사투리와 옛날 단위를 배우면 뇌가 오염됩니다. 그래서 완벽한 이중언어 통역사(ACL)를 고용해 할머니의 구수한 사투리를 세련된 비즈니스 영어로 완벽히 번역해서 전달하게 만드는 철저한 격리 시스템입니다.
Ⅲ. 트레이드오프: 언제 ACL을 써야 하는가?
좋은 방화벽이지만 공짜가 아니다. 배보다 배꼽이 커질 수 있다.
- 도입의 대가 (성능 저하와 관리 오버헤드):
- 통신 과정에 ACL이라는 징검다리(Hop)가 하나 무조건 추가되므로, 네트워크 지연(Latency)이 발생한다.
- 데이터를 매번 파싱(Parsing)하고 객체를 변환하는 연산 때문에 CPU 비용이 들고, 별도의 통역관 코드를 유지보수해야 하는 인건비가 든다.
- ACL 적용이 필수적인 곳 (판단 기준):
- 레거시 시스템이 곧 폐기될 운명일 때: 굳이 레거시에 맞춰 내 코드를 더럽힐 이유가 전혀 없다. 임시로 통역관만 세워두고 존버(?)하면 된다.
- 두 시스템의 도메인 모델이 너무나도 다를 때: 개념적 차이가 너무 커서 억지로 맞추려다 버그가 폭발할 것 같은 경우.
- 적용하면 안 되는 곳:
- 레거시 시스템과 신규 시스템의 차이가 거의 없거나, 성능(0.01초의 응답 속도)이 너무나도 중요한 구간에서는 변환 비용이 더 치명적이므로 그냥 직접 통신(Shared Kernel)을 고려해야 한다.
📢 섹션 요약 비유: 통역사(ACL)를 쓰면 의사소통 시간(성능)이 2배로 걸리고 통역사 월급(유지보수)이 나갑니다. 따라서 곧 거래를 끊을 사람이나 도저히 말이 안 통하는 외계인(폐기 예정 레거시)과 대화할 때만 통역사를 부르고, 말이 대충 통하는 이웃과는 답답하더라도 콩글리시(직접 통합)로 바로 대화하는 것이 속 편할 때도 있습니다.