-
Notifications
You must be signed in to change notification settings - Fork 3
๐ websocket์ด ๋ฆ๊ฒ ํ ๋น๋์ด ๋ฐ์๋๋ ๋ฌธ์
๋ถ์ผ | ์์ฑ์ | ์์ฑ์ผ |
---|---|---|
BE | ๊น์ฑํ | 24๋ 12์ 04์ผ |
-
์ฃผ์ถค์ฃผ์ถค ์๋น์ค๋ ํ๊ตญํฌ์์ฆ๊ถ์ ์น์์ผ์ ์ฌ์ฉํด ์ฃผ์์ ํ์ฌ ์ฒด๊ฒฐ๋๊ณผ ๊ฐ์ ์ค์๊ฐ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ ์ ์๋ค.
-
์ ์ ์๊ฒ ํ์ฌ๊ฐ๋ฅผ ๋ณด์ฌ์ฃผ๊ธฐ ์ํด์๋ ์ฃผ์๋ง๋ค ๋ผ์ด๋ธ๋ฐ์ดํฐ๋ฅผ ๊ตฌ๋ ํ๋ ๊ฒ์ด ์ต๊ณ ์ง๋ง, ์น์์ผ์ ๋ง๋ค ์ ์๋ ๊ฐ์์ ์ฃผ์์ ํ ๋นํ๋ ๊ฐ์๋ ํ์ ๋์ด ์๋ค
- ํ์ฌ ์น์์ผ์ ํ๊ตญํฌ์์ฆ๊ถ๊ณผ ์ฐ๊ฒฐํ ์ ์๋ ๊ฐ์๋ 4๊ฐ, ์ฃผ์์ ํ ๋นํ ์ ์๋ ๊ฐ์๋ ์น์์ผ๋น 41๊ฐ์ด๋ค.
- ๋ฐ๋ผ์ 164๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ผ์ด๋ธ๋ก ์ ๊ณตํ ์ ์์ผ๋, ์ฐ๋ฆฌ๋๋ผ์ ์ฃผ์์ ๋๋ต 4200๊ฐ์ ๋๋ก ํฑ์์ด ๋ถ์กฑํ๋ค.
-
๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์ ์ ์ ์ฐ๊ฒฐ์
socket.IO
์room
๊ธฐ๋ฅ์ ํ์ฉํด ํ์ฌ ์ฃผ์์ฐฝ์ ๋ณด๊ณ ์๋ ์ง ์ฒดํฌํ๊ณ , ํ๊ตญํฌ์์ฆ๊ถ๊ณผ์ ์ฐ๊ฒฐ์Websocket
์ ์ด์ฉํด ์ฐ๊ฒฐํ๋ ๋ฐฉ์์ผ๋ก ๊ตฌํํ๋ค.- ์ ์ ๊ฐ ์ฃผ์์ฐฝ์ ๋ณด๊ณ ์์ผ๋ฉด ํ๊ตญํฌ์์ฆ๊ถ์์ ์ฃผ์ ์ ๋ณด๋ฅผ ๋ฐ์ ์ ์๊ฒ ๊ตฌ๋ ํ๊ณ , ํ ๋ช ์ ์ ์ ๋ ๋ณด์ง ์๋ ๋ค๋ฉด ์ฃผ์ ์ ๋ณด ๊ตฌ๋ ์ ์ทจ์ํ๋ ๋ฐฉ์์ผ๋ก ์ ํ๋์ด ์๋ ๊ตฌ๋ ์ ๋ณด๋ฅผ ํจ์จ์ ์ผ๋ก ๊ด๋ฆฌํ๋ค.
- ์ ์ ๊ฐ ์ ์ํ์ ๋, ์๋ฒ๊ฐ ๋ง ์ผ์ก์ ์ํฉ์ผ ๊ฒฝ์ฐ ๋ผ์ด๋ธ๋ฐ์ดํฐ๊ฐ ์ ๋๋ก ๋ฐ์์ง์ง ๋ชปํ๋ ๊ฒฝ์ฐ๊ฐ ๋ฐ์ํ๋ค.
- ์ค์๊ฐ ๋ฐ์ดํฐ๊ฐ ๋ฐ์์ง์ง ์๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
-
LiveData
- ํ๊ตญํฌ์์ฆ๊ถ ์น์์ผ์ ๋ฉ์์ง, ๋ฆฌํด ๋ฉ์์ง ๋ฑ ๋น์ฆ๋์ค ๋ก์ง์ ์ฒ๋ฆฌํ๋ ํด๋์ค
- 4๊ฐ์ ์ฝ๋ฐฑํจ์๋ฅผ
WebsocketClient
ํด๋์ค์ ํ ๋นํ๋ ๋ฐฉ์์ผ๋ก ์๋ํ๋ค. -
WebsocketClient
๋ฅผ ํ ๋นํ ์ ์๋ ๊ฐ์๊ฐ ๋ฐํ์ ๋ ๊ฒฐ์ ๋์ด ์ด๋ฅผWebsocketClient
๋ฅผ ๋์ ์ผ๋ก ํ ๋นํ๋ค.
-
WebsocketClient
- ์น์์ผ ๋ก์ง๋ง ์ฒ๋ฆฌํ๋ ํด๋์ค
- ๋ก๊น , ์ฐ๊ฒฐ์ ํ๊ณ , ์ฝ๋ฐฑ์ ์ธ๋ถ์์ ์ฃผ์ ๋ฐ๋ ํด๋์ค
export class WebsocketClient {
static url = process.env.WS_URL ?? 'ws://ops.koreainvestment.com:21000';
private client: WebSocket;
constructor(@Inject('winston') private readonly logger: Logger) {
this.client = new WebSocket(WebsocketClient.url);
}
// ์ค๊ฐ ์๋ต
private sendMessage(message: string) {
if (this.client.readyState === WebSocket.OPEN) {
this.client.send(message);
this.logger.info(`Sent message: ${message}`);
} else {
this.logger.warn('WebSocket not open. Queueing message.');
}
}
static websocketFactory(logger: Logger) {
const websocket = new WebsocketClient(logger);
return websocket;
}
//์ดํ ์๋ต
}
- ์ด๋ฐ ํ๊ฒฝ์์ ์ ์ ๊ฐ subscribe๋ฅผ ํด์ ์ฃผ์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ๊ณ ์ ํ๋ฉด ๋ค์ sequence diagram ์ฒ๋ผ ๋ฌธ์ ๊ฐ ๋ฐ์ํ๋ค.
sequenceDiagram
participant User
participant LiveData
participant WebsocketClient
participant WebsocketToKoreaInvestment
LiveData->>WebsocketClient: ๋์ ์ผ๋ก ํ ๋นํ๋ ์ค
User->>LiveData: subscribe()
LiveData->>WebsocketClient: subscribe()
WebsocketClient->>WebsocketToKoreaInvestment: (์์ง ์์ผ์ด ์ด๋ฆฌ์ง ์์ ์ํ)
WebsocketClient->>User: Error: socket not assigned
-
Websocket
๋ด ์ฝ๋์์ ๋๋ถ๋ถ ๋๊ธฐ๋ก ์๋ํ์ง๋ง,upgrade
์์ฒญ์ ๋ํด์event emitter
๋ฅผ ํตํด ๋ฐ์์ ์ฒ๋ฆฌํ๋ค.
// Websocket lib/websocket.js
req.on('upgrade', (res, socket, head) => {
websocket.emit('upgrade', res);
//์ค๊ฐ ์๋ต
// socket์ ์ค์ง์ ์ผ๋ก ํ ๋นํ๋ ํจ์
// ํ ๋น์ด ์๋ฃ๋๋ฉด _readyState๋ฅผ WebSocket.OPEN์ผ๋ก ๋ณ๊ฒฝ
websocket.setSocket(socket, head, {
allowSynchronousEvents: opts.allowSynchronousEvents,
generateMask: opts.generateMask,
maxPayload: opts.maxPayload,
skipUTF8Validation: opts.skipUTF8Validation
});
//์ดํ ์๋ต
})
-
์น์์ผ์ ๊ฒฝ์ฐ ์ฒ์์
http
๋๋https
๋ก ๋จผ์ ์ฐ๊ฒฐ์ ์๋ํ๊ณ ,101 switching protocols
์ด ๋ค์ด์ค๋ฉดhttp
์์Websocket
์ผ๋ก ์ ๊ทธ๋ ์ด๋๋ฅผ ํ๋ค. -
์ด๋,
event emitter
๋ฅผ ์ด์ฉํดupgrade
์์ฒญ์ ์ฒ๋ฆฌํ๊ณ , ํ๊ตญํฌ์์ฆ๊ถ์์ ์์ผ์ด ์ด๋ฆฐ ์ํฉ์ด ์๋ ์ํ์์ ์ ์ ์ ์์ฒญ์ด ๋ค์ด์ฌ ์ ์๋ค๋ ์ ์ด์๋ค.
-
nestjs
์lifecycle
์ ๋ง์ถฐ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐฉ์์ด๋ค. - ๋ค๋ง, ์ด๋ฐ ๊ตฌ์กฐ์๋ ์ค์ง์ ์ผ๋ก ์์ผ์ด ์ธ์ ํ ๋น๋๋ ์ง๋ ์ ์ ์์ผ๋ฏ๋ก ๋ฌธ์ ๋ฅผ ์๋ฒฝํ๊ฒ ํด๊ฒฐํ ์ ์์๋ค.
- ๋ฌธ์ ๋ฅผ ์ค์ง์ ์ผ๋ก ํด๊ฒฐํ๊ธฐ ์ํด์ ์ ์ ์ ์์ฒญ์ ์์๋ก ์ ์ฅํ๊ณ , ๋ง์ฝ ์์ผ์ด ์ด๋ฆฐ๋ค๋ฉด ๊ทธ ์ดํ ์์ฒญ์ ์ฒ๋ฆฌํ๋ ๋ก์ง์ด ํ์ํ๋ค.
- ์ด๋ ํ๋ฅผ ์ด๋์ ์ ์ฉํ๋ ์ง์ ๋ํ ๊ณ ๋ฏผ์ด ์์๋ค.
-
LiveData
- ๋น์ฆ๋์ค ๋ก์ง์ ์ฒ๋ฆฌํ๋ ๋ถ๋ถ์ผ๋ก ์น์์ผ์ ์ฐ๊ฒฐ๊ณผ๋ ์๊ด ์๋ ๋ก์ง๋ง ์ฒ๋ฆฌํ๋ ๊ฒ์ ์ฒ์์ ์๊ฐํ์๋ค. - ๋ฐ๋ผ์ ํ๋ฅผ ์ ์ฉํด ์ ์ ์์ฒญ์ ์ฒ๋ฆฌํ๋ ๊ฒ์๋
WebsocketClient
์ ์ ์ฉํ๋ ๊ฒ์ด ๋ง๋ค๊ณ ์๊ฐํ๋ค.
-
export class WebsocketClient {
static url = process.env.WS_URL ?? 'ws://ops.koreainvestment.com:21000';
private client: WebSocket;
private messageQueue: string[] = [];
constructor(@Inject('winston') private readonly logger: Logger) {
this.client = new WebSocket(WebsocketClient.url);
this.initOpen(() => this.flushQueue());
this.initError((error) => this.logger.error('WebSocket error', error));
}
static websocketFactory(logger: Logger) {
return new WebsocketClient(logger);
}
//์ค๊ฐ ์๋ต
private sendMessage(message: string) {
if (this.client.readyState === WebSocket.OPEN) {
this.client.send(message);
this.logger.info(`Sent message: ${message}`);
} else {
this.logger.warn('WebSocket not open. Queueing message.');
this.messageQueue.push(message); // ํ์ ๋ฉ์์ง๋ฅผ ์ถ๊ฐ
}
}
private flushQueue() {
while (this.messageQueue.length > 0) {
const message = this.messageQueue.shift();
if (message) {
this.sendMessage(message);
}
}
}
}
- ์ด๋ฐ ๋ฐฉ์์ผ๋ก ์ค์ ๋ก ์์ผ์ด ์ด๋ฆฌ์ง ์์ ์ํฉ์์๋ ์์ ์ ์ผ๋ก ์ ์ ์ ์ค์๊ฐ ๋ฐ์ดํฐ ์์ฒญ์ ์๋ตํ ์ ์์๋ค.
- ๐ฉ FE ๊ธฐ์ ์ ํ์ด์
- โจ ์ฐจํธ์ ๋ฐ์ํ ๊ตฌํ๊ณผ useRef ํ์ ๋ฌธ์
- ๐ฃ ๋ถ๋ชจ ์์์ ์ํ์ ๋ฐ๋ผ ์์ ์์๋ ์คํ์ผ ๋ณํ ๋ถ์ฌํ๊ธฐ
- ๐ zod ๋์ ํ๊ธฐ
- ๐ useInfiniteQuery๋ฅผ ์ฌ์ฉํ ๊ทธ๋ํ ๋ฌดํ์คํฌ๋กค ๊ตฌํ
- ๐ซ ์ฌ์ฉ์์ ์์ ๋ณํ ์๋ ๊ทธ๋ํ ์คํฌ๋กค ๊ตฌํํ๊ธฐ
- ๐งช ์๋ง์ ๊ทธ๋ํ ๋ฐ์ดํฐ ์์ฒญ์ ์ด๋ป๊ฒ ์ค์ผ๊น
- ๐ ๋คํฌ๋ชจ๋์์ ์๋ก๊ณ ์นจ ์ ๋ผ์ดํธ๋ชจ๋๊ฐ ์ ๊น ๋ณด์ด๋ ๋ฌธ์
- ๐ ์น์์ผ์ ์ฑํ ๋ฐ์ดํฐ์ REST API์ ์ฑํ ๋ฐ์ดํฐ๋ฅผ ํจ๊ป ๊ด๋ฆฌํ๊ธฐ
- ๐ก BE ๊ธฐ์ ์ ํ ์ด์
- โ๏ธ Node WebSocket ํ๊ณ ๋ค๊ธฐ
- โ๏ธ TypeORM Datasource mock ๋ง๋ค๊ธฐ
- โ๏ธ oauth ID range ๋ฌธ์
- ๐ custom pipe์์ Nan์ด ๋ฐ์์ง๋ ๋ฌธ์
- ๐ช nest Websocket์ ์ธ์ ์ด ์๋๋ค๊ณ ?
- ๐ด nginx websocket ์ฐ๊ฒฐ ์ ๋ฌธ์ ๋ฐ์
- ๐ WebPush ๊ตฌํ
- ๐ง ์ฐ์ ์์ ํ๋ก ์์ฒญ ์ ์ดํ๊ธฐ
- ๐ websocket์ด ๋ฆ๊ฒ ํ ๋น๋์ด ๋ฐ์๋๋ ๋ฌธ์
- ๐ฅณ typeorm์ ์ด์ฉํ FCM ์๋ฆผ ์๋น์ค
- ๐ฆ ๋ค์ค ์ ์ ๋์์ฑ ์ ์ด โ ์ฑ๊ธํค, ๋ฎคํ ์ค
- ๐ ๊ทธ๋ํ ๋ฐ์ดํฐ๋ฅผ ์ค์๊ฐ์ผ๋ก ์ ๊ณตํ๊ธฐ์ํ ์ ๋ต
- ๐ ๏ธ ์ธํ๋ผ ๊ธฐ์ ์คํ ์ ํ ์ด์
- ๐ Ncloud ์ค์ ๊ณผ์
- ๐ ORM ๊ธฐ์ ์คํ ๋น๊ต
- ๐ค RabbitMQ๋ก ๋ถ์ฐ ์๋ฒ์๊ฒ ๋ฉ์์ง๋ฅผ ๋ถ๋ฐฐํ๊ธฐ
- ๐ข private DB ์๋ฒ์ ์ ์ํ์ง ๋ชปํ๋ ํ์
- ๐ 1์ฃผ์ฐจ ๋ฐํ
- ๐ 2์ฃผ์ฐจ ๋ฐํ
- ๐ 3์ฃผ์ฐจ ๋ฐํ
- ๐ 4์ฃผ์ฐจ ๋ฐํ
- ๐ 5์ฃผ์ฐจ ๋ฐํ
- ๐ ์ต์ข ๋ฐํ