파사드 패턴을 사용해야 하는 경우

choi·2025년 11월 27일

[아키텍처] 파사드 패턴(Facade Pattern) 써야 하는 경우

백엔드 개발하다 보면 Controller, Service, Repository 계층 구조에 익숙해짐.
근데 비즈니스 로직이 복잡해질수록 Service가 비대해지거나, Controller가 너무 많은 Service를 호출하는 문제가 발생함.

이럴 경우 사용해야 하는 것이 파사드(Facade) 레이어.
실무에서 파사드 패턴을 도입해야 하는 확실한 타이밍 3가지를 정리해 봄.


1. 파사드(Facade)란?

건물의 정면(출입구)을 의미하는 단어처럼, 복잡한 내부 로직을 감추고 외부(Controller)에는 깔끔한 인터페이스만 보여주는 역할을 함.

핵심 역할: Controller와 여러 Service 사이의 중간 조율자 (Orchestrator)


2. 언제 써야 할까? (도입 기준 3가지)

Case 1. 컨트롤러가 너무 많은 서비스를 의존할 때

하나의 API 요청을 처리하기 위해 3~4개의 Service를 호출해야 한다면, Controller가 과도한 책임을 지고 있다는 신호임.

  • Before (Controller가 바쁨):
    Controller가 OrderService, PaymentService, DeliveryService를 다 주입받아서 순서대로 호출함. 로직이 Controller에 노출됨.
  • After (Facade 도입):
    Controller는 OrderFacade 하나만 알고 있음. "주문해줘"라고 요청하면 끝.

Case 2. 서비스 간의 순환 참조(Circular Dependency)를 끊을 때

개발하다 보면 UserServicePointService를 참조하고, 반대로 PointServiceUserService를 참조해야 하는 상황이 옴. 이때 서로 import 하면 순환 참조 에러가 발생함.

이때 파사드가 두 서비스를 위에서 내려다보며 조율하면, 서비스끼리는 서로 몰라도 되므로 순환 참조가 깔끔하게 해결됨.

Case 3. 트랜잭션 단위가 여러 서비스에 걸쳐 있을 때

여러 서비스의 로직이 '전부 성공하거나, 전부 실패해야 하는(Atomic)' 경우임.
개별 Service에 트랜잭션을 거는 것만으로는 부족할 때, 파사드 메서드에 @Transactional을 걸어 전체 흐름을 하나의 트랜잭션으로 묶어주기 좋음.


3. 코드로 보는 Before & After (Python)

사용자가 '상품 구매'를 요청했을 때의 흐름 비교.

Before: Controller가 모든 로직을 제어

Controller가 비즈니스 흐름(재고 확인 -> 결제 -> 알림)을 다 알고 있음. 코드가 지저분하고 재사용이 어려움.

# controller.py

class OrderController:
    def __init__(self, inventory_svc, payment_svc, noti_svc):
        self.inventory_svc = inventory_svc
        self.payment_svc = payment_svc
        self.noti_svc = noti_svc

    def order(self, request):
        # 1. 재고 감소
        self.inventory_svc.decrease(request.product_id)
        
        # 2. 결제 시도
        self.payment_svc.pay(request.user_id, request.amount)
        
        # 3. 알림 발송
        self.noti_svc.send(request.user_id, "주문 완료")
        
        return "Success"

After: Facade 레이어 도입

Controller는 단순해지고, 비즈니스 흐름의 조합은 Facade가 전담함.

# facades/order_facade.py

class OrderFacade:
    def __init__(self, inventory_svc, payment_svc, noti_svc):
        self.inventory_svc = inventory_svc
        self.payment_svc = payment_svc
        self.noti_svc = noti_svc

    # 여러 서비스의 흐름을 하나의 트랜잭션으로 관리하기 용이함
    def process_order(self, user_id, product_id, amount):
        self.inventory_svc.decrease(product_id)
        self.payment_svc.pay(user_id, amount)
        self.noti_svc.send(user_id, "주문 완료")

# controller.py

class OrderController:
    def __init__(self, order_facade):
        self.order_facade = order_facade

    def order(self, request):
        # 깔끔해진 컨트롤러
        self.order_facade.process_order(
            request.user_id, 
            request.product_id, 
            request.amount
        )
        return "Success"
profile
늦게나마 정신을 차리려고 하는 개발 뭐시기하는 사람

0개의 댓글