[Nest.js] websocket 클라이언트로 ack 보내기

[Nest.js] websocket 클라이언트로 ack 보내기

NestJS의 websocket 라이브러리를 사용해 웹소켓을 구현할 때 클라이언트로 ack를 보내는 방법을 알아봅시다.

  1. 문제
  2. 해결방법 (ack 보내기)
  3. 마치며

문제

프로젝트에서 웹소켓을 사용해서 1대1 채팅을 구현하고 있었는데 일단 코드는 이렇게 구현했었는데요.

export class ChatMessageGateway
  implements OnGatewayConnection, OnGatewayDisconnect
{
  constructor(
    @Inject(CHAT_ROOM_REPOSITORY_DI_TOKEN)
    private readonly chatRoomRepository: ChatRoomRepositoryPort,
    @Inject(APP_JWT_SERVICE_DI_TOKEN)
    private readonly appJwtService: AppJwtServicePort,
    @Inject(WINSTON_MODULE_NEST_PROVIDER)
    private readonly logger: Logger,
    private readonly commandBus: CommandBus,
  ) {}

  @WebSocketServer()
  server: Server;

  async handleConnection(socket: SocketWithUserDto) {
    ...
  }

  async handleDisconnect(socket: SocketWithUserDto) {
    ...
  }

  @UsePipes(customValidationPipe)
  @UseFilters(SocketCatchHttpExceptionFilter)
  @SubscribeMessage('enter_chat_room')
  async enterChat(
    @MessageBody() data: EnterChatDto,
    @ConnectedSocket() socket: SocketWithUserDto,
  ) {
    const { roomId } = data;

    const chatRoom = await this.chatRoomRepository.findOneById(roomId);

    if (isNil(chatRoom)) {
      this.logger.error(
        `[Socket] enter_chat_room failed: Room ${roomId} does not exist`,
      );
      throw new WsException('Does not exist room');
    }

    socket.join(String(roomId));

    this.logger.log(`[Socket] User ${socket.user.sub} joined room ${roomId}`);
  }

  @UsePipes(customValidationPipe)
  @UseFilters(SocketCatchHttpExceptionFilter)
  @SubscribeMessage('send_message')
  async sendMessage(
    @MessageBody() data: CreateChatMessageDto,
    @ConnectedSocket() socket: SocketWithUserDto,
  ) {
    const { roomId, message, blogPostUrl } = data;
    const { sub: userId } = socket.user;

    this.logger.log(
      `[Socket] Received message from ${userId} in room ${roomId}: ${message}`,
    );

    const command = new CreateChatMessageCommand({
      roomId: BigInt(roomId),
      userId: BigInt(userId),
      message,
      blogPostUrl,
    });

    this.logger.log(
      `[Socket] Sending message from ${userId} in room ${roomId}: ${message}, ${blogPostUrl}`,
    );

    await this.commandBus.execute<CreateChatMessageCommand, void>(command);

    socket
      .to(String(roomId))
      .emit('receive_message', { senderId: userId, message, blogPostUrl });

    this.logger.log(
      `[Socket] Message emitted to room ${roomId} from ${userId}`,
    );
  }
}



postman에서 enter_chat_room 이벤트와 send_message 이벤트를 테스트 해보면 결과는 아래와 같습니다.

enter-chat-room-테스트

send-message-테스트

postman에서는 이벤트 결과를 확인할 수 있지만 실제로 클라이언트에서는 이벤트 결과를 확인하기 힘들다고 하더라구요. 소켓 통신 테스트 과정에서 send_message 이벤트를 보냈을 때 메시지가 전송되지 않는 문제가 있었는데 이게 채팅방에 입장이 안된건지, 진짜 메시지가 전송이 안된건지 확인하기 힘들었습니다.

그래서 웹소켓에서도 http 요청처럼 응답을 보낼 수 있는 방법이 있나 찾아보다가 ack(acknowledgement)를 응답으로 보내는 방법을 발견하게 되었습니다.

해결방법 (ack 보내기)

ack를 보내게 되면 클라이언트에서 callback 함수를 통해서 응답을 받을 수 있습니다.
ack를 보내는 방법은 생각보다 간단했습니다.

ack-보내기

그냥 이렇게 이벤트 함수 안에 return값을 추가해주면 됩니다!

클라이언트에선 callback 함수를 통해서 ack를 확인할 수 있는데, 일단 코드를 커밋하기 전에 postman에서 테스트를 해봐야겠죠?
postman에서 ack를 받는 방법도 엄청 간단합니다. 그냥 Ack 버튼을 눌러 활성화해주면 됩니다.

postman-ack-테스트

이제 postman에서 enter_chat_room 이벤트를 테스트 해보면 아래와 같이 ack를 받을 수 있습니다.

ack-테스트

마치며

ack를 보내게 되면 클라이언트 입장에서 메시지가 전송이 되었는지 확인할 수 있기 때문에 소켓 통신을 테스트할 때 유용하게 사용할 수 있을거 같습니다.

그럼 다음 포스트에서 뵙겠습니다! 뾰로롱~

프리렌 움짤1


© 2024 NicoDora. All rights reserved.

Powered by Hydejack v9.1.6