Mediator 패턴: 객체 간 통신을 한곳으로 모으는 중재자 설계
Mediator 패턴으로 N:N 상호작용을 중앙 조정하는 방법과 결합도 감소 효과, 마이크로서비스 오케스트레이션 적용 사례를 Java 예제로 정리한다.
2026-08-13 · 최초 발행 2025-10-14
컴포넌트가 늘어날수록 서로를 직접 참조하는 콜백이 얽혀 스파게티가 된다. Mediator 패턴은 객체들이 직접 참조하지 않고 Mediator를 통해서만 상호작용하게 만들어 이 문제를 정면으로 다룬다. GoF 행위 패턴 중 하나로, 복잡한 N:N 상호작용을 N:1:N 구조로 단순화해 결합도를 낮추고 변경 용이성과 테스트성을 높인다. 참여자는 Mediator(중재자), ConcreteMediator(구현), Colleague(동료 객체), 그리고 이들이 주고받는 이벤트·명령 메시지다.
구성 요소와 동작 방식
Mediator 인터페이스는 notify/handle/route 같은 메시지 진입점을 정의한다. ConcreteMediator는 라우팅 규칙, 검증, 순서 제어, 트랜잭션·보상 흐름을 맡는다. Colleague(동료 객체)는 자기 책임만 수행하고 다른 객체와의 상호작용은 Mediator에 위임한다.
메시지는 Colleague에서 Mediator를 거쳐 대상 Colleague나 서비스로 라우팅된다. 이벤트 타입·컨텍스트·정책에 따라 분기하고 오케스트레이션하며, 상태 일관성은 단일 트랜잭션으로 처리하거나 사가(보상 트랜잭션) 방식을 쓴다.
입력은 동료 객체가 발생시킨 이벤트·명령과 그 컨텍스트다. 처리는 규칙 검증 → 대상 선택 → 순차·병렬 호출 → 결과 집계·검증 순으로 진행되고, 출력은 상태 변이나 응답, 또는 추가 이벤트 발행이다. 검증에 실패하면 즉시 거부하고 원인을 반환하며, 호출이 실패하면 재시도 정책·보상 트랜잭션·폴백 경로를 적용하고, 타임아웃이 나면 서킷 브레이커나 디그레이드 전략을 쓴다. 동시성 측면에서는 단일 프로세스라면 Mediator 내부 임계영역을 최소화하도록 설계하고, 분산 환경이라면 사가 패턴·멱등 처리·메시지 중복 방지 토큰을 적용한다.
결합도를 낮추는 대신 감수하는 것
동료 객체 간 직접 참조를 없애면 의존 그래프가 단순해지고, 신규 동료나 시나리오를 추가할 때도 Mediator의 규칙만 확장하면 되니 영향이 최소화된다. 순서 제어·조건 분기·보상 로직을 한곳에 명시적으로 모아두면 비즈니스 워크플로를 코드 레벨에서 추적할 수 있다. Mediator 단위로 테스트하면 시나리오 커버리지가 올라가고, 로깅·트레이싱 삽입 지점이 하나로 모여 APM·분석도 쉬워진다. 전략·룰 엔진·플러그인 구조와 결합하면 기능 확장이 쉬워지고, Pub/Sub·CQRS·사가 오케스트레이터와 조합하면 마이크로서비스 확장에도 대응할 수 있다.
다만 중앙 집중은 병목이나 단일 장애점이 될 위험을 안고 있다. 규칙이 과도하게 쌓이면 Mediator 자체가 "갓 오브젝트"가 될 수 있어, 경량 Mediator로 분리하는 판단이 필요하다.
실제 적용 지점
데스크톱·웹 UI 폼에서는 입력값 변화 → 버튼 활성화·검증 → 제출까지의 흐름을 중앙에서 제어해 이벤트 난립과 양방향 의존을 없앤다. 업무 워크플로·승인 프로세스에서는 상태 전이, 역할별 승인 규칙, 알림·보상 절차를 Mediator가 오케스트레이션하고 감사로그·감사추적을 한곳으로 모아 컴플라이언스에 유리하다. 마이크로서비스 오케스트레이션에서는 주문→결제→재고→배송 호출 순서와 보상 흐름을 관리하며, 트랜잭션 경계를 분리하고 사가 패턴으로 최종 일관성을 유지한다. 도메인 이벤트 라우팅이나 Command Bus에서는 Command→Handler 매핑과 검증·권한·로깅 같은 파이프라인 동작을 중앙화하는데, .NET MediatR이나 Node/NestJS CQRS 라이브러리가 이와 비슷한 모델을 쓴다.
버튼과 텍스트박스로 보는 구현
전제조건은 JDK 17, 단일 파일로 컴파일·실행할 수 있다.
// File: MediatorDemo.java
interface Mediator {
void notify(Component sender, String event);
}
abstract class Component {
protected final Mediator mediator;
protected Component(Mediator mediator) { this.mediator = mediator; }
}
class Button extends Component {
Button(Mediator mediator) { super(mediator); }
void click() { mediator.notify(this, "click"); }
}
class TextBox extends Component {
private String text = "";
TextBox(Mediator mediator) { super(mediator); }
void setText(String t) { this.text = t; System.out.println("TextBox=" + text); }
String getText() { return text; }
}
class FormMediator implements Mediator {
private final Button submit;
private final TextBox input;
FormMediator() {
this.submit = new Button(this);
this.input = new TextBox(this);
}
Button submit() { return submit; }
TextBox input() { return input; }
@Override
public void notify(Component sender, String event) {
if (sender == submit && "click".equals(event)) {
if (input.getText().isBlank()) {
System.out.println("검증 실패: 입력값 공백");
} else {
System.out.println("제출 성공: " + input.getText());
}
}
}
}
public class MediatorDemo {
public static void main(String[] args) {
FormMediator mediator = new FormMediator();
mediator.input().setText("hello");
mediator.submit().click();
}
}
javac MediatorDemo.java로 컴파일하고 java MediatorDemo로 실행한다. 동료 간 직접 참조를 없애고 상호작용을 notify를 통해 Mediator에 위임하며, 검증과 흐름 제어를 Mediator로 일원화하는 게 핵심이다.
지표로 보면
| 지표 | 평가 | 비고 |
|---|---|---|
| 성능 | 보통 | 중앙 라우팅 오버헤드 존재, 캐싱/비동기화로 상쇄 가능 |
| 확장성 | 높음 | 규칙/핸들러 플러그인화, 샤딩/분할 Mediator로 스케일 |
| 일관성 | 높음 | 규칙 일원화, 사가/트랜잭션 경계 명시 가능 |
| 안정성 | 중간~높음 | 단일 장애점 위험, 이중화/페일오버 필요 |
| 운영 편의 | 높음 | 로깅/트레이싱/메트릭 삽입 지점 일원화 |
동료 간 직접 참조를 제거하면 직접 의존 수가 4070% 줄어든다는 추정이 있고(동료 간 레퍼런스 제거 기준), 시나리오 단위 테스트가 쉬워지면서 테스트 케이스 수 대비 커버리지가 1530%p 향상된다는 추정도 있다(시나리오 단위 테스트 용이성 기준). 규칙을 중앙화하면 수정 지점이 줄어 변경 영향 범위가 30~50% 축소된다는 추정도 함께 보고된다. 정성적으로는 설계 가시성·추적 가능성이 좋아지고, 도메인 규칙을 명시적으로 모델링하면서 공유 이해도가 올라가며, 장애 원인 분석 시간이 줄고 운영 안정성이 오른다.
도입할 때 고려할 점
Mediator는 경량화하고 도메인별·워크플로 단계별로 분할하는 게 좋다. 규칙 자체는 전략 패턴, 체인/파이프라인, 룰 엔진과 결합해 구성화하고, 코릴레이션 ID·구조화 로그·분산 트레이싱 같은 관찰성을 기본으로 갖춘다. 서킷 브레이커, 재시도·백오프, 멱등 키 같은 실패 내성 패턴도 함께 적용한다.
반대로 중앙 집중이 병목이나 단일 장애점이 될 수 있고, 복잡성이 다른 곳으로 옮겨갈 뿐이라는 위험도 있다. 단순히 이벤트를 브로드캐스트하는 정도의 요구라면 Observer나 Pub-Sub가 더 맞을 수 있다.