강동현

리팩토링

1 import express from "express"; 1 import express from "express";
2 import socketIo, { Server as IoServer } from "socket.io"; 2 import socketIo, { Server as IoServer } from "socket.io";
3 import { createServer } from "http"; 3 import { createServer } from "http";
4 -import { SocketHandler } from "./SocketHandler";
5 import { RoomManager } from "./room/RoomManager"; 4 import { RoomManager } from "./room/RoomManager";
5 +import { Connection } from "./connection/Connection";
6 +import { SocketIoWrapper } from "./connection/SocketWrapper";
6 7
7 export class Server { 8 export class Server {
8 public readonly port: number; 9 public readonly port: number;
...@@ -15,7 +16,6 @@ export class Server { ...@@ -15,7 +16,6 @@ export class Server {
15 const server = createServer(app); 16 const server = createServer(app);
16 this.io = new socketIo.Server(server); 17 this.io = new socketIo.Server(server);
17 18
18 - const handler = new SocketHandler();
19 const roomManager = new RoomManager(); 19 const roomManager = new RoomManager();
20 20
21 roomManager.create("테스트 방 #1", 8); 21 roomManager.create("테스트 방 #1", 8);
...@@ -23,7 +23,7 @@ export class Server { ...@@ -23,7 +23,7 @@ export class Server {
23 roomManager.create("테스트 방 #3", 2); 23 roomManager.create("테스트 방 #3", 2);
24 24
25 this.io.on("connection", (socket) => { 25 this.io.on("connection", (socket) => {
26 - handler.connected(socket); 26 + new Connection(new SocketIoWrapper(socket), roomManager);
27 }); 27 });
28 28
29 server.listen(port, () => console.log(`Listening on ${port}`)); 29 server.listen(port, () => console.log(`Listening on ${port}`));
......
1 -import { Socket } from "socket.io";
2 -import { ConnectionMapper } from "./connection/ConnectionMapper";
3 -
4 -export class SocketHandler {
5 - private connectionMapper: ConnectionMapper;
6 -
7 - constructor() {
8 - this.connectionMapper = new ConnectionMapper();
9 - }
10 -
11 - public connected(socket: Socket) {
12 - const connection = this.connectionMapper.get(socket);
13 - }
14 -}
1 import { Socket } from "socket.io"; 1 import { Socket } from "socket.io";
2 -import { ServerOutboundMessage, ServerOutboundMessageKey } from "../../common"; 2 +import {
3 -import { MessageHandlerChain } from "../message/MessageHandlerChain"; 3 + RawMessage,
4 + ServerInboundMessage,
5 + ServerInboundMessageKey,
6 + ServerOutboundMessage,
7 + ServerOutboundMessageKey,
8 + ServerResponse,
9 +} from "../../common";
4 import { Room } from "../room/Room"; 10 import { Room } from "../room/Room";
11 +import { RoomManager } from "../room/RoomManager";
5 import { User } from "../user/User"; 12 import { User } from "../user/User";
13 +import { SocketWrapper } from "./SocketWrapper";
6 14
7 export class Connection { 15 export class Connection {
8 - public readonly socket: Socket; 16 + public readonly socket: SocketWrapper;
17 + public readonly roomManager: RoomManager;
9 18
10 public user?: User; 19 public user?: User;
11 20
12 - private messageHandlerChain: MessageHandlerChain; 21 + constructor(socket: SocketWrapper, roomManager: RoomManager) {
13 -
14 - constructor(socket: Socket) {
15 this.socket = socket; 22 this.socket = socket;
16 - this.messageHandlerChain = new MessageHandlerChain(this); 23 + this.roomManager = roomManager;
24 + socket.setHandler((raw) => this.handleRaw(raw));
17 } 25 }
18 26
19 public send<T extends ServerOutboundMessageKey>( 27 public send<T extends ServerOutboundMessageKey>(
20 type: T, 28 type: T,
21 message: ServerOutboundMessage<T> 29 message: ServerOutboundMessage<T>
22 ) { 30 ) {
23 - this.socket.emit("msg", { 31 + this.socket.send({
24 type: type as string, 32 type: type as string,
25 message: message, 33 message: message,
26 }); 34 });
27 } 35 }
36 +
37 + public handleRaw(raw: RawMessage): ServerResponse<any> {
38 + const type = raw.type as ServerInboundMessageKey;
39 + const message = raw.message;
40 +
41 + // 유저 정보가 없으므로 로그인은 따로 핸들링
42 + if (type === "login") {
43 + return this.handleLogin(message);
44 + }
45 +
46 + // Game > Room > User 순으로 전달
47 + if (this.user?.room) {
48 + const response = this.user.room.handler.handle(type, this.user, message);
49 + if (response) return response;
50 + }
51 + if (this.user) {
52 + const response = this.user.handler.handle(type, this.user, message);
53 + if (response) return response;
54 + }
55 + return { ok: false };
56 + }
57 +
58 + private handleLogin(
59 + message: ServerInboundMessage<"login">
60 + ): ServerResponse<"login"> {
61 + this.user = new User(message.username, this);
62 + console.log(`User ${message.username} has logged in!`);
63 +
64 + return { ok: true };
65 + }
28 } 66 }
......
1 -import { Socket } from "socket.io";
2 -import { Connection } from "./Connection";
3 -
4 -export class ConnectionMapper {
5 - private map: Map<Socket, Connection>;
6 -
7 - constructor() {
8 - this.map = new Map<Socket, Connection>();
9 - }
10 -
11 - public get(socket: Socket): Connection {
12 - var value = this.map.get(socket);
13 - if (value) {
14 - return value;
15 - }
16 -
17 - value = new Connection(socket);
18 - // FIXME: Register connection to the map
19 - return value;
20 - }
21 -
22 - public close(socket: Socket): void {
23 - this.map.delete(socket);
24 - }
25 -}
1 +import { Socket } from "socket.io";
2 +import { RawMessage, ServerResponse } from "../../common";
3 +
4 +export interface SocketWrapper {
5 + setHandler: (listener: (raw: RawMessage) => ServerResponse<any>) => void;
6 + send: (raw: RawMessage) => void;
7 +}
8 +
9 +export class SocketIoWrapper implements SocketWrapper {
10 + private socketIo: Socket;
11 +
12 + constructor(socketIo: Socket) {
13 + this.socketIo = socketIo;
14 + }
15 +
16 + public setHandler(listener: (raw: RawMessage) => ServerResponse<any>): void {
17 + this.socketIo.on("msg", (raw: RawMessage, callback: Function) => {
18 + callback(listener(raw));
19 + });
20 + }
21 +
22 + public send(raw: RawMessage) {
23 + this.socketIo.emit("msg", raw);
24 + }
25 +}
1 -import { Connection } from "../connection/Connection";
2 import { 1 import {
3 ServerInboundMessage, 2 ServerInboundMessage,
4 ServerInboundMessageKey, 3 ServerInboundMessageKey,
...@@ -23,13 +22,10 @@ export class MessageHandler { ...@@ -23,13 +22,10 @@ export class MessageHandler {
23 public handle( 22 public handle(
24 type: ServerInboundMessageKey, 23 type: ServerInboundMessageKey,
25 user: User, 24 user: User,
26 - message: any, 25 + message: any
27 - callback: Function 26 + ): ServerResponse<any> | undefined {
28 - ): boolean {
29 const handler = this.handlers[type]; 27 const handler = this.handlers[type];
30 - if (!handler) return false; 28 + if (!handler) return undefined;
31 - const response = handler(user, message); 29 + return handler(user, message);
32 - callback(response);
33 - return true;
34 } 30 }
35 } 31 }
......
1 -import { Connection } from "../connection/Connection";
2 -import {
3 - RawMessage,
4 - ServerInboundMessage,
5 - ServerInboundMessageKey,
6 - ServerResponse,
7 -} from "../../common/index";
8 -import { User } from "../user/User";
9 -
10 -export class MessageHandlerChain {
11 - connection: Connection;
12 -
13 - constructor(connection: Connection) {
14 - this.connection = connection;
15 -
16 - this.connection.socket.on("msg", (raw: RawMessage, callback: Function) => {
17 - this.handleRaw(connection, raw, callback);
18 - });
19 - }
20 -
21 - private handleRaw(
22 - connection: Connection,
23 - raw: RawMessage,
24 - callback: Function
25 - ) {
26 - const type = raw.type as ServerInboundMessageKey;
27 - const message = raw.message;
28 -
29 - // 유저 정보가 없으므로 로그인은 따로 핸들링
30 - if (type === "login") {
31 - this.handleLogin(connection, message, callback);
32 - return;
33 - }
34 -
35 - // Game > Room > User 순으로 전달
36 - if (
37 - connection?.user?.room &&
38 - connection.user.room.handler.handle(
39 - type,
40 - connection.user,
41 - message,
42 - callback
43 - )
44 - )
45 - return;
46 -
47 - if (
48 - connection?.user &&
49 - connection.user.handler.handle(type, connection.user, message, callback)
50 - )
51 - return;
52 - }
53 -
54 - private handleLogin(
55 - connection: Connection,
56 - message: ServerInboundMessage<"login">,
57 - callback: Function
58 - ) {
59 - connection.user = new User(message.username, connection);
60 - console.log(`User ${message.username} has logged in!`);
61 -
62 - callback({ ok: true });
63 - }
64 -}
1 import { Connection } from "../connection/Connection"; 1 import { Connection } from "../connection/Connection";
2 import { v4 as uuidv4 } from "uuid"; 2 import { v4 as uuidv4 } from "uuid";
3 import { User } from "../user/User"; 3 import { User } from "../user/User";
4 -import { MessageHandlerChain } from "../message/MessageHandlerChain";
5 import { MessageHandler } from "../message/MessageHandler"; 4 import { MessageHandler } from "../message/MessageHandler";
6 import { 5 import {
7 ServerInboundMessage, 6 ServerInboundMessage,
......
...@@ -2,19 +2,12 @@ import { RoomDescription } from "../../common/dataType"; ...@@ -2,19 +2,12 @@ import { RoomDescription } from "../../common/dataType";
2 import { Room } from "./Room"; 2 import { Room } from "./Room";
3 3
4 export class RoomManager { 4 export class RoomManager {
5 - private static _instance: RoomManager;
6 -
7 private rooms: Map<string, Room>; 5 private rooms: Map<string, Room>;
8 6
9 constructor() { 7 constructor() {
10 - RoomManager._instance = this;
11 this.rooms = new Map<string, Room>(); 8 this.rooms = new Map<string, Room>();
12 } 9 }
13 10
14 - public static instance(): RoomManager {
15 - return RoomManager._instance;
16 - }
17 -
18 public create(name: string, maxConnections: number): Room { 11 public create(name: string, maxConnections: number): Room {
19 const room = new Room(name, maxConnections); 12 const room = new Room(name, maxConnections);
20 this.rooms.set(room.uuid, room); 13 this.rooms.set(room.uuid, room);
......
...@@ -18,10 +18,10 @@ export class User { ...@@ -18,10 +18,10 @@ export class User {
18 this.connection = connection; 18 this.connection = connection;
19 this.handler = new MessageHandler({ 19 this.handler = new MessageHandler({
20 roomList: (user, message) => { 20 roomList: (user, message) => {
21 - return { ok: true, result: RoomManager.instance().list() }; 21 + return { ok: true, result: connection.roomManager.list() };
22 }, 22 },
23 joinRoom: (user, message) => { 23 joinRoom: (user, message) => {
24 - const room = RoomManager.instance().get(message.uuid); 24 + const room = connection.roomManager.get(message.uuid);
25 if (user.room || !room) { 25 if (user.room || !room) {
26 return { ok: false }; 26 return { ok: false };
27 } 27 }
......