Coupling
은 한 모듈과 다른 모듈이 직접적으로 연결됨으로써 일어난다.
이 챕터는 음식 배달 앱을 만든다는 전제하에 진행된다. 사용자가 주문을 하면, 앱은 주문을 만들어낸 후 사용자에게 예상 배달 시간을 전달한다. 사용자는 배달 상태를 확인하거나 주문을 취소할 수 있다.
이 앱을 모듈을 활용해 만들어본다면, 주문을 하는 모듈과 배달을 관리하는 모듈로 나눌 수 있다. 주문 관리 모듈은 주문을 생성하고, 읽어내고, 갱신하고, 삭제하는 기능을 가질 것이고 배달 관리 모듈은 배달 시간을 추정하고, 배달을 시작하고, 완성하는 등의 기능을 가질 것이다.
// Order module definition
var orderModule = (function() {
var module = {},
deliveries = myApp.deliveryModule;
module.createOrder = function(orderData) {
var orderResult;
orderResult = // Code to actually create the order
orderResult.estimatedDeliveryTime =
deliveries.getDeliveryTime(orderData);
return orderResult;
};
return module;
})();
위에서 주문 모듈과 배달 모듈은 서로 긴밀하게 Coupling
되어있다. 주문 모듈이 예상 배달 시간을 가져오기 위해서는 배달 모듈과 연결이 되어있어야 하며, 모듈의 적절한 API를 불러와야 한다.
유지/보수가 쉽고 유연한 코드를 작성하기 위해서는 긴밀한 커플링을 피해야 한다. 각 모듈은 언제든 삭제가 가능해야 하며, 코드 재사용도 용이해야 한다.
만약 위 코드에서 예상 배달 시간을 가져오는 기능이 잘못되었다면, 이는 주문 프로세스를 완성하는 기능을 손상시키거나, 최악의 상황에서는 앱 전체를 망가뜨릴 수도 있다. 그러나 바람직한 방향은 한 기능에 오류가 있더라도 나머지 기능들과 앱은 그대로 작동하게끔 하는 것이다.
커플링의 긴밀함을 낮추기 위해서는 관찰자 패턴이라 불리우는 것의 여러 변형을 사용할 수 있다.그 변형 중 하나는 Pub/Sub
혹은 Publish/Subscribe
패턴이다.
어떤 경우에서는 관찰자가 자기 자신을 Event emitter에 직접적으로 register하는데 이는 어떠한 이벤트가 일어날때 마다 알림을 받기 위해서다. 이 접근법의 단점은 관찰자가 이벤트 에미터 객체와 이벤트에 대해 "알아야" 한다는 것이다. (= 연결이 되어있어야 한다)
그러나 이보다 나은 방법이 존재한다. 중재자 객체(Mediator object)를 보유한 다양한Pub/Sub
패턴을 통해 모듈간의 커플링을 줄일 수 있다. 중재자 객체는 publisher를 subscriber로 부터 고립시키는 역할을 한다.
Addy Osmani는 중재자를 관제탑에 비유함으로써 중재자에 대해 훌륭한 분석을 해냈다. 항공기는 절대 자기들끼리 교신하지 않는다. 그 대신 관제탑으로부터 모든 정보를 수신/제공하며, 따라서 관제탑 없이는 서로의 정보를 알 수 없다.
Pub/Sub
패턴을 적용한 라이브러리는 다양한데, 우리는 그 중에서 Morgan Roderick이 만든 PubSubJS를 사용할 것이다. 이 라이브러리는 모듈이 publish하거나 subscribe할 수 있는 topic에 기초한 것으로, 모듈은 필요할 때 토픽으로부터 unsubscribe할 수 있다.
document.addEventListener("DOMContentLoaded", function(event) {
var orderModule = (function() {
var orders = {},
EST_DELIVERY = 'current estimated delivery time',
estimatedDeliveryTime;
PubSub.subscribe(EST_DELIVERY, function(msg, data) {
console.log(msg);
estimatedDeliveryTime = data;
});
return orders;
})();
var deliveryModule = (function() {
var deliveries = {},
EST_DELIVERY = 'current estimated delivery time';
deliveries.getEstimatedDeliveryTime = function() {
var estimatedDeliveryTime = 1;
// Hard-coded to 1 hour, but likely an API call.
PubSub.publish(EST_DELIVERY, estimatedDeliveryTime);
};
return deliveries;
})();
deliveryModule.getEstimatedDeliveryTime();
});
위의 코드에서는EST_DELIVERY
라는 상수를 통해 'current estimated delivery time'이라 불리우는 토픽을 정의했다. 모듈들은 서로 연결되지 않은 상태로 이 토픽에 대해 publish 혹은 subscribe할 수 있다. 해당 코드에서는 delivery 모듈의 getEstimatedDeliveryTime
이라는 메소드를 직접 호출하고 있다.
이 메소드를 호출함으로써 delivery 모듈은 현재 예상 배달시간을 가져온 후 EST_DELIVERY
라는 토픽에 publish할 수 있다. 따라서 이 토픽을 구독하고 있는 구독자들은 배달시간이 업데이트된 후 알림을 받을 수 있게 된다.
order 모듈은 이 토픽에 대해 구독하고 있고, 따라서 주문이 들어올 때 마다 언제나 배달 예상시간에 관한 최신 업데이트를 받게될 것이다. delivery 모듈과 연결되지 않은 채로 말이다.