퍼사드 패턴

반응형

지금까지 두 가지 생성 패턴을 학습했습니다. 팩토리 패턴과 빌더 패턴입니다. 팩토리 패턴은 객체 생성 과정에서
어떤 구현체를 생성할 것인가에 대한 결정을 호출부에서 분리하는 데 초점이 맞춰진 패턴입니다.즉, 생성의 선택 책임을 한 곳으로 모아 코드의 결합도를 낮추는 것이 핵심입니다. 반면 빌더 패턴은 객체를 어떻게 구성해서 생성할 것인가에 집중합니다.
생성자 파라미터 폭발 문제를 해결하고, 복잡한 객체를 단계적으로 조립함으로써 가독성과 안정성을 확보하는 것이 목적입니다. 
그렇다면, 퍼사드 패턴은 어떤 패턴일까요? 뭐랄까 이전의 패턴들과 다르게 용어부터 심상치 않습니다. 

퍼사드는 외관이라는 뜻을 가지고 있다고 합니다. 그렇다면, 퍼사드 패턴은 겉보기에 괜찮아 보이게 만드는 패턴일까요?

왜 탄생하게 되었을까?

가장 먼저 이 패턴이 왜 탄생했는지 알아야 한다고 생각합니다.

퍼사드 패턴은 코드를 예쁘게 만들기 위해 등장한 패턴이 아닙니다.
이 패턴은 객체지향 시스템이 커질수록 반복적으로 드러나는 구조적 문제를 해결하기 위해 탄생하였다고 합니다.

반복적으로 드러나는 구조적 문제?

이미 한 곳에서 정의된 기능의 흐름(서비스 조합과 호출 순서)이 다른 사용 지점에서도 동일한 형태로 다시 작성되는 상황을 의미합니다.

예를 들어, API 계층에 다음과 같은 기능 흐름이 있다고 가정해봅시다.

A기능 -> B기능 -> C기능 -> 완료

이 기능이 배치 작업에서도 필요해지면, 배치 코드 역시 동일한 흐름을 그대로 다시 구현하게 됩니다.

A기능 -> B기능 -> C기능 -> 완료

이렇게 되면 기능 자체가 아니라, 기능을 수행하기 위한 흐름과 조합 규칙이 여러 곳에 중복되며,
변경·테스트·확장 시 항상 동일한 수정이 반복되는 구조가 됩니다.

퍼사드 패턴은 이러한 문제를 해결하기 위해, 여러 서비스의 조합과 호출 흐름을 하나의 진입점으로 고정하고
외부에서는 해당 흐름을 다시 만들지 않도록 하기 위해 사용됩니다.

그러면 단순하게 생각했을때

class XXXFacade {
  public void execute() {
    기능A();
    기능B();
    기능C();
  }
}

이와 같은 구조는 퍼사드 패턴의 개념적 스켈레톤으로는 타당합니다. 외부에서는 execute()라는 단일 진입점만 호출하고,
그 내부에서 여러 기능이 정해진 순서로 조합되어 실행되기 때문입니다.

다만 실제 구현에서는 기능A/B/C가 퍼사드 내부의 로직이 아니라 각각의 서비스 호출로 구성되어야 합니다.
퍼사드는 어디까지나 서비스들의 조합과 호출 흐름만을 책임지는 역할이기 때문입니다.

만약 퍼사드가 직접 모든 로직과 판단을 수행하게 되면, 이는 더 이상 퍼사드 패턴이 아니라 책임이 과도하게 집중된 God Object로 변질될 수 있습니다.

퍼사드의 진실과 오해

그렇다면 기능만 모아서 한 번에 사용하면 되는 걸까요?
그렇지 않습니다.

퍼사드 패턴의 핵심은 단순히 여러 기능을 묶는 것이 아니라,
항상 이 순서로 이 기능들을 사용하라는 규칙을 한 곳에 고정하는 데 있습니다.

즉, 퍼사드는 기능을 실행하는 객체가 아니라, 기능이 사용되는 표준적인 흐름을 정의하고 강제하는 객체입니다.

그래서 API, Batch, Event와 같은 서로 다른 호출 지점들이 같은 기능 흐름을 각자 구현하지 않고,
하나의 진입점을 통해 동일한 흐름을 호출만 하게 되는 순간, 그 구조는 퍼사드 패턴이라고 볼 수 있습니다.

예시를 통해 한번 이해해봅시다.

API

 
class LoginController {

  public void login() {
    userService.findUser();
    authService.verify();
    tokenService.issue();
    auditService.log();
  }
}

Batch

class TokenReissueJob {

  public void run() {
    userService.findUser();
    authService.verify();
    tokenService.issue();
    auditService.log();
  }
}
 

Event

class LoginEventHandler {

  public void handle() {
    userService.findUser();
    authService.verify();
    tokenService.issue();
    auditService.log();
  }
}

여기에는 다음과 같은 문제가 있습니다.

 

  • 같은 흐름(A → B → C → D) 이 3군데에 복제됨
  • 순서 변경 시 전부 수정
  • 조합 책임이 API / Batch / Event에 흩어져 있음

이것을 퍼사드로 바꾼다면 이렇게 변경이 된다고 합니다.

퍼사드

class LoginFacade {

  private final UserService userService;
  private final AuthService authService;
  private final TokenService tokenService;
  private final AuditService auditService;

  public void execute() {
    userService.findUser();
    authService.verify();
    tokenService.issue();
    auditService.log();
  }
}

그러면 각각

class LoginController {

  private final LoginFacade loginFacade;

  public void login() {
    loginFacade.execute();
  }
}

 

 

 

요렇게 사용하는것이 퍼사드 패턴이라고 합니다.

갑자기 머릿속을 스치는 정보가 있습니다.

그것은 바로 ApplicationService입니다. 이전에 퍼사드를 들었을때(?) 여러 레파지토리를 하나의 클래스에서 사용했던걸로 기억합니다. 제 기억이 맞다면 그걸 ApplicationService라고도 불렸던거 같은데 그렇다면 뭐가 맞는 표현인걸까요?

ApplicationService vs 퍼사드

차이는 무엇을 기준으로 이름 붙이느냐에 따라 다르다고 합니다.
퍼사드는 이 클래스는 복잡한 내부 협력 구조를 감싸서 외부에 단순한 인터페이스를 제공하고 있는가를 물어보고
application service는 이 클래스는 유스케이스를 실행하는 진입점인지 물어본다고 합니다.

결국 하나의 클래스가 퍼사드이면서 Applicatio Service일수도 있다는 거죠. 그렇다면 다음 코드는 퍼사드일까요? 아닐까요?

  public void unlike(String userId, Long productId) {
    try {
      ProductStatus productStatus = productRepository.hasWithLock(productId).orElseThrow(
          () -> new CoreException(ErrorType.NOT_FOUND, "해당하는 상품이 존재하지 않습니다.")
      );

      Optional<LikeModel> likeModel = likeRepository.liked(userId, productId);

      // 좋아요가 해제되있다면 탈출..
      if (likeModel.isEmpty()) {
        return;
      }

      likeRepository.unlike(userId, productId);

      // 좋아요 감소
      cacheRepository.decrement(productId);
      publisher.decrease(userId, productId, productStatus.getLikeCount() - 1);
      publisher.send(userId, "DECREASE", userId + "가 productId : " + productId + "에 좋아요를 해제했습니다.");
    } catch (Exception e) {
      publisher.fail(userId, "DECREASE", e.getMessage());
    }

  }

아쉽게도 위 코드는 퍼사드가 아니라고 합니다. 퍼사드는 다음이 만족해야 한다고 합니다.

  1. 여러 하위 컴포넌트의 사용 흐름을 고정한다
  2. 외부에서 그 흐름을 다시 구현하지 못하게 차단한다
  3. 직접 비즈니스 판단을 하지 않는다
  4. 조합(Orchestration)에만 집중한다

만약 위 코드를 퍼사드로 바꾼다면 다음처럼 바꿔야 할것입니다.

[Facade]
  - unlike(userId, productId)
    -> LikeService.unlike(...)
    -> CacheService.decrease(...)
    -> EventPublisher.publish(...)

위 코드는 퍼사드 패턴의 엄밀한 정의로 보면 비즈니스 규칙을 포함하고 있어서 퍼사드라고 보긴 어렵고,
다만 구조적으로는 여러 컴포넌트를 감싸는 퍼사드-like한 Application Service라고 보는 게 맞다고 생각합니다.

결론

정리하면 퍼사드는 객체의 흐름을 고정하고 그것을 반복적으로 사용한다면 (실제로 반복 사용이 아니어도됨) 퍼사드라는 것을 이번에 배웠습니다. 그리고 지금까지 단순하게 서비스나 레파지토리를 섞는 과정이 퍼사드라고만 알고 있었지만,


알고보니 퍼사드 like이고 application service여더라구요. 그리고 퍼사드는 특히 웹 개발이나 앱 개발을 하는 경우 많이 사용이 되어질거 같습니다. 사용은 할 수 있겠지만 잘 사용하지는 않을거 같습니다.

 

'개발 > 디자인패턴' 카테고리의 다른 글

빌더 패턴  (0) 2025.12.29
디자인패턴과 안티 패턴  (1) 2025.12.25
팩토리 메소드 패턴  (0) 2025.12.22
템플릿 메소드 패턴  (0) 2022.03.19
퍼사드 패턴  (0) 2021.12.05

댓글

Designed by JB FACTORY