ACL 패턴

2026. 4. 5. 00:29·BackEnd/Spring

[DDD/MSA] 외부 시스템으로부터 내 도메인을 지키는 법: ACL 패턴 적용기

새로운 프로젝트를 진행하며 마이크로서비스(MSA) 간의 통신을 구현할 때 가장 고민했던 지점은 **"외부 서비스의 변경이 우리 서비스의 핵심 로직을 흔들지 않게 만드는 것"**이었습니다. 이를 위해 도입한 ACL(Anti-Corruption Layer, 부패 방지 계층) 패턴에 대해 정리해 봅니다.

 

 

1. ACL(Anti-Corruption Layer)이란?

ACL은 서로 다른 도메인 모델을 가진 두 시스템이 통신할 때, 외부 시스템의 복잡한 모델이나 기술적 세부사항이 내부 도메인으로 침투(오염)하는 것을 막아주는 보호 계층입니다.

  • 핵심 특징: 모델 보호, 상호 운용성 확보, 결합도 낮춤.
  • 구현 방법: 주로 Adapter 및 Facade 패턴을 사용해 데이터를 변환(Mapping).
  • 사용 시기: 레거시 시스템 연동, 서로 다른 언어/모델을 사용하는 마이크로서비스 간 통합 시.

 

 

 

 

2. 왜 '부패(Corruption)'라는 표현을 쓸까?

만약 주문 서비스(Order)가 결제 서비스(Payment)의 응답을 그대로 가져다 쓴다고 가정해 봅시다.

  • 결제 서비스 응답: {"pay_status": "DONE"}
  • 주문 서비스 도메인: enum PaymentStatus { SUCCESS, FAIL }

이때 ACL 없이 결제 서비스의 pay_status를 직접 사용하면, 주문 서비스의 도메인 로직은 결제 서비스의 설계 방식에 종속됩니다. 이후 결제 서비스의 규격이 바뀌면 주문 서비스의 코드 전체가 깨지게 되는데, 이것이 바로 도메인 오염입니다.

 

 

 

 

 

3. 우리 프로젝트에서의 ACL 실전 적용

우리 서비스에서는 File 모듈과 User(Seller) 모듈과의 통신에서 각각의 특성에 맞춰 ACL을 구현했습니다.

Case 1: 응답 모델 및 오류 체계 번역 (File 모듈)

File 모듈은 고유의 응답 구조와 HTTP 예외 체계를 가지고 있습니다. 이를 Product 도메인이 직접 알지 못하도록 중간에서 번역합니다.

public List<FileConfirmResponse> confirmBulk(List<UUID> fileIds) {
    String url = fileModuleUrl + "/api/internal/files/confirm/bulk";
    // ... 헤더 및 요청 설정

    try {
        ResponseEntity<FileConfirmBulkApiResponse> response = restTemplate.exchange(
                url, HttpMethod.POST, entity, FileConfirmBulkApiResponse.class
        );

        FileConfirmBulkApiResponse body = response.getBody();
        if (body == null || body.data() == null) {
            throw new BusinessException(CommonErrorCode.FILE_CONFIRM_FAIL);
        }
        // 1. 응답 모델 번역: 외부 DTO를 내부에서 사용하는 FileConfirmResponse로 변환
        return body.data();

    } catch (HttpClientErrorException.NotFound e) {
        // 2. 오류 체계 번역: HTTP 404를 도메인 예외(FILE_NOT_FOUND)로 변환
        throw new BusinessException(CommonErrorCode.FILE_NOT_FOUND);
    } catch (Exception e) {
        throw new BusinessException(CommonErrorCode.FILE_MODULE_UNAVAILABLE);
    }
}

효과: ProductService는 이제 파일 모듈의 API 스펙이나 HTTP 통신 에러를 몰라도 됩니다. 오직 내부 비즈니스 예외만 처리하면 됩니다.

 

 

Case 2: 도메인 경계 명시와 인터페이스 활용 (Seller 모듈)

Seller(User) 정보를 가져올 때는 **의존성 역전 원칙(DIP)**을 활용해 더 견고한 ACL을 구축했습니다.

  • Application 레이어: 도메인이 필요한 기능을 인터페이스로 정의합니다.
  • Infrastructure 레이어: 실제 외부 API 호출(Feign, RestTemplate 등)을 통해 인터페이스를 구현합니다.
// [Application Layer] 도메인의 요구사항을 인터페이스로 선언
public interface SellerRepository {
    Optional<UserResponseDto> findSeller(UUID sellerId);
}

// [Infrastructure Layer] 실제 구현체 (Adapter)
@Component
@RequiredArgsConstructor
public class SellerAdapter implements SellerRepository {
    private final SellerClient sellerClient; // 외부 API 호출 Client

    @Override
    public Optional<UserResponseDto> findSeller(UUID sellerId) {
        return sellerClient.findSeller(sellerId);
    }
}

효과: 의존성 방향이 항상 도메인 → 인프라로 유지됩니다. 외부 시스템이 바뀌어도 SellerAdapter만 수정하면 도메인 로직은 안전합니다.

 

 

 

 

 

4. ACL 패턴 도입의 결과

  1. 독립성 유지: 서비스마다 자기만의 언어(Bounded Context)를 사용할 수 있습니다.
  2. 유지보수성 향상: 외부 API의 필드명이나 에러 코드가 바뀌어도 ACL 계층(Client, Translator)만 수정하면 됩니다.
  3. 테스트 용이성: 복잡한 외부 통신 로직을 ACL 인터페이스로 추상화했으므로, 단위 테스트 시 Mocking이 매우 쉬워집니다.

 

 

 

한 줄 요약

"ACL은 서로 다른 언어를 쓰는 서비스들 사이의 '통역사'이며, 외부의 변경으로부터 내 도메인의 순수성을 지켜주는 방패입니다."

 

마치며 MSA 환경에서 각 서비스가 의미를 잃지 않으려면, 서비스 간의 결합도를 낮추는 것이 필수적입니다. 이번 프로젝트에서 ACL 패턴을 적용해 보며, 클린 아키텍처에 한 발짝 더 다가갈 수 있었습니다.

 

'BackEnd > Spring' 카테고리의 다른 글

벡터 데이터(Vector Data)와 벡터 데이터베이스(Vector DB)  (0) 2026.03.13
MSA [2편]  (1) 2026.03.13
MSA [1편]  (0) 2026.03.13
'BackEnd/Spring' 카테고리의 다른 글
  • 벡터 데이터(Vector Data)와 벡터 데이터베이스(Vector DB)
  • MSA [2편]
  • MSA [1편]
JK-LEE98
JK-LEE98
백엔드 개발자
  • JK-LEE98
    JK-LEE98
    JK-LEE98
  • 전체
    오늘
    어제
    • 분류 전체보기 (60)
      • 나의 지식 공유 (4)
      • SQL (4)
      • PS (10)
      • BackEnd (32)
        • Java (3)
        • Spring (4)
        • 내일배움캠프 (20)
        • 프로그래머스 데브코스 (5)
      • 건강한 나 되기🍀 (10)
  • 인기 글

  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
JK-LEE98
ACL 패턴
상단으로

티스토리툴바