NestJS-Socket.io(amit/on)

효딩딩·2023년 7월 23일
0

HTTP 연결방식(단방향 연결)

Client에서 어떤 url로 request 보내게 되면 server는 그 request에 해당하는 로직을 수행하게 된다. 그 로직을 수행하게 되면 결과값으로 response를 client에게 보내게 된다. 요청을 주면 응답을 보내고 응답을 보내는 순간 연결이 끊기게 된다. 이것이 단방향 연결 방식이다.
client가 반드시 server로 요청을 해야지 server는 응답를 보낼 수 있다.
한번 요청하고 응답했었을 때 다음에도 동일한 클라이언트가 요청을 해도 동일한 클라이언트인지 인식을 못한다.(왜냐면 단방향이기 때문) 그래서 jwt 인증방식 이런것들을 사용하는것이다.

양방향 연결이 필요한 경우?
실시간 경매나 실시간 채팅 같은 경우는 실시간으로 브라우저에 보여야한다.

http polling 방식

  • 단방향 연결인데 계속 클라이언트가 n초마다 요청을 보내서 양방향 연결 방식으로 보이는것이다.
  • 모든 브라우저가 n초 마다 서버에 요청을 보내면 서버가 부하가 된다.

소켓 프로그래밍

이런문제로 인해 Socket 연결방식(양방향)이 나오게 되었다.
* Soket : 입구, 콘센트 Socket

client(socket) <---> (socket)server

  • (socket)을 콘센트이고 콘센트를 통해 양방향으로 연결됬다고 생각하면된다.
  • amit과 on으로 소통한다. /amit 보내는것, on 받는것이다.

실시간 경매의 경우?

  • 주식을 매도했다고(자체가 이벤트) 하면 주식 매도했을 때 해당하는 정보가 socket을 통해 연결된다. 연결되면 server는 그에 대한 데이터를 받아서 비즈니스 로직을 수행하게 된다. 수행한 후 해당하는 모든 클라이언트에게 socket으로 연결이 되있으므로 데이터를 쏴주기만하면된다.(거래 내용이 달라졌기 때문에)

실시간 채팅의 경우?

  • 클라이언트가 "안녕"이라는 채팅을 치게 되면 채팅 메세지가 데이터에 담기게 된다. 데이터를 받아서 채팅 이벤트가 실행되서 비즈니스 로직이 수행된다. 수행 후 변경사항을 모든 클라이언트에게 보여주게된다.
  • "안녕"이라는 채팅을 클라이언트에서 서버에 보낼 때 amit으로 해당 이벤트가 서버에서 받을 on으로 받는다. 또 받은 이벤트를 서버에 저쟝하고 비즈니스 로직을 수행한 후 응답을 클라이언트에 보낼때 amit으로 보내고 클라이언트에서는 해당 응답을 on으로 받는다.

nest에서 socket 프로그래밍을 하기위해 도와주는 패키지가 있다.

nestJS-websockets-geteways 공식문서
socket 프로그래밍은 geteway를 사용해서 개발한다.

Gateways

  • DI(의존성 주입)이 가능하다.
  • geteway로 socket 프로그래밍을 한다.

설치

npm i --save @nestjs/websockets @nestjs/platform-socket.io
npm install socket.io

nest g ga chats  // geteway 만들기
nest g mo chats // module 만들기 

chats.Module.ts

import { Module } from '@nestjs/common';
import { ChatsGateway } from './chats.gateway';

@Module({
  providers: [ChatsGateway], << 등록하기
})
export class ChatsModule {}

index.hbs

<html>
  <head>
    <meta charset='utf-8' />
    <title>{{data.title}}</title>
    <link href='css/styles.css' rel='stylesheet' />
  </head>
  <body>
    <div id='hello_stranger'></div>
    <div id='chatting_box'></div>
    <form id='chat_form'>
      <input placeholder='Chat..' />
      <button type='submit'>Chat</button>
    </form>

// 소켓을 사용하기 위해  소켓io라는 cdn 서비스가 필요하다. 그 라이브러리는 링크로 스크립트로 껴넣을 수 있다. 아래같이 사용한다.

   <script src='https://unpkg.com/@babel/standalone/babel.min.js'></script>
// babel: 최신 JS 문법은 브라우저에서 사용할 수 있도록 해주는것
   <script
      src='https://polyfill.io/v3/polyfill.min.js?  features=default%2Ces2015%2Ces2016%2Ces2017%2Ces2018%2Ces2019'></script>
// polyfill 은 promist.all 같은것 도 할수 있게 해주는것
   <script
      src='https://cdn.socket.io/3.1.3/socket.io.min.js'
// socket.io를 클라이언트에서 사용할 수 있도록 해준다.
      integrity='sha384-cPwlPLvBTa3sKAgddT6krw0cJat7egBga3DJepJyrLl4Q9/5WLra3rrnMcyTyOnh'
      crossorigin='anonymous'></script>
   <script src='js/script.js'></script>

  </body>
</html>

script.js

const socket = io('/');
// ('/') : 네임스페이스
// io 가 socket.io.min.js 여기에 있는 하나의 요소(메소드)이다.
const getElementById = (id) => document.getElementById(id) || null;
//  document.getElementById(id) 해당 id를 통해서 dom요소를 잡아오는 것이다.

//* get DOM element
const helloStrangerElement = getElementById('hello_stranger'); 
const chattingBoxElement = getElementById('chatting_box');  
const formElement = getElementById('chat_form');   

function helloUser() {
  const username = prompt('What is your name?');
 // (1) socket.emit('new_user', username) 
  console.log(username) 
 // socket.emit()  클라이언트에서 서버로 보낼때 / 서버에서 클라이언트로 보낼 때 사용한다. 
 // 첫번째 인자: 이벤트 이름, 두번쩨 인자 : 데이터
 // 이렇게 보내면 서버에서는 받아서 처리를해줄 수 있다.  
  socket.emit('new_user', (data)=> {console.log(data)}) 
  socket.on('hello_user', (data)=> { console.log(data)})
  // 데이터를 받을 때  callback 함수로 받을 수 있다. 데이터를 받자 마자 콜백함수가 실행된다.
  
}

function init() {
  helloUser();
}
init();
// init()이 실행되어 helloUser가 실행되는데  console.log(username)을 하게되면 
// 해당 name이 console창에 뜨게 된다. (웹브라으저에서 입력을 하면 받아오는것이다.)

받아온 username을 server에 보내고 server에서 읽을 수 있도록 받아보자.

  • socket.emit()을 사용해서 보낼 수 있다.
  • newUser()라는 하나의 이벤트를 등록해서 socket을 통해 데이터를 보낼것이다.

chats.gateway.ts

import {
  ConnectedSocket,
  MessageBody,
  SubscribeMessage,
  WebSocketGateway,
} from '@nestjs/websockets';
import { Socket } from 'socket.io';

@WebSocketGateway()
export class ChatsGateway {
  @SubscribeMessage('new_user') //  여기서 이벤트를 쓰게 되면 해당 함수를 실행시켜준다.
// handleMessage(client: any, payload: any): string { return 'Hello world!'}
handleNewUser(  
  @MessageBody() username: string,
  @ConnectedSocket() socket: Socket, // socket으로 amit, on이 가능하다. 
  ) : string {
  console.log(username)
 socket.emit('hello_user', `hello ${username}`) << // 서버에서 클라이언트로 보내기
   // 'hello_user' 라는 이벤트로 `hello ${username}` 데이터로 보내기.
   
 return 'hello world' 
  //  socket.emit('new_user', (data)=> {console.log(data)}) 으로 받게 됨.
  }

}
// handleMessage 이것은 실행시켜주는 함수이다. controller 라고 생각하면되고 이름은 변경할 수 있다. 인자들을 데코레이터를 가공해서 만들어주고 어떤 데이터를 받을지 타이핑을 해주는것임. 
//  @SubscribeMessage('new_user') 해서 데코레이터가 on으로 메세지를 받은것이다.

  • 사용자가 입력을 하는 순간 프론트에서 username이 담기게 되고 담긴 username이 socket으로 보내게 된다. 보낼 때는 이벤트 등록을 해야하는데 new_user라는 이벤트로 등록을 했다. 그래서 amit으로 보내게 되면 서버에 socket이 들어오게 되면 해당하는 이벤트를 찾는다.(controller 처럼) 해당 이벤트가 new_user에 대해 handleNewUser(인자로 클라이언트에서 보낸 데이터를 받는다) 받은 username을 console에 찍어준것이다.

  • 새로고침을 하는 순간 Socket은 끊기게 되고 새로운 아이디로 부여 받는다.
    socket은 개별적으로 id가 존재한다. 그 id는 연결이 끊기기 전까지 유지가 된다. 서버 자체는 클라이언트에 접속한 id를 통해 해당 유저를 식별할 수 있는 것이다.

  • console 에 hello world 찍히게 됨 이것은 chats.gateway.ts return 값에서 왔다.
profile
어제보다 나은 나의 코딩지식

1개의 댓글

comment-user-thumbnail
2023년 7월 23일

정리가 잘 된 글이네요. 도움이 됐습니다.

답글 달기