최종 프로젝트에서 채팅을 구현하던 도중 에러 핸들링이 되지 않는 문제가 생겼다.
문제상황은 Websocket에서 createChat() 이벤트를 실행하던 도중 발생했다.
// src/chats/chats.gateway.ts
@WebSocketGateway({
namespace: 'chats',
})
export class ChatsGateway implements OnGatewayConnection {
constructor(
private readonly chatsService: ChatsService,
private readonly messagesService: ChatMessagesService,
) {}
@WebSocketServer()
server: Server;
...
@SubscribeMessage('create_chat')
async createChat(@MessageBody() createChatDto: CreateChatDto) {
const chat = await this.chatsService.createChat(createChatDto);
}
...
}
creatChat()은 간단히 유저 목록을 받아, 유저들로 이뤄진 채팅방을 만드는 이벤트이다.
만약 존재하지 않는 유저를 보내거나, 아무런 유저도 보내지 않은 경우 등 에러가 발생해야한다.
위와 같이 Internal Server Error가 발생하는 것을 볼 수 있다.
터미널을 보면 createChat 로직으로 들어간 것을 볼 수 있다.
우리가 Dto를 만들었으므로, 잘못된 정보를 보냈을 경우, Dto에서 걸러줘야 하는데 현재 걸러주지 못하고 있는 상황이다.
이런 경우 pipe를 사용해야 하는데, 프로젝트의 main.ts에
app.useGlobalFilters(new HttpExceptionFilter());
위 설정을 해줘도 WebSocket에서 Dto에선 잘못된 값들을 걸러주지 못하고 있다.
기본적으로 NestJS는 REST API 기준으로 만들어졌으므로, WebSocket의 Dto가 적용이 안되는 것이다.
Gateway에서 Validate를 사용하려면 직접 데코레이팅을 해줘야한다.
@UsePipes(
new ValidationPipe({
transform: true,
whitelist: true,
forbidNonWhitelisted: true,
}),
)
@SubscribeMessage('create_chat')
async createChat(@MessageBody() createChatDto: CreateChatDto) {
const chat = await this.chatsService.createChat(createChatDto);
}
여전히 Internal Server Error가 발생한다.
터미널을 보면
Bad Request Exeption이 발생하는 것을 볼 수 있다.
우리가 보고싶은 에러는 Websocket에러인데, HttpException을 상속받는 Bad Request Exception을 던지고 있다.
이런 경우 HttpException에러를 받아서 WsException으로 변환해주면 된다.
// src/common/exception-filter/ws.exception-filter.ts
import { Catch, HttpException } from '@nestjs/common';
import { BaseWsExceptionFilter } from '@nestjs/websockets';
@Catch(HttpException)
export class WsExceptionFilter extends BaseWsExceptionFilter<HttpException> {
catch(exception: HttpException, host) {
const socket = host.switchToWs().getClient();
socket.emit('exception', { data: exception.getResponse() });
}
}
위 코드 처럼 HttpException 에러가 발생할 경우, 에러 메세지를 exception이란 이벤트로 소켓에 emit하면 된다.
실제 코드에 적용한 뒤 응답이 바뀌는지 알아보자
@UsePipes(
new ValidationPipe({
transform: true,
whitelist: true,
forbidNonWhitelisted: true,
}),
)
@UseFilters(WsExceptionFilter)
@SubscribeMessage('create_chat')
async createChat(@MessageBody() createChatDto: CreateChatDto) {
const chat = await this.chatsService.createChat(createChatDto);
}
우리가 원하는 WsException 에러를 받을 수 있게 됐다