Socket.IO 웹소켓 세팅
Cola Chat 익명 채팅 개발기· 3 / 7
Node.js 환경에서는 웹소켓을 직접 구현할 수 있지만, 간편성, 성능, 보안 등의 이점으로 전용 라이브러리를 주로 사용한다. 그중 가장 보편적인 Socket.IO 라이브러리를 이용해 구현할 예정이다.
Socket.IO
Socket.IO는 WebSocket 프로토콜 기반의 애플리케이션 개발을 위한 JavaScript 라이브러리로, 클라이언트와 서버 간의 양방향 통신을 간편하게 설정할 수 있도록 지원한다.
특징
-
이벤트 기반 모델
서버와 클라이언트가 다양한 이벤트로 수신한다. 전송 시에는emit, 수신 시에는on메서드를 사용한다. -
그룹화 방식
- namespace: 소켓 서버 내에 여러개의 네임스페이스를 서로 다른 기능으로서 독립적으로 관리할 수 있다.(ex. 채팅 서비스의 채널 단위)
- room: 네임스페이스 내에서 클라이언트를 그룹화해 관리할 수 있다.(ex. 채널 내의 채팅방 단위)
-
통신 안정성
연결이 끊어졌을 때 자동으로 재연결을 시도하며, WebSocket이 지원되지 않는 경우, XHR Polling, Long Polling 등 대체 방법으로 자동으로 전환하여 통신을 유지한다. -
보안 설정
특정 도메인만을 허용하도록 CORS 적용을 지원하며, JWT 인증을 통해 사용자를 식별하고 무분별한 소켓 생성을 방지할 수 있다.
Socket.IO를 이용한 웹소켓 세팅
서버와 클라이언트가 웹소켓 프로토콜로 통신하기 위해선 두 사이드 모두 웹소켓 세팅을 해주어야 한다. 각각의 웹 프레임워크는 다음과 같다.
- 서버: Nest.js
- 클라이언트: Next.js
서버 웹소켓 세팅: Nest.js
-
모듈 및 라이브러리 설치
npm install @nestjs/websockets @nestjs/platform-socket.io socket.io # @nestjs/websockets: Nest.js 환경에서 사용할 수 있는 웹소켓 관련 기능(핸들러, 데코레이터 등)이 담긴 모듈 # @nestjs/platform-socket.io: Socket.IO를 Nest.js에서 사용할 수 있게 설정하는 모듈 # socket.io: 웹소켓 실행 라이브러리Nest.js에서는
Socket.IO를 기반으로 만들어진@nestjs/websockets모듈을 사용할 수 있다. 이 모듈을 사용하면 Nest.js의 모듈 기반 아키텍처와 의존성 주입, 데코레이터 등 주요 특성을 활용하면서Socket.IO를 적용할 수 있다. -
모듈 생성
# Module(chat.module.ts) nest g mo chat # Gateway(chat.gateway.ts) nest g ga chat # Service(chat.service.ts) nest g s chatWebSocket을 활용한 메세징 기능은
chat이라는 모듈로 구성해 관리할 것이다. 이 모듈 내에서 gateway 파일이 WebSocket 통신의 진입점을 담당한다. -
Module 세팅
// src/app/chat/chat.module.ts import { Module } from '@nestjs/common'; import { ChatGateway } from './chat.gateway'; import { ChatService } from './chat.service'; @Module({ imports: [], providers: [ChatGateway, ChatService], }) export class ChatModule {}생성한 기능 파일들을 모듈 파일에 등록한다. 이때 Gateway은 Provider로서 작동한다.
-
Gateway 구성
// src/app/chat/chat.gateway.ts import { ConnectedSocket, OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit, WebSocketGateway, WebSocketServer, } from '@nestjs/websockets'; import { Server, Socket } from 'socket.io'; // 웹소켓 게이트웨이 정의(namespace 지정) @WebSocketGateway({ namespace: /\/*.+/ }) export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect { // 웹소켓 서버 인스턴스 주입 @WebSocketServer() public server: Server; // 웹소켓 서버 초기화 afterInit(server: Server): void { console.log('WebSocket Server Init'); } // 웹소켓 연결 handleConnection(@ConnectedSocket() socket: Socket): void { console.log('WebSocket Connected to', socket.nsp.name); } // 웹소켓 연결 해제 handleDisconnect(@ConnectedSocket() socket: Socket): void { console.log('WebSocket Disconnected from', socket.nsp.name); } }위와 같이 구성하면 서버 실행 시, 웹소켓 서버를 생성하고 외부 소켓 인스턴스를 연결할 준비가 완료된다.
implements에 인터페이스를 지정해 필요한 메서드 구현을 강제하여 일관된 구조를 유지한다.OnGatewayInit→afterInitOnGatewayConnection→handleConnectionOnGatewayDisconnect→handleDisconnect
소켓 연결(
handleConnection)와 해제(handleDisconnect) 메서드는 각각'connection'과'disconnect'라는 소켓 이벤트가 암묵적으로 지정되어 있어 특정 소켓의 연결과 해제를 자동으로 처리한다.
클라이언트 웹소켓 세팅: Next.js
-
모듈 및 라이브러리 설치
npm install socket.io-client # socket.io-client: Socket.IO의 클라이언트 측 라이브러리 -
Hook 세팅
// src/app/hooks/useSocket.ts import { useEffect } from 'react'; import { io } from 'socket.io-client'; import useSocketStore from '../stores/socketStore'; import { Room } from '@/shared/types/type'; import { SocketEvent } from '@/shared/types/enum'; const SOCKET_BASE_URL = process.env.NEXT_PUBLIC_SOCKET_BASE_URL as string; type Props = { room: Room; }; export default function useSocket(props: Props) { const { id: roomId, namespace } = props.room; const { socket, setSocket } = useSocketStore(); useEffect(() => { if (!namespace) return; // 소켓 생성 const socketInstance = io(`${SOCKET_BASE_URL}/${namespace}`, { transports: ['websocket'], autoConnect: false, }); // 소켓 연결 socketInstance.connect(); // 소켓 연결 후 socketInstance.on(SocketEvent.CONNECT, () => { console.log('Connected to WebSocket server'); setSocket(socketInstance); }); // 소켓 연결 해제 후 socketInstance.on(SocketEvent.DISCONNECT, () => { console.log('Disconnected from WebSocket server'); setSocket(null); }); return () => { // 소켓 연결 해제 if (socket) socket.disconnect(); }; }, [namespace, roomId]); }socket.io-client라이브러리의io함수를 이용해 서버와 웹소켓 통신을 할 수 있는 ‘소켓’ 인스턴스를 생성한다.생성한 인스턴스로
connect함수를 호출하면 핸드셰이크와 소켓 연결 작업이 순차적으로 이루어지며, 해당 Hook이 언마운트되면,disconnect함수가 호출되어 소켓 연결이 해제된다.인스턴스의
on함수는 특정 이벤트를 수신하는 역할을 하며, 서버에서는 소켓 연결 및 해제 후 자동으로 해당 이벤트를 전송한다. 각 이벤트 수신 장치에 대해 콜백 함수로 후속 작업을 설정해두었다.이제 웹소켓 연결이 필요한 컴포넌트에서 해당 Hook을 마운트하면 웹소켓 연결이 완료되며, 이벤트를 주고받을 수 있는 준비가 완료된다.
이렇게 서버와 클라이언트 간의 웹소켓 통신을 위한 기본적인 설정을 완료했다.