Mediator/Middleware 패턴
1. 사용이유
이 패턴은 공항에서 비행기(object)의 동선을 관리하는 관제소(mediator)에 비교할 수 있습니다.
비행기끼리 직접 통신하면 사고로 이어질 수 있지만, 관제소에서 상황을 전달받아 통제를 하게 되면 서로 충돌 없이 안전하게 활주로를 이용할 수 있게 됩니다.
→ 객체끼리 서로 통신하게 하여 [ 다 : 다 ] 의 관계를 이루는 모습
마찬가지로, 자바스크립트에서도 마찬가지입니다. 종종 여러 객체들이 서로 데이터를 주고 받는 상황이 생기곤 하는데, 컴포넌트가 많아질수록 통신의 횟수는 많아지고 점점 흐름을 파악하기 어려워 질 것입니다.
또한, object간의 통신은 유지보수가 쉽고 다른 object를 건드리지 않으면서, 애플리케이션의 일부분을 안전하게 수정할 수 있는 방식으로 이루어져야 합니다.
따라서 아래와 같이 컴포넌트들이 서로 직접 통신하는 대신 중재자 역할을 하는 객체를 통하도록 하는 것입니다.
→ 객체의 요청들을 모두 중재자 객체에게 보내는 모습
중재자 객체가 요청을 받아 이를 필요로 하는 객체들에게 전달을 하는 것입니다. 주로 중재자는 객체나 함수로 구현됩니다.
2. 실무에서 사용되는 예) 채팅 구현
실무에서 이 중재자 패턴이 적합한 곳은 채팅을 구현할 때입니다.
채팅 앱에서 사용자들은 직접 서로 메시지를 주고 받지 않고, 채팅 서버에 메시지를 전송하고 서버가 각 사용자에게 메시지를 전달하는 형태이기 때문입니다.
// 중재자 역할 : 메시지를 관리하는 역할 class ChatRoom { // 사용자가 보낸 메시지를 기록하고 콘솔에 출력 logMessage(user, message) { const time = new Date() const sender = user.getName() console.log(`${time} [${sender}]: ${message}`) } } //ChatRoom의 사용자 객체 class User { constructor(name, chatroom) { this.name = name this.chatroom = chatroom } getName() { return this.name } send(message) { this.chatroom.logMessage(this, message) } }
- 사용자는 ChatRoom과 연결되는 User를 만들어낼 수 있고, 각 인스턴스는 send 메서드를 통해 다른 사용자에게 메시지를 전송할 수 있습니다.
- 사용자가 ChatRoom에 직접 접근하지 않고, 자신의 메시지를 ChatRoom에 전달하기 때문에, 사용자 간의 직접적인 의존성을 줄이고 메시지 전달 로직을 중앙 집중화할 수 있는 것입니다.
<MiddleWare>
- MiddleWare는 Middle software의 약자로, request와 respond 사이에 있는 controlelr입니다.
3. 사례 분석
- 많이 사용하는 웹 서버 프레임워크인
Express.js
를 예시로 들어보겠습니다. - express가 middleware라는 용어를 대중화하여 구체적으로 디자인 패턴을 구현했는데요, 이를 통해 개발자가 프레임워크의 핵심에 손대지 않고도 새로운 기능을 쉽게 만들고 배포하여 기능을 추가할 수 있도록 한 것입니다.
- 특정 라우팅 경로에 대해 콜백을 추가함으로써 요청을 처리할 수 있습니다.
/
경로를 요청했을 때 요청에 헤더를 추가해야 한다고 가정해봅시다.- 아래의 예시와 같이 미들웨어를 추가하여 처리할 수 있습니다.
const app = require('express')() app.use('/', (req, res, next) => { req.headers['test-header'] = 1234 next() })
- next함수는 요청-응답 사이클에 걸려있는 다음 콜백을 호출합니다.
- 이전 콜백의 변경 사항을 다음 콜백에서 확인할 수 있는 것입니다
const app = require('express')() app.use( 1. '/', (req, res, next) => { 2. req.headers['test-header'] = 1234 3. next() }, 4. (req, res, next) => { 5. console.log(`Request has test header: ${!!req.headers['test-header']}`) 6. next() } )
- 클라이언트가
/
경로에 요청을 보냅니다.
- 첫 번째 미들에워가 실행되어 요청 객체의 헤더에 ‘test-header’를 추가 한뒤 값을 설정해줍니다.
- 그런 다음, next()를 호출해서 다음 미들웨어로 이동합니다.
- 그렇게 두번째 미들웨어가 실행됩니다.
- 두번째 미들웨어는 요청 객체의 헤더에 ‘test-header’가 추가되었는지 확인하고, 그 결과를 로그에 출력하는 것입니다.
- 마지막으로 다시 next()를 호출하여 요청-응답 사이클의 다음 단계로 넘어가는 것입니다.
- 정리하자면, 이 미들웨어 패턴은 여러 객체 간 다대 다의 통신을 하나의 관리 포인트를 통하도록 만들어 관계를 단순하게 만들어주는 패턴입니다.
- 그러나, 중재자에 권한이 집중되어 있기 때문에, 잘못된 중재자의 설계는 더 복잡한 객체를 생성할 수 있다는 단점이 있습니다.