const io = new Server(httpServer, {});
io.on('connection', (socket) => {
socket.on('disconnect', () => {
//!!! 소켓에 관련된 로직 !!!
});
...
socket.on('domainA', () => {
//!!! '도메인A'에 관련된 로직 !!!
});
socket.on('domainB', () => {
//!!! '도메인B'에 관련된 로직 !!!
});
...
}
우선, io 객체를 관리하는 App class를 선언하여 서버가 동작하는 파일과 구조를 담당하는 파일을 분리하려고 시도하였다.
import { ControllerInterface } from './controller/controller.interface';
import * as socketIo from 'socket.io';
import http from 'http';
export default class App {
httpServer: http.Server;
io: socketIo.Server;
constructor(controllers: ControllerInterface[]) {
this.httpServer = http.createServer();
this.io = new socketIo.Server(this.httpServer, {});
this.io.on('connection', (socket) => {
controllers.forEach((controller) => {
controller.register(socket);
});
});
}
listen(port: number, callback?: () => void) {
this.httpServer.listen(port).then(callback || () => {});
}
}
그 후, Controller의 기본적인 형태를 결정하였다.
export interface ControllerInterface {
register(socket: Socket): void;
}
register()
함수를 각각 호출하는 형태로
이벤트를 등록하는 부분과 실제 비즈니스 로직이 작성되는 부분의 두 관심사를 분리하고자 하였다.다음은 실제로 구현된 Controller의 예시이다
class KeywordController implements ControllerInterface {
constructor(
private readonly keywordService: KeywordServiceInterface,
) {}
onSelectKeyword(socket: Socket): (input: inputDTO, callback?: (error: any) => void) => void {
return async (
input: inputDTO,
errorCallback?: (error: any) => void
) => {
try {
// 비즈니스 로직
} catch (error) {
errorCallback && errorCallback(createFailedResponseTemplate(error));
}
};
}
register(socket: Socket) {
socket.on(EventEnum.select_keyword, this.onSelectKeyword(socket));
}
}
export { KeywordController };
export default new KeywordController(keywordService, KeywordUserService, CommunityUserService, CommunityService);
class KeywordController implements ControllerInterface {
constructor(
private readonly keywordService: KeywordServiceInterface,
) {}
// 기존의 이벤트
onSelectKeyword(socket: Socket): (input: inputDTO, callback?: (error: any) => void) => void {
return async (
input: inputDTO,
errorCallback?: (error: any) => void
) => {
try {
// 비즈니스 로직
} catch (error) {
errorCallback && errorCallback(createFailedResponseTemplate(error));
}
};
}
//!!! 추가된 이벤트 !!!//
onNewEvent(socket: Socket): (input: newInputDTO, callback?: (error: any) => void) => void {
return async (
input: newInputDTO,
errorCallback?: (error: any) => void
) => {
// 새로운 비즈니스 로직
}
}
register(socket: Socket) {
socket.on(EventEnum.select_keyword, this.onSelectKeyword(socket));
//!!! 새로운 이벤트 추가 !!!
socket.on('new event', this.onNewEvent(socket));
}
}
...
마지막으로, Controller를 일괄적으로 App 객체를 생성할 때 등록하고 실행하면 모든 이벤트에 대해서 적용이 되도록 한다.
// src/controller/index.ts
export default [KeywordController, CommunityController];
import App from './app';
import Controllers from './controller/index.ts';
new App(Controllers).listen(3000);