개발
Mediator 패턴: 복잡한 객체 의존을 한 방에 정리하기
스택큐힙리스트
2025. 7. 29. 09:04
반응형
UI 컴포넌트·모듈·마이크로서비스가 서로 직접 호출을 시작하면, 어느 순간 “누가 누구를 부르는지” 알 수 없는 스파게티 의존 그래프가 됩니다.
Mediator 패턴은 “중재자”-객체를 사이에 두어 모든 통신을 중앙 집중화합니다. 콜리그(Colleague)들은 중재자에게만 말하고, 중재자가 대신 다른 콜리그에게 메시지를 전달합니다. 결과적으로 각 객체는 서로를 전혀 모른 채 협력할 수 있고, 관계 변경도 중재자만 수정하면 됩니다.
언제 쓰면 좋은가?
- 이벤트 버스: 게시자와 구독자가 직접 연결되지 않고 중앙 EventBus(Mediator)를 통해 소통.
- 알림 시스템: 다양한 채널(SMS, 이메일, 앱 푸시)을 하나의 NotificationHub(Mediator)가 조정.
- 상태 머신: 여러 상태 객체가 TransitionManager(Mediator)를 통해 전이 로직을 공유.
구조 한눈에 보기
- Mediator 인터페이스: notify(sender, event)
- ConcreteMediator: 이벤트별 라우팅 로직 보유
- Colleague: send(event) → mediator 로 위임, receive() 로 콜백
Kotlin 미니 예제 – ChatEventBus
interface Mediator {
fun notify(sender: Colleague, event: String, payload: Any? = null)
}
class ChatEventBus : Mediator {
private val colleagues = mutableListOf<Colleague>()
fun register(c: Colleague) { colleagues += c }
override fun notify(sender: Colleague, event: String, payload: Any?) {
colleagues.filter { it != sender }
.forEach { it.receive(event, payload) }
}
}
abstract class Colleague(private val bus: Mediator) {
fun send(event: String, payload: Any? = null) = bus.notify(this, event, payload)
abstract fun receive(event: String, payload: Any?)
}
class User(bus: Mediator, private val name: String) : Colleague(bus) {
override fun receive(event: String, payload: Any?) =
println("[$name] $event : $payload")
}
fun main() {
val bus = ChatEventBus()
val a = User(bus, "철수").also { bus.register(it) }
val b = User(bus, "영희").also { bus.register(it) }
a.send("MSG", "안녕 👋")
}
이벤트 버스를 Mediator로 구현해 두 사용자 객체가 서로를 모른 채 메시지를 주고받습니다. 중앙 버스를 다른 구현체로 교체하면 로직 전체를 흔들지 않고 기능을 확장할 수 있습니다.
JUnit 테스트 스케치
@Test
fun `버스가 메시지를 중계한다`() {
val bus = ChatEventBus()
val received = mutableListOf<String>()
val a = object : Colleague(bus) {
override fun receive(event: String, payload: Any?) { /* no-op */ }
}
val b = object : Colleague(bus) {
override fun receive(event: String, payload: Any?) {
if (event == "PING") received += "${payload}"
}
}
bus.register(a); bus.register(b)
a.send("PING", "hello")
assertEquals(listOf("hello"), received)
}
장점 vs 단점
- 👍 결합도↓: Colleague 간 직접 참조 제거 → 테스트·유지보수 용이
- 👍 단일 행동 지점: 복잡한 비즈니스 흐름을 Mediator 하나에 집중
- 👎 Mediator 비대화: 모든 규칙이 몰리면 거대한 “신-객체”가 될 위험 → 서브-중재자 분할·이벤트 서브 타입 도입으로 완화
한 줄 요약
Mediator 패턴은 “다대다 의존의 골칫거리”를 “하나의 중재자”로 수렴시켜 코드의 복잡도를 기하급수적으로 낮춰 줍니다.
반응형