[NestJS] socket.io 사용법 + req.user받아 오는법

홍성웅·2024년 4월 18일
0

시작하기 앞서

이건 놀라운 사실인데 nestjs에서는 dynamic namespace를 정규표현식으로 적용하는 방법이 없다. 이걸 안것이 3일이나 걸렸다는 사실이 너무 슬프다.

공식 문서를 통해 기초를 구현하고 확장시킨 것이니 기초적인 구현은 공식문서를 보도록 하자.

socket.io 사용

import {
  ConnectedSocket,
  MessageBody,
  OnGatewayConnection,
  OnGatewayDisconnect,
  OnGatewayInit,
  SubscribeMessage,
  WebSocketGateway,
  WebSocketServer,
} from '@nestjs/websockets';
import { Server, Socket } from 'socket.io';


@WebSocketGateway({ namespace: 'anything' })
export class SocketGateway
  implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect
{
  @WebSocketServer()
  public server: Server;

  handleConnection(client: Socket): void {
  }

  afterInit() {
    console.log('socket is open!');
  }

  handleDisconnect(client: Socket): void {
    delete this.clients[client.id];
  }

  @SubscribeMessage('chat')
  handleMessage(@MessageBody() { roomId, message }: Data): void {
    this.server.emit('chat', message);
  }

}

afterInit

이함수는 socket이 열리면 실행되는 함수이다.

handleConnection

이함수는 OnGatewayConnection를 implement하면 꼭선언해주어야한다. client가 연결이 되었을 때 실행되는 함수다

handleDisconnect

이함수는 OnGatewayDisconnect를 implement하면 꼭선언해줘야한다.
client가 연결이 끊겼을 때 실행되는 함수다.

event받는법

@SubscribeMessage('chat')

이것은 client에서 'chat'이라고 보낸 event를 받겟다는 의미이다.

@MessageBody() message:string

받은 이벤트와 같이 온 parameter를 선언해주는 데코레이터다.

@SubscribeMessage('chat')
handleMessage(@MessageBody() message:string){
	console.log(message);
}// 이런식으로 클라이언트에서 보낸 파라미터를 이용할 수 있다.

@ConnectedSocket() socket:Socket

이벤트를 보낸 클라이언트의 소켓을 불러올수 있다.

@SubscribeMessage('chat')
handleMessage(@MessageBody() message:string, @ConnectedSocket socket:Socket){
	socket.emit('chat',message);
}
 // 이벤트를 보낸 클라이언트에게 이런식으로 이벤트를 날릴 수 있다.

this.server의 역할

여기서 말하는 this.server 말그대로 socket 서버를 의미한다.
그래서 전체에게 이벤트를 보내고싶으면 아래와 같이 사용가능하다.

handleAll(){
	this.server.emit('all','전체에게 보냅니다.);
    this.server.(/*이부분에 socketId나 room Id를 넣어주면 그클라이언트나 room에있는 모든 유저에게 */).emit('event','해당하는 모두');
}

req.user 받는법

Adapter 사용

req.user을 받으려면 Adapter를 사용을 해서 socket에 request 정보를 넣어야한다.

adapter.ts

import { INestApplicationContext } from '@nestjs/common';
import { IoAdapter } from '@nestjs/platform-socket.io';
import { Server, ServerOptions } from 'socket.io';
import express from 'express';
import * as passport from 'passport';

export class SessionAdapter extends IoAdapter {
  private session: express.RequestHandler;

  constructor(session: express.RequestHandler, app: INestApplicationContext) {
    super(app);
    this.session = session;
  }

  create(port: number, options?: ServerOptions): Server {
    const server: Server = super.create(port, options);

    // 미들웨어로 request를 등록한다.
    const wrap = (middleware) => (socket, next) =>
      middleware(socket.request, {}, next);

    server.use((socket, next) => {
      next();
    });
    server.use(wrap(this.session));
    server.use(wrap(passport.initialize()));
    server.use(wrap(passport.session()));
    return server;
  }
}

main.ts

import { NestFactory } from '@nestjs/core';
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
import * as passport from 'passport';
import { SessionAdapter } from './session.adapter';
import * as session from 'express-session';
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const sessionMiddleware = session({
    secret: process.env.SECRET, // 세션을 암호화하기 위한 암호기 설정
    resave: false, // 모든 request마다 기존에 있던 session에 아무런 변경 사항이 없을 시에도 그 session을 다시 저장하는 옵션
    // saveUnitialized: 초기화되지 않은 세션을 저장할지 여부를 나타낸다.
    saveUninitialized: false,
    // 세션 쿠키에 대한 설정을 나타낸다.
    cookie: {
      maxAge: 60000 * 60, // 1 hour
      httpOnly: true,
    },
  });
  app.use(sessionMiddleware);
  app.useWebSocketAdapter(new SessionAdapter(sessionMiddleware, app));

  // Passport를 초기화하는 미들웨어, 이를 통해 Passport의 인증/인가를 사용할 수 있다.
  app.use(passport.initialize());
  // Passport 세션을 사용하기 위한 미들웨어이다. 이를 통해 Passport는 세션을 기반으로 사용자의 인증 상태를 유지 관리 할 수 있다.
  app.use(passport.session());
  await app.listen(3000);
}
bootstrap();

사용법

connectedSocket() client : any
any로 설정해줘야 socket정보에 request 정보가 들어가기 때문에 꼭 any로 type을 지정해줘야한다.

handleConnection(@ConnectedSocket() client: any) {
    this.server.emit('list', client.request.user);
  }
profile
Backend Developer

0개의 댓글