J238-C, J204-C

์šฐ๋ฆฌ์˜ ์ฒดํฌํฌ์ธํŠธ

  • ์„œ๋ฒ„

  • checkin

  • clap

  • checkout

  • summary

  • direct

  • ์ฑ„ํŒ…(chat, finish, broadcast)

  • ํด๋ผ์ด์–ธํŠธ

  • !history

  • checkin

  • clap

  • checkout

  • summary

  • direct

  • ์ฑ„ํŒ…(chat, finish, broadcast)

๋ฌธ์ œ ํ•ด๊ฒฐ ๊ณผ์ •

Telnet ์—ฐ๊ฒฐ

์ฑŒ๋ฆฐ์ง€ GPT ๋งŒ๋“ค๊ธฐ

ํ•ต์‹ฌ ํฌ์ธํŠธ - ์š”์ฒญ ์š”๊ตฌ์‚ฌํ•ญ

  • ์„œ๋ฒ„๊ฐ€ ๋ฐ›์€ ๋ชจ๋“  ์š”์ฒญ์—๋Š” ์ ์ ˆํ•œ ์‘๋‹ต์„ ๋ณด๋‚ด์ค€๋‹ค. (์•„๋ฌด๋Ÿฐ ๋ฐ˜์‘์ด ์—†์œผ๋ฉด ์•ˆ๋œ๋‹ค.)

  • ์„œ๋ฒ„๋Š” ์—ฌ๋Ÿฌ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋™์‹œ์— ์—ฐ๊ฒฐํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.

  • checkin ์š”์ฒญ์„ ๋ฐ›์€ ํ›„์—๋„ ์—ฐ๊ฒฐ์„ ์œ ์ง€ํ•ด์•ผ ํ•œ๋‹ค.

ํ•ต์‹ฌ ํฌ์ธํŠธ - ํด๋ผ์ด์–ธํŠธ

  • ๋น„๋™๊ธฐ ์ž…๋ ฅํ™”๋ฉด

  • checkin ์„ฑ๊ณตํ•œ ์‹œ๊ฐ์„ ๋ณ€์ˆ˜์— ๊ธฐ๋ก

  • chekoutํ•  ๋•Œ checkout์‹œ๊ฐ - chekin ์‹œ๊ฐ โ†’ Core Time

  • ์‹คํ–‰ ํ›„ ์ž…๋ ฅํ•œ ๋ช…๋ น์„ ๋ชจ๋‘ ๊ธฐ๋ก(์„œ๋ฒ„x ํด๋ผ์ด์–ธํŠธ ์ž์ฒด์—์„œ)

  • ์š”์ฒญ, ์‘๋‹ต, ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ๋ฅผ ๋ชจ๋‘ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ฐ›์•„ ์ถœ๋ ฅ

์„œ๋ฒ„

 
const net = require("net");
 
const { handleCommand } = require("./handleCommand");
 
  
 
const server = net.createServer((socket) => {
 
console.log("Client Connected");
 
const userSessionId = crypto.randomUUID();
 
  
 
socket.on("data", (data) => {
 
console.log(`${userSessionId} Received from client: ${data}`);
 
data = JSON.parse(data);
 
const response = handleCommand(userSessionId, data, socket);
 
socket.write(response);
 
});
 
  
 
socket.on("end", () => {
 
console.log("Client disconnected");
 
});
 
});
 
  
 
const port = 2024;
 
server.listen(port, () => {
 
console.log(`Server listening on port ${port}`);
 
});
 

์„œ๋ฒ„๋Š” net.Socket์„ ํ™œ์šฉํ•˜์—ฌ 2024ํฌํŠธ์— ๋Œ€ํ•ด์„œ ์„œ๋ฒ„๋ฅผ ํ‚ค๊ณ , JSON ํ˜•์‹์˜ ์ปค๋งจ๋“œ, ๋ฐ์ดํ„ฐ๋กœ ์ด๋ฃจ์–ด์ง„ ๊ฐ์ฒด๋ฅผ ๋ฐ›์•„ ์ด์— ๋Œ€ํ•ด์„œ ๊ฐ๊ฐ ์ฒ˜๋ฆฌํ•œ๋‹ค. ์ด๋Ÿฌํ•œ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ๋ฐฉ์‹์€ ๊ธฐ์กด ํ”„๋ก ํŠธ์™€ ๋ฐฑ์˜ ๋ฐ์ดํ„ฐ ํ†ต์‹ ์— ๋Œ€ํ•œ ๋ฐฉ์‹๊ณผ ๊ฑฐ์˜ ํก์‚ฌํ•˜๋‹ค.

ํด๋ผ์ด์–ธํŠธ

 
function checkin(campId, host, port) {
 
let client = new net.Socket();
 
client.connect(port, host, () => {});
 
  
 
client.on("data", (data) => {
 
ResponseHandler.handle(data);
 
});
 
  
 
client.on("close", () => {
 
process.exit();
 
});
 
client.write(RequestJson.makeCheckinRequest(campId));
 
historyList.push("checkin");
 
return client;
 
}
 

ํด๋ผ์ด์–ธํŠธ๋Š” net.Socket์„ ์ด์šฉํ•˜์—ฌ ์ „๋‹ฌ๋œ host์™€ port์— ๋Œ€ํ•ด ์—ฐ๊ฒฐ

์ดํ›„ write๋กœ ์„œ๋ฒ„์— ์š”์ฒญ์ด ๊ฐ„ํ›„ ์‘๋‹ต์ด ๋Œ์•„์˜ค๊ฒŒ ๋ ๊ฒฝ์šฐ ResponseHandler๋ฅผ ์ด์šฉํ•˜์—ฌ ์‘๋‹ต ๊ฒฐ๊ณผ ์ถœ๋ ฅ

ํ•ต์‹ฌ ํฌ์ธํŠธ - checkin


// ํด๋ผ์ด์–ธํŠธ

> checkin J004

< checkin success to group#1

  

// ์„œ๋ฒ„

>> checkin J004 (success) from 127.0.0.1:12334 => session#1, group#1

 
const { Repository } = require("./repository");
 
const User = require("./user");
 
  
 
function checkin(userSessionId, data, socket) {
 
const campId = data.data;
 
const repository = new Repository();
 
numberValidCheck(campId);
 
const user = repository.addUser(campId, userSessionId, socket);
 
const groupNum = repository.joinGroup(user);
 
console.log(
 
`checkin ${user.campId} (Success) from ${socket.remoteAddress} => session#${user.indexId}, group#${user.group.groupNum}`
 
);
 
return JSON.stringify({ command: data.command, data: groupNum });
 
}
 
  
 
const numberValidCheck = (campId) => {
 
const idNumber = parseInt(campId.slice(1));
 
if (typeof idNumber !== "number" || idNumber > 256 || idNumber < 1) {
 
throw new Error("Invalid Id number");
 
}
 
};
 
  
 
module.exports = { checkin };
 
  • campId (J001~J256)

  • checkin โ†’ ๊ทธ๋ฃน ํ• ๋‹น

  • 4๋ช…๊นŒ์ง€ ๊ทธ๋ฃน ํ• ๋‹น

  • ๊ทธ๋ฃน์—์„œ ๋น ์ ธ๋‚˜๊ฐ„ ๊ฒฝ์šฐ ์žฌํ• ๋‹น ๊ฐ€๋Šฅ(ํ• ๋‹นํ•˜๋Š” ๋ฐฉ์‹์— ๋Œ€ํ•ด์„  ์˜๋…ผ)

  • checkin ์‘๋‹ต โ†’ ๊ทธ๋ฃน ๋ฒˆํ˜ธ๋ฅผ ์ •์ˆ˜ํ˜•์œผ๋กœ ์•Œ๋ ค์คŒ

์ฒดํฌ์ธ์˜ ๊ฒฝ์šฐ ๊ฐ ์œ ์ €๋งˆ๋‹ค ์˜ค๋Š” ๋‹ฌ๋ผ์ง€๋Š” UUID ๊ฐ’์— ๋Œ€ํ•ด Database ๊ฐ์ฒด์— Repository๋ผ๋Š” ์ค‘๊ฐ„ ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ๊ฐฑ์‹ ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ–ˆ๋‹ค.

๋˜ํ•œ ๊ทธ๋ฃน๋„ ๊ฐ์ฒด๋กœ ๋งŒ๋“ค์–ด์ฃผ์–ด ๊ฐ ๊ฐ ๊ทธ๋ฃน์—์„œ ๋‚จ์€ ๊ณต๊ฐ„์„ ์ฐพ์•„ ํ•ด๋‹นํ•˜๋Š” ์บ ํผ๋ฅผ ๋„ฃ์–ด์ฃผ๋„๋ก ํ–ˆ๊ณ , ์ด ๋˜ํ•œ repository์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด ๊ฐฑ์‹ ํ•œ๋‹ค. ๋งˆ์ง€๋ง‰์—๋Š” JSON๊ฐ’์„ ๋ฆฌํ„ดํ•ด์ฃผ์–ด ํด๋ผ์ด์–ธํŠธ์—์„œ๋„ ์ฒ˜๋ฆฌ๊ฐ€ ์šฉ์ดํ•˜๋„๋ก ๊ตฌํ˜„ํ–ˆ๋‹ค.

์˜ˆ์™ธ

  • campId ๋ฒ”์œ„ ์ดˆ๊ณผ ์—๋Ÿฌ

  • ์žฌ์ž…๋ ฅ

  • checkin ์ƒํƒœ์—์„œ ๋‹ค์‹œ checkin ๋ถˆ๊ฐ€๋Šฅ

ํ•ต์‹ฌ ํฌ์ธํŠธ - checkout


// ํด๋ผ์ด์–ธํŠธ

> checkout

< checkout (disconnected)

> Core time = 11min 32sec

  

// ์„œ๋ฒ„

>> checkout from session#2(J005) - disconnected

 
const { Repository } = require("./repository");
 
  
 
function checkout(user, userSessionId) {
 
const repository = new Repository();
 
console.log(
 
`checkout from session#${user.indexId}(${user.campId}) - disconnected`
 
);
 
repository.deleteUser(userSessionId);
 
return JSON.stringify({ command: "checkout", data: true });
 
}
 
module.exports = { checkout };
 
  • ์ด์ „์— checkin ํ–ˆ๋˜ ๊ทธ๋ฃน์—์„œ ํ‡ด์žฅ

  • ๊ทธ๋ฃน์— ๋‹ค๋ฅธ ์บ ํผ๊ฐ€ ๋‚จ์•„์žˆ๋‹ค๋ฉด, ํ•ด๋‹น ๊ทธ๋ฃน ์บ ํผ๋“ค์—๊ฒŒ ํ‡ด์žฅ ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ

  • chekout ์š”์ฒญ ํ›„ ์‘๋‹ต์„ ๋ฐ›์œผ๋ฉด ์—ฐ๊ฒฐ์„ ๋Š๊ธฐ

  • chekout ์—†์ด TCP ์—ฐ๊ฒฐ์ด ๋Š๊ฒจ๋„ checkout ์ฒ˜๋ฆฌ

์ฒดํฌ์•„์›ƒ์—์„œ ์‹ ๊ฒฝ์จ์•ผ ํ•  ์ ์€ ๊ธฐ์กด์˜ ์ ‘์† ์ค‘์ธ ์บ ํผ๋ฅผ ๋ณด๊ด€ํ•˜๋Š” database์˜ user๊ฐ์ฒด์— ๋Œ€ํ•ด ์‚ญ์ œ๋ฅผ ํ•จ๊ณผ ๋™์‹œ์— ๊ทธ๋ฃน์—์„œ ๋˜ํ•œ ์ œ๊ฑฐํ•ด์•ผ ํ•œ๋‹ค๋Š” ์ ์ด์—ˆ๋‹ค. ์ด๋ฅผ ์œ ์˜ํ•˜์—ฌ repository๋ฅผ ํ†ตํ•ด ์ •๋ณด๋ฅผ ๊ฐฑ์‹ ํ•˜๊ณ , error๊ฐ€ ๋˜์ ธ์ง€์ง€ ์•Š๋Š” ์ด์ƒ true๊ฐ’๋งŒ ๋ฐ์ดํ„ฐ๋กœ ๋ฆฌํ„ดํ•˜์—ฌ ์„ฑ๊ณต ์—ฌ๋ถ€๋ฅผ ์•Œ๋ฆฌ๋„๋ก ํ–ˆ๋‹ค.

ํ•ต์‹ฌ ํฌ์ธํŠธ - Summary


// ํด๋ผ์ด์–ธํŠธ

> summary day4

< keywords are "Heap, Stack"

  

// ์„œ๋ฒ„

>> summary from session#2(J005) : day19 => 'Network, Server'

 
function summary(user, data) {
 
if (!day.hasOwnProperty(data.data) || !data.data)
 
throw new Error("404 Day Not Found");
 
console.log(`summary from session#${user.indexId}(${user.campId})`);
 
return JSON.stringify({ command: "summary", data: `\"${day[data.data]}\"` });
 
}
 
  
 
const day = Object.freeze({
 
day1: "IDE,node",
 
day2: "Linux,System",
 
day3: "XML,JSON",
 
day4: "Heap,Stack",
 
day6: "Object,Class",
 
day7: "File Path, UnitTest",
 
day8: "Immutable, Closure",
 
day9: "Event, Publisher",
 
day11: "Async,EventLoop",
 
day13: "Git, Object",
 
day16: "HTTP, SQL",
 
day18: "Network, Server",
 
});
 
  
 
module.exports = { summary };
 
  • summary ์š”์ฒญ์„ ๋ณด๋‚ด์„œ ํ‚ค์›Œ๋“œ ๋ฐ›๊ธฐ

  • ๋ฐ์ดํ„ฐ โ†’ day ๋ช‡ ๋ฒˆ์งธ ๋ฏธ์…˜์ธ์ง€ ์ •์ˆ˜ํ˜•์œผ๋กœ ๋ฐ›๊ธฐ

  • ์„œ๋ฒ„ โ†’ ์ •์ˆ˜ํ˜•์„ ํ™•์ธํ•ด์„œ ์‘๋‹ต

summary๊ฐ™์€ ๊ฒฝ์šฐ ์ด๋ฏธ ์ฃผ์–ด์ง€๋Š” ๊ฐ’๋“ค์ด ๊ณ ์ •๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ, ์ด๋ฅผ ์ƒ์ˆ˜์ฒ˜๋ฆฌ ํ•ด์ฃผ๊ณ  ๊ฐ€์ ธ๋‹ค๊ฐ€ ์“ธ ์ˆ˜ ์žˆ๊ฒŒ๋” ํ•ด ์ฃผ์—ˆ๋‹ค. ํ•ด๋‹นํ•˜์ง€ ์•Š์€ ๋Œ€์ƒ์ด ๋‚˜์˜ค๊ฒŒ ๋œ๋‹ค๋ฉด ์—๋Ÿฌ๋ฅผ ๋˜์ ธ์ฃผ์—ˆ๋‹ค.

ํ•ต์‹ฌ ํฌ์ธํŠธ - chat(์ฑ„ํŒ…๋ฐฉ ๋งŒ๋“ค๊ธฐ)


// ํด๋ผ์ด์–ธํŠธ

> chat maxCount=2

< broadcast from server, "์ฑ„ํŒ…์ด ์‹œ์ž‘๋˜์—ˆ์Šต๋‹ˆ๋‹ค"

//ํด๋ผ์ด์–ธํŠธ - ๋‹ค๋ฅธ ํด๋ผ์ด์–ธํŠธ์—๊ฒŒ chat์„ ํ†ตํ•ด ์ฑ„ํŒ…๋ฐฉ์„ ๋งŒ๋“ค๊ณ  ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ ๋ฐ›์•˜์„ ๋•Œ

< broadcast from J005, "๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹ค"

  

// ์„œ๋ฒ„

>> chat from session#1(J004)

 
function chat(user) {
 
if (!user.group) throw new Error("404 Group not found");
 
user.group.chatStart();
 
return JSON.stringify({ command: "chat", data: true });
 
}
 
module.exports = { chat };
 
  • ๊ฐ™์€ ๊ทธ๋ฃน์— ์žˆ๋Š” ์‚ฌ๋žŒ๋“ค๊ณผ ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ

  • ์š”์ฒญ ๋ฐ์ดํ„ฐ โ†’ maxCount(int)

  • ๋ฉ”์‹œ์ง€ ๊ฐœ์ˆ˜ < maxCount

์˜ˆ์™ธ

  • ๋ฉ”์‹œ์ง€ ๊ฐœ์ˆ˜๊ฐ€ MaxCount๋ฅผ ๋„˜์œผ๋ฉด ์„œ๋กœ์—๊ฒŒ ์ „๋‹ฌ๋˜์ง€ ์•Š์Œ

ํ•ต์‹ฌ ํฌ์ธํŠธ - finish


//ํด๋ผ์ด์–ธํŠธ -> ์•Œ์•„์„œ ๋„ฃ๊ธฐ

//์„œ๋ฒ„

>> finish from session#1(J004)

 
function finish() {
 
if (!user.group) throw new Error("404 Group not found");
 
user.group.chatFinish();
 
return JSON.stringify({ command: "finish", data: true });
 
}
 
  
 
module.exports = { finish };
 
  • ์ฑ„ํŒ… ์š”์ฒญ์„ ๋ณด๋‚ธ ์บ ํผ๊ฐ€ finish๋ฅผ ๋ณด๋‚ด๋ฉด ์ฑ„ํŒ… ๋ฉˆ์ถ”๊ธฐ

  • ๋‹ค์‹œ ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ ๋ถˆ๊ฐ€๋Šฅ

ํ•ต์‹ฌ ํฌ์ธํŠธ - broadcast(์ฑ„ํŒ… ๋ฉ”์‹œ์ง€ ๋ณด๋‚ด๊ธฐ)

๋ณด๋‚ผ ๋•Œ


//ํด๋ผ์ด์–ธํŠธ

> broadcast "๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹ค"

//์„œ๋ฒ„

>> broadcast from session#2(J005) => "๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹ค"

 
class Database {
 
_instance = null;
 
constructor() {
 
if (this._instance === null) {
 
this._instance = this;
 
this.user = {};
 
this.groups = [];
 
this.clap = 0;
 
this.currsessionId = 0;
 
}
 
return this._instance;
 
}
 
  
 
getNewSessionId() {
 
this.currsessionId++;
 
return this.currsessionId;
 
}
 
}
 
  
 
const getInstance = () => {
 
if (this.database == undefined) {
 
this.database = new Database();
 
}
 
return this.database;
 
};
 
  
 
module.exports = { getInstance };
 

๋ฐ›์„ ๋•Œ


//ํด๋ผ์ด์–ธํŠธ

< broadcast from J004, "์˜ค๋Š˜ํž˜๋“œ๋„ค์š”"

//์„œ๋ฒ„

>> broadcast to group#1 => text="๋ฐ˜๊ฐ‘์Šต๋‹ˆ๋‹ค", from="J005"

  • ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ ์š”์ฒญ์„ ๋ณด๋‚ด๋ฉด chat ์ง„ํ–‰์ค‘์ธ ๊ทธ๋ฃน ๋ชจ๋‘์—๊ฒŒ ๋ธŒ๋กœ๋“œ์บ์ŠคํŠธ

  • ์š”์ฒญ ๋ฐ์ดํ„ฐ โ†’ text(string)

ํ•ต์‹ฌ ํฌ์ธํŠธ - direct

๋ณด๋‚ผ ๋•Œ


//ํด๋ผ์ด์–ธํŠธ

> direct to J004 "๋งˆ์ง€๋ง‰์ด๋‹ˆ ํž˜๋‚ด์š”"

< direct (success)

//์„œ๋ฒ„

>> direct from session#2(J005) => to="J004", text="๋งˆ์ง€๋ง‰์ด๋‹ˆ ํž˜๋‚ด์š”"

๋ฐ›์„ ๋•Œ


//ํด๋ผ์ด์–ธํŠธ

< direct from J005, "๋งˆ์ง€๋ง‰์ด๋‹ˆ ํž˜๋‚ด์š”"

//์„œ๋ฒ„

broadcast to session#1(J004) => text="๋งˆ์ง€๋ง‰์ด๋‹ˆ ํž˜๋‚ด์š”"

  • ์ง์ ‘ ํŠน์ •ํ•œ ์บ ํผ์—๊ฒŒ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ

  • ์š”์ฒญ ๋ฐ์ดํ„ฐ โ†’ campId(string), text(string)

์˜ˆ์™ธ

  • ์ˆ˜์‹ ํ•  ๋Œ€์ƒ ์บ ํผ๊ฐ€ ์ฒดํฌ์ธ ์•ˆํ–ˆ์œผ๋ฉด ๋ณด๋‚ด์ง€ ์•Š๊ธฐ

  • ์ฒดํฌ์ธ์„ ํ•œ ์ƒํƒœ๋ผ๋ฉด text ๋ฉ”์‹œ์ง€ ์ „๋‹ฌ

ํ•ต์‹ฌ ํฌ์ธํŠธ - clap


//ํด๋ผ์ด์–ธํŠธ

> clap

< clap count is 1

  

//์„œ๋ฒ„

>> clap from session#1(J004) => 1

 
const { Repository } = require("./repository");
 
  
 
function clap() {
 
const repository = new Repository();
 
repository.addClap();
 
console.log("clap!");
 
return JSON.stringify({ command: "clap", data: repository.getClap() });
 
}
 
module.exports = { clap };
 
  • ๋ชจ๋“  ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋ณด๋‚ด๋Š” ์š”์ฒญ ํšŸ์ˆ˜๋ฅผ ๋ˆ„์ ํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ๋Šฅ

  • ์š”์ฒญํ•  ๋•Œ๋งˆ๋‹ค ํ•˜๋‚˜์”ฉ ๊ฐ’์„ ๋ˆ„์ ํ•ด์„œ ์ˆซ์ž ์‘๋‹ต

  • ํ•œ ํด๋ผ์ด์–ธํŠธ์—์„œ ๋ฐ˜๋ณตํ•ด์„œ ์š”์ฒญ ๊ฐ€๋Šฅ

ํ•™์Šต ๋ฉ”๋ชจ


๊ฐœ์„ ํ•˜๊ธฐ ์ฒดํฌํฌ์ธํŠธ

  • ๋ชจ๋“  ๊ธฐ๋Šฅ ์™„์„ฑ โœ… 2024-08-08
  • ํด๋ผ์ด์–ธํŠธ์˜ readline ๊ตฌ์กฐ ๊ฐœ์„  โœ… 2024-08-08
  • http Request && response ๊ตฌ์กฐ๋กœ ๋ฐ์ดํ„ฐ ๋ณด๋‚ด๊ธฐ โœ… 2024-08-08
  • Error ํ•ธ๋“ค๋ง โ†’ ํด๋ผ์ด์–ธํŠธ์— ์—๋Ÿฌ ์ „์†ก ๊ฐœ์„  โœ… 2024-08-08

ํด๋ผ์ด์–ธํŠธ์˜ readline ๊ตฌ์กฐ ๊ฐœ์„ 

const RequestHandler = require("./requestHandler");
const readline = require("readline");
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});
 
let client = null;
const port = 2024;
const host = "localhost";
let startTime = 0;
rl.on("line", (line) => {
  const order = line.trim().split(" ");
  if (order[0] == "!history") {
    RequestHandler.history();
  } else if (!client) {
    if (order[0] == "checkin") {
      startTime = Date.now();
      client = RequestHandler.checkin(order[1], host, port);
    } else {
      console.log("์ž˜๋ชป๋œ ๋ช…๋ น์ž…๋‹ˆ๋‹ค");
    }
  } else {
    if (order[0] == "clap") {
      RequestHandler.clap(client);
    } else if (order[0] == "checkout") {
      RequestHandler.checkout(client);
      printTime(startTime, Date.now());
    } else if (order[0] == "summary") {
      RequestHandler.summary(client, order[1]);
    } else if (order[0] == "direct") {
      //RequestHandler.direct(client);
    } else if (order[0] == "chat") {
    } else if (order[0] == "finish") {
    } else if (order[0] == "broadcast") {
    } else {
      console.log("์ž˜๋ชป๋œ ๋ช…๋ น์ž…๋‹ˆ๋‹ค");
    }
  }
});
 
function printTime(startTime, endTime) {
  let time = parseInt((endTime - startTime) / 1000);
  console.log(`Core time = ${parseInt(time / 60)}min ${time % 60}sec`);
}
 

๊ธฐ์กด ์ฝ”๋“œ์˜ ๊ฒฝ์šฐ readline ๋‚ด์—์„œ ์–ด๋–ป๊ฒŒ ์ฒ˜๋ฆฌํ•  ์ง€์— ๋Œ€ํ•ด ๋ณด๋‹ค ๊ตฌ์กฐ๋ฅผ ์ž˜ ํŒŒ์•…ํ•˜๊ธฐ ์œ„ํ•ด readline ๋‚ด์—์„œ ๋ช…๋ น์–ด๋ฅผ ์ฒ˜๋ฆฌํ•˜๋‹ค๋ณด๋‹ˆ ์•„๋ฌด๋ž˜๋„ if else ๋ฌธ์„ ๋ณด๋‹ค ๋‚จ๋ฐœํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์—ˆ๋‹ค. ๋˜ํ•œ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ์— ๋Œ€ํ•ด์„œ๋„ ๋”ฐ๋กœ ์„œ๋ฒ„์˜ ๋ช…๋ น์–ด๋ฅผ ์ฒ˜๋ฆฌํ•˜๋ฉด ์•ˆ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์ด๋ฅผ ๋ง‰๋‹ค๋ณด๋‹ˆ ๋‹ค์–‘ํ•œ ์˜ˆ์™ธ ์ƒํ™ฉ์— ๋Œ€ํ•ด ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด if else๋ฌธ์„ ์ผ์ง€๋งŒ ์ด๋ฅผ switch/case๋ฌธ์„ ํ†ตํ•ด ๋ณด๋‹ค ๊ตฌ์กฐ์ ์œผ๋กœ ์ •๋ฆฌํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์•˜๋””.

const RequestHandler = require("./requestHandler");
 
//ํด๋ผ์ด์–ธํŠธ ๊ฐ์ฒด
let client;
//์ ‘์† ์‹œ๊ฐ„์„ ๊ตฌํ•˜๊ธฐ ์œ„ํ•œ startTime
let startTime;
const PORT = 2024;
const HOST = "localhost";
 
function commandHandler(line) {
  const [command, ...data] = line.trim().split(" ");
  console.log(command, data);
  if (!client && command !== "checkin") {
    console.log("์ฒดํฌ์ธ์„ ์ง„ํ–‰ํ•ด์•ผ ๋ช…๋ น์–ด๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.");
  } else {
    switch (command) {
      case "!history":
        RequestHandler.history();
        break;
      case "checkin":
        startTime = Date.now();
        client = RequestHandler.checkin(data[0], HOST, PORT);
        break;
      case "checkout":
        RequestHandler.checkout(client);
        printTime(startTime, Date.now());
        break;
      case "summary":
        RequestHandler.summary(client, data[0]);
        break;
      case "clap":
        RequestHandler.clap(client);
        break;
      case "direct":
        RequestHandler.direct(client, data[0], data[1]);
        break;
      case "broadcast":
        RequestHandler.broadcast(client, data[0]);
        break;
      case "finish":
        RequestHandler.finish(client);
        break;
      default:
        console.log("์ž˜๋ชป๋œ ๋ช…๋ น์–ด์ž…๋‹ˆ๋‹ค.");
        break;
    }
  }
}
 
function printTime(startTime, endTime) {
  let time = parseInt((endTime - startTime) / 1000);
  console.log(`Core time = ${parseInt(time / 60)}min ${time % 60}sec`);
}
 
module.exports = { commandHandler };

๋”ฐ๋ผ์„œ client์˜ commandHandler๋ฅผ ๋”ฐ๋กœ ๋งŒ๋“ค๊ณ , ์ด์— ๋Œ€ํ•ด์„œ readline์˜ ํด๋กœ์ €๋กœ ๋„˜๊ฒจ์ฃผ์—ˆ๋‹ค. ์ด ํด๋กœ์ €์—์„œ๋Š” ๊ฐ ํด๋ผ์ด์–ธํŠธ์— ๋Œ€ํ•ด ๊ฐ์ฒด๋ฅผ ๊ด€๋ฆฌํ•˜๊ณ , ๋ช…๋ น์–ด์— ๋Œ€ํ•ด ์ฒ˜๋ฆฌํ•˜๋Š” ๊ธฐ์กด์— ์žˆ๋˜ ์ฝœ๋ฐฑํ•จ์ˆ˜๋ฅผ ๋”ฐ๋กœ ๋ชจ๋“ˆํ™”์‹œ์ผœ๋†“๊ณ  ๊ธฐ์กด์˜ ๋ฌธ๋ฒ•์„ ์‚ด์ง ๋ณ€ํ˜•ํ•œ ์ •๋„์ด๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๋‚˜์ค‘์— ๋ณด๋‹ค ๊ตฌ์กฐ์ ์œผ๋กœ ํ™•์ธ์ด ํŽธํ•˜๊ณ , ๋ช…๋ น์–ด ์ถ”๊ฐ€๊ฐ€ ๋ณด๋‹ค ์šฉ์ดํ•˜๋‹ค.

const { commandHandler } = require("./commandHandler");
const readline = require("readline");
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});
 
rl.on("line", commandHandler);
 

๋˜ํ•œ ๊ธฐ์กด์— ์žˆ๋˜ client์˜ readline๋ถ€๋ถ„์ด ๋งค์šฐ ๊ฐ„์†Œํ™”๋˜์–ด ๋ช…๋ น์–ด์™€ ๋ฐ์ดํ„ฐ์˜ ํ๋ฆ„ ๋ณ„๋กœ ๋ณด๋‹ค ํŒŒ์•…์ด ์šฉ์ดํ•˜๊ฒŒ ๊ฐœ์„ ๋˜์—ˆ๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค.

HTTP request & response ๊ตฌ์กฐ ์ง€ํ‚ค๊ธฐ

function makeCheckinRequest(campId) {
  return JSON.stringify({
    command: "checkin",
    data: campId,
  });
}
 
function makeClapRequest() {
  return JSON.stringify({
    command: "clap",
  });
}
 
function makeCheckoutRequest() {
  return JSON.stringify({
    command: "checkout",
  });
}
 
function makeSummaryRequest(day) {
  return JSON.stringify({
    command: "summary",
    data: day,
  });
}
 
function makeChatRequest(cnt) {
  return JSON.stringify({
    command: "chat",
    data: cnt,
  });
}

์ด์ „์—๋Š” ๊ทธ๋ƒฅ jsonํ˜•์‹์œผ๋กœ command & data์˜ ํ˜•์‹์œผ๋กœ ๋ณด๋ƒˆ๋Š”๋ฐ, ์ด๋Ÿด ๋–„๋งˆ๋‹ค command์˜ ์—ญํ•  ์ž์ฒด๊ฐ€ ํ•˜๋‚˜์˜ http ์š”์ฒญ์„ ๋ณด๋‚ด๋Š” ๊ฒƒ์ด ์•„๋‹Œ, ๋ฐ์ดํ„ฐ๋งŒ ๋ฐ›์•„์„œ ์ด๋ฅผ ์ฒ˜๋ฆฌํ•ด์„œ ์‹คํ–‰ํ•˜๋Š” ๋А๋‚Œ์ด ๋“ค์—ˆ๋‹ค. ์šฐ๋ฆฌ์˜ ๋ฏธ์…˜์—์„œ๋Š” ํ•™์Šตํ•ด์•ผ ํ•  ๋ถ€๋ถ„์ด http request & response์— ๋Œ€ํ•œ ๋ถ€๋ถ„๋„ ์žˆ์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ด๋Ÿฌํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์„œ๋ฒ„์— ๋ณด๋‚ด๋Š” ๊ฒƒ์ด ํ˜•์‹์— ๋งž์ง€ ์•Š๋Š” ๊ฒƒ ๊ฐ™์•˜๋‹ค. ๋”ฐ๋ผ์„œ ์ด๋ฅผ ๊ธฐ๋ณธ์ ์ธ http ์š”์ฒญ์˜ ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค์–ด ์„œ๋ฒ„์— ๋ณด๋‚ด๋Š” ๋กœ์ง์„ ๋”ฐ๋กœ ์„ค์ •ํ•˜์—ฌ ์š”์ฒญ ๋ฐ ์‘๋‹ตํ•˜๋Š” ๊ฒƒ์ด ์กฐ๊ธˆ ๋” ์ ํ•ฉํ•œ ๋ฐฉ์‹์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.

const httpRequest = (path, data = {}, method = "POST") => {
  const request = {
    method: method,
    path: path,
    version: "HTTP/1.1",
    headers: {
      "Content-Type": "application/json",
      "Content-Length": JSON.stringify(data).length,
    },
    body: {
      data: data,
    },
  };
  return JSON.stringify(request);
};
 
module.exports = {
  httpRequest,
};

๋”ฐ๋ผ์„œ ๊ธฐ์กด HTTP ์š”์ฒญ์˜ ์ผ๋ถ€๋ถ„์„ ๋”ฐ๋ผํ•˜์—ฌ ๋น„์Šทํ•œ ์‹์˜ ํ…œํ”Œ๋ฆฟ์— ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด์•„ ๋ณด๋‚ด๋„๋ก ํ–ˆ๋‹ค. ์—ฌ๊ธฐ์„œ path์˜ ๊ฒฝ์šฐ ๊ธฐ์กด์— ์š”์ฒญํ•˜๋˜ ๋ช…๋ น์–ด๋“ค์„ ์˜๋ฏธํ•˜๋ฉฐ, ๋ฐ์ดํ„ฐ์˜ ๊ฒฝ์šฐ requestHandler์—์„œ readline์„ ํ†ตํ•ด ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฒ˜๋ฆฌํ•˜์—ฌ ๋„˜๊ฒจ์ฃผ์–ด JSON ํ˜•์‹์˜ ํ‘œ์ค€ํ™”๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์–ป๊ฒŒ ๋˜๊ณ , ์ด๋ฅผ ์„œ๋ฒ„์— ๋ณด๋‚ธ๋‹ค.

const httpSuccessResponse = (path, data = {}, method = "POST") => {
  const request = {
    result: "SUCCESS",
    method: method,
    path: path,
    version: "HTTP/1.1",
    headers: {
      "Content-Type": "application/json",
      "Content-Length": JSON.stringify(data).length,
    },
    body: {
      data: data,
    },
  };
  return JSON.stringify(request);
};
 
const httpFailResponse = (path, error = "", method = "POST") => {
  const request = {
    result: "ERROR",
    method: method,
    path: path,
    version: "HTTP/1.1",
    headers: {
      "Content-Type": "application/json",
      "Content-Length": JSON.stringify(error).length,
    },
    body: {
      error: error,
    },
  };
  return JSON.stringify(request);
};
 
module.exports = {
  httpFailResponse,
  httpFailResponse,
};

์„œ๋ฒ„์—์„œ๋„ ๋ฐ›์€ ์‘๋‹ต์—๋Œ€ํ•ด ์ฒ˜๋ฆฌ๋ฅผ ํ•˜๊ณ  ์‘๋‹ต ๊ฒฐ๊ณผ์— ๋”ฐ๋ผ ๋‘ ๊ฐ€์ง€์˜ ์‘๋‹ต์„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋„๋ก ํ•˜์˜€๋‹ค. FAIL์˜ ๊ฒฝ์šฐ ์™œ FAIL์ด ์ด๋ฃจ์–ด์กŒ๋Š”์ง€์— ๋Œ€ํ•œ ์ด์œ ๋ฅผ ๋‹ด์•„ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋„๋ก ํ•˜์˜€๋‹ค.

์—๋Ÿฌ ํ•ธ๋“ค๋ง

function throwServerError(errorCode, path, message) {
  throw Object.assign(new Error(message), {
    code: errorCode,
    path: path,
  });
}
 
module.exports = { throwServerError };

๊ธฐ์กด์˜ ์„œ๋ฒ„ ์—๋Ÿฌ๋“ค์€ ๊ทธ์ € ์—๋Ÿฌ๋ฅผ ๋˜์ ธ์ฃผ๊ธฐ๋งŒ ํ•˜๊ณ  ์ด์— ๋Œ€ํ•ด ๋”ฐ๋กœ ์ฒ˜๋ฆฌํ•ด์ฃผ์ง€ ์•Š์•„ ์„œ๋ฒ„์—์„œ๋งŒ ์ œ๋Œ€๋กœ ์—๋Ÿฌ๊ฐ€ ๋œจ๊ณ  , ํด๋ผ์ด์–ธํŠธ์—์„œ๋Š” ์—๋Ÿฌ์— ๋Œ€ํ•ด ํ•ธ๋“ค๋ง์„ ๋”ฐ๋กœ ํ•ด์ฃผ์ง€ ์•Š์•˜๋‹ค.

๋”ฐ๋ผ์„œ ์ด๋Ÿฌํ•œ ์—๋Ÿฌ๋ฅผ ๋˜์ ธ์ฃผ๋Š” ๊ณผ์ •์—์„œ ์„œ๋ฒ„์—์„œ ์ด ์—๋Ÿฌ๋ฅผ ๋ฐ›์•„ ์ ์ ˆํ•œ http Response๋กœ ๋ฐ”๊ฟ”์ค€ ํ›„ error ๊ด€๋ จ ์ •๋ณด๋ฅผ ๋‹ด์€ ์‘๋‹ต์„ ๋ณด๋‚ด์ค˜ ํด๋ผ์ด์–ธํŠธ์—์„œ ์ด๋ฅผ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์คฌ๋‹ค. ์—๋Ÿฌ์™€ ๊ฐ™์€ ๊ฒฝ์šฐ ์ฝ”๋“œ์™€ ์–ด๋–ค ๋ช…๋ น์–ด๋ฅผ ์‹คํ–‰ํ–ˆ์„ ๋•Œ, ์–ด๋–ค ์—๋Ÿฌ๊ฐ€ ๋–ด๋Š”์ง€๋ฅผ ์•Œ์•„์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์œ„์™€ ๊ฐ™์ด ๊ธฐ์กด Error์— ์ƒˆ๋กœ์šด property๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ์—๋Ÿฌ๋ฅผ ๋ฐ›์•„ ์ฒ˜๋ฆฌํ•ด์ฃผ๋„๋ก ํ–ˆ๋‹ค.

  socket.on("data", (request) => {
    console.log(`Received from client: ${request}`);
    request = JSON.parse(request);
    const data = request.body.data;
    const path = request.path;
    try {
      handleCommand(userSessionId, path, data, socket);
    } catch (error) {
      socket.write(httpFailResponse(error.path, error.code, error.message));
    }
  });

์„œ๋ฒ„๊ฐ€ ์—๋Ÿฌ๋ฅผ ํ•ธ๋“ค๋งํ•˜๋Š” ๋กœ์ง๋˜ํ•œ ๋ฐ”๊ฟ”์ฃผ์—ˆ๋‹ค. ์„œ๋ฒ„๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์œผ๋ฉด handleCommand๋ฅผ ํ†ตํ•ด ๋ฐ์ดํ„ฐ๊ฐ€ ๊ฐ€๊ฒŒ ๋˜๊ณ , ์ด๋ ‡๊ฒŒ ๊ฐ„ ๋ฐ์ดํ„ฐ๊ฐ€ ์œ ํšจ์„ฑ ๊ฒ€์ฆ ์ค‘์— ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค๋ฉด ์œ„์˜ ์—๋Ÿฌ๋ฅผ ๋˜์ ธ์ฃผ์–ด ์„œ๋ฒ„์˜ ์ตœ์ƒ์œ„์ธ ํ•ด๋‹น ์ฝ”๋“œ์—์„œ ๋ฐ›์•„ socket.write๋กœ ๋ฐ›์•„ ์ด๋ฅผ http response ํ˜•์‹์œผ๋กœ ์ฒ˜๋ฆฌํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ๋กœ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋„๋ก ํ•˜์˜€๋‹ค.