WebSocket과 실시간 애플리케이션
WebSocket은 클라이언트와 서버 간의 실시간, 양방향, 전이중 통신을 가능하게 하는 프로토콜입니다.
전통적인 HTTP 통신과 달리, WebSocket은 연결을 유지하며 지속적인 데이터 교환을 허용합니다.
이는 채팅 애플리케이션, 실시간 대시보드, 게임 등 즉각적인 데이터 업데이트가 필요한 애플리케이션에서 특히 중요합니다.
NestJS에서 WebSocket 구현
NestJS에서 WebSocket을 구현하는 기본 단계
- 의존성 설치
npm install @nestjs/websockets @nestjs/platform-socket.io
- WebSocket 게이트웨이 생성
import { WebSocketGateway, SubscribeMessage, MessageBody } from '@nestjs/websockets';
@WebSocketGateway()
export class EventsGateway {
@SubscribeMessage('events')
handleEvent(@MessageBody() data: string): string {
return data;
}
}
- 모듈에 게이트웨이 등록
import { Module } from '@nestjs/common';
import { EventsGateway } from './events.gateway';
@Module({
providers: [EventsGateway],
})
export class EventsModule {}
메시지 핸들러와 양방향 통신
클라이언트와의 양방향 통신을 위한 메시지 핸들러 구현
import { WebSocketGateway, SubscribeMessage, MessageBody, WebSocketServer } from '@nestjs/websockets';
import { Server } from 'socket.io';
@WebSocketGateway()
export class ChatGateway {
@WebSocketServer()
server: Server;
@SubscribeMessage('chatToServer')
handleMessage(@MessageBody() message: string): void {
this.server.emit('chatToClient', message);
}
}
Socket.IO 통합 및 고급 기능
Socket.IO를 사용한 네임스페이스와 룸 구현
@WebSocketGateway({ namespace: '/chat' })
export class ChatGateway {
@SubscribeMessage('joinRoom')
handleJoinRoom(client: Socket, room: string) {
client.join(room);
return { event: 'joinedRoom', data: room };
}
@SubscribeMessage('chatToRoom')
handleChatToRoom(client: Socket, payload: { room: string, message: string }) {
this.server.to(payload.room).emit('chatToClient', payload.message);
}
}
WebSocket 연결 인증 및 보안
WebSocket 연결에 대한 인증 미들웨어 구현
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
import { Socket } from 'socket.io';
@Injectable()
export class WsAuthGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
const client: Socket = context.switchToWs().getClient();
// 토큰 검증 로직
return this.validateToken(client.handshake.auth.token);
}
private validateToken(token: string): boolean {
// 토큰 검증 로직 구현
return true;
}
}
게이트웨이에 가드 적용
@WebSocketGateway()
@UseGuards(WsAuthGuard)
export class SecureGateway {}
실시간 데이터 동기화 및 상태 관리
Redis를 사용한 상태 관리 및 동기화
import { WebSocketGateway, WebSocketServer } from '@nestjs/websockets';
import { Server } from 'socket.io';
import * as Redis from 'ioredis';
@WebSocketGateway()
export class SyncGateway {
@WebSocketServer()
server: Server;
private redis: Redis.Redis;
constructor() {
this.redis = new Redis();
this.redis.subscribe('dataUpdate');
this.redis.on('message', (channel, message) => {
if (channel === 'dataUpdate') {
this.server.emit('dataUpdated', JSON.parse(message));
}
});
}
// 데이터 업데이트 메서드
async updateData(data: any) {
await this.redis.publish('dataUpdate', JSON.stringify(data));
}
}
대규모 WebSocket 연결 처리
확장성 및 성능 최적화 전략
- 수평적 확장 : 여러 서버 인스턴스에 걸쳐 부하 분산
- Redis Adapter 사용 : 여러 서버 간 상태 공유
import { WebSocketGateway } from '@nestjs/websockets';
import { RedisIoAdapter } from './redis-io.adapter';
const app = await NestFactory.create(AppModule);
const redisIoAdapter = new RedisIoAdapter(app);
await redisIoAdapter.connectToRedis();
app.useWebSocketAdapter(redisIoAdapter);
- 연결 풀링 : 클라이언트 연결 수 제한
- 메시지 버퍼링 : 대량의 메시지 처리 시 버퍼링 적용
실시간 애플리케이션 예시
- 실시간 채팅 애플리케이션
@WebSocketGateway()
export class ChatGateway {
@WebSocketServer()
server: Server;
@SubscribeMessage('sendMessage')
handleMessage(@MessageBody() message: string, @ConnectedSocket() client: Socket) {
this.server.emit('newMessage', { text: message, userId: client.id });
}
}
- 실시간 대시보드
@WebSocketGateway()
export class DashboardGateway {
@WebSocketServer()
server: Server;
@Interval(5000) // 5초마다 업데이트
handleInterval() {
const data = this.getDashboardData(); // 대시보드 데이터 가져오기
this.server.emit('dashboardUpdate', data);
}
}
- 실시간 알림 시스템
@WebSocketGateway()
export class NotificationGateway {
@WebSocketServer()
server: Server;
sendNotification(userId: string, notification: any) {
this.server.to(userId).emit('notification', notification);
}
@SubscribeMessage('subscribeToNotifications')
handleSubscription(@MessageBody() userId: string, @ConnectedSocket() client: Socket) {
client.join(userId);
}
}
Best Practices 및 주의사항
- 연결 관리 : 클라이언트 연결/연결 해제 이벤트 처리
- 에러 처리 : 예외 상황에 대한 적절한 처리 및 클라이언트에게 알림
- 메시지 유효성 검사 : 클라이언트로부터 받은 데이터 검증
- 보안 : SSL/TLS 사용, 메시지 암호화 고려
- 성능 모니터링 : 연결 수, 메시지 처리량 등 주요 지표 모니터링
- 재연결 전략 : 클라이언트 측 재연결 로직 구현
- 테스트 : 단위 테스트 및 통합 테스트 작성
- 문서화 : WebSocket API 명세 작성 및 유지보수
- 버전 관리 : WebSocket 프로토콜 버전 관리 전략 수립
- 확장성 고려 : 초기 설계 단계부터 대규모 확장 가능성 고려
NestJS의 @WebSocketGateway 데코레이터와 관련 기능들은 WebSocket 구현을 간소화하고, 기존의 NestJS 아키텍처와 잘 통합됩니다.
메시지 핸들러를 통해 클라이언트의 이벤트를 쉽게 처리할 수 있으며, 서버에서 클라이언트로의 푸시 알림도 효과적으로 구현할 수 있습니다.
Socket.IO와의 통합은 더욱 강력한 기능을 제공합니다.
네임스페이스와 룸 개념을 활용하면 복잡한 실시간 애플리케이션 구조를 효율적으로 관리할 수 있습니다.