Anti-Corruption Layer

Engineering

ACL 이란 무엇일까?

진격의 거인 벽

외부 시스템의 모델이 우리 시스템의 모델을 오염시키지 않도록 경계를 만드는 것

ACL은 Eric Evans가 Domain-Driven Design에서 소개한 전략적 설계 패턴입니다. 외부 시스템과 연동할 때 외부 모델이 우리 도메인 모델에 스며들지 않도록 두 모델 사이에서 데이터를 변환하고 번역하는 역할을 합니다.

외부 시스템과 연동하다 보면 서로 다른 모델을 만나게 됩니다. 이때 외부 시스템의 모델이 우리 도메인 모델에 침투하지 않도록 그 사이에 격리 계층을 두고 서로의 모델을 번역하는 설계 패턴입니다.

거창하고 새로운 개념이 아닙니다. 외부 API의 응답을 우리 시스템에 맞는 DTO로 변환하거나 서로 다른 형식의 외부 데이터를 중간에서 변환한 경험이 있을 겁니다.

우리가 무의식적으로 해오던 일을 하나의 개념과 이름으로 정리한 것이 ACL입니다.

여기서 말하는 오염이란?

외부 시스템의 개념이나 규칙이 우리 시스템 코드 곳곳으로 퍼지는 것

예를 들어보겠습니다.

우리 시스템이 약어 대신 풀네임, 상태는 enum, Y/N 값은 boolean으로 관리한다고 가정해 보겠습니다. 이제 외부 시스템에서 아래와 같은 데이터를 받는 상황이라고 해봅시다.

[
  {
    "id": "7d914e2e-068c-479f-a885-29f7c50fb08b",
    "ordst": "10",
    "delYn": "Y"
  },
  {
    "id": "6ce283f7-bec4-4825-80e2-2c468b5b1b88",
    "ordst": "50",
    "delYn": "N"
  }
]

이 값을 그대로 코어 (Application, Domain Layer) 에 전달하면 ordst, delYn 같은 외부 용어와 “Y”, “N”, “10” 같은 표현 방식이 우리 코드 곳곳에 퍼지게 됩니다. 이것이 ACL이 막고자 하는 오염 입니다

ACL이 왜 필요할까?

외부 시스템은 우리 시스템과 다른 이름, 데이터 형식, 처리 규칙을 사용합니다. 처음에는 필요한 값 몇 개만 바꿔 쓰면 될 것처럼 보이지만 연동이 늘어날수록 외부 시스템의 방식이 우리 코드 여러 곳에 퍼질 수 있습니다.

생각보다 이런 케이스는 정말 자주 일어납니다.

  • 외부 시스템에서는 Y, N 으로 boolean을 표현하지만, 우리는 True, False 로 처리할 경우
  • 외부 시스템에서는 car_type_cd 를 사용하지만, 우리는 vehicleTypeCode 를 사용할 경우
  • 외부 시스템에서는 1, 2, 3 으로 상태 코드를 관리하지만, 우리는 Waiting, Working, Completed 로 관리할경우
  • 외부 시스템에서는 값이 없을때 null 을 사용하지만, 우리는 null 없이 기본값 NONE 으로 표기할 경우

ACL은 외부 데이터를 우리 시스템이 이해하는 형태로 바꾸는 전용 경계입니다. 중요한 서비스, 도메인 에서는 익숙한 이름과 규칙의 데이터만 전달합니다.

얻을 수 있는 장점은 아래와 같습니다.

  • 외부 시스템의 변화가 ACL을 중심으로 수정하면 됩니다. (변경의 파급을 어느정도 막아줌)
  • 내부 코드가 읽기 쉬워집니다.
  • 우리 시스템의 규칙을 지킬 수 있습니다. 외부 시스템의 편의나 제약 때문에 내부 모델이 복잡해지는 일을 막아 줍니다.

ACL은 외부 시스템과 연결하면서도 시스템의 핵심 서비스와 도메인에는 우리 방식대로 유지할 수 있게 해주는 방어막입니다.

ACL 은 어디에 둬야할까?

정해진 위치가 있는 것은 아닙니다. ACL의 본질은 외부 시스템의 모델이 우리 시스템의 모델을 오염시키지 않도록 경계를 만드는 것이기 때문입니다.

클린 아키텍처를 예로 들면 우리 시스템에서 가장 중요한 Core(Application, Domain) 에 외부 세계의 모델이 직접 들어오지 않도록 경계에서 변환이 일어나야 합니다. 외부세계와 우리 코어와의 경계인 Presentation이나 Infrastructure 가 적절해보입니다.

중요한 것은 ACL이라는 이름의 별도 계층을 만드는 것이 아니라 외부 모델과 내부 모델의 경계를 명확하게 만드는 것입니다.

---
config:
  layout: elk
---

flowchart TB
    subgraph EXTERNAL["외부 시스템"]
        API["HTTP"]
        EX_SYSTEM["연동 외부 시스템"]
    end

    subgraph SYSTEM["우리 시스템"]
        subgraph PRESENTATION["Presentation"]
            SYSTEM_ACL["ACL<br/>내부 모델 ↔ 외부 모델"]
        end

        subgraph CORE["Core (Application, Domain)"]

            LOGIC["Business Logic"]
            DOMAIN["Domain Rule"]
        end

        subgraph INFRA["Infrastructure"]
            INFRA_ACL["ACL<br/>내부 모델 ↔ 외부 모델"]
        end
    end

    API --> PRESENTATION
    SYSTEM_ACL --> CORE

    INFRA_ACL --> CORE
    INFRA --> EX_SYSTEM

마치며

대부분의 프로젝트는 Standalone 시스템인 경우가 많지 않습니다. 기존 레거시 시스템이나 외부 시스템과의 연동이 필요한 경우가 많습니다. 그러다 보니 개인적으로는 외부 시스템의 용어와 스타일이 우리 시스템 내부까지 그대로 들어오는 것이 항상 마음에 걸렸습니다.

ACL은 정말 단순해 보이지만 이런 작은 경계가 결국 개발자에겐 큰 영향을 준다고 생각합니다. 외부 시스템의 용어를 이해하기 위해 코드를 다시 해석할 필요가 줄어들고 코드의 의도를 파악하기도 쉬워집니다. 사람이 바뀌더라도 시스템의 규칙을 이해하기 쉬워지고 결과적으로 소프트웨어를 오랫동안 유지하는 데에도 도움이 됩니다.

외부세계에 휩슬리지 않고 우리의 언어와 규칙을 사용하는것.

저는 이것이 ACL을 이해하는 가장 단순한 방법이라고 생각합니다.