Skip to Content
API 레퍼런스실시간 (Socket)

실시간 (Socket.IO)

ASAPJS는 @asapjs/socket 패키지를 통해 Socket.IO 를 래핑합니다. 소켓 이벤트 핸들러는 createSocket()을 사용하여 *Socket.ts 파일에 선언되고, 시작 시 자동으로 검색되며, 연결된 모든 클라이언트에 자동으로 바인딩됩니다. 선택적 Redis 어댑터를 통해 여러 서버 인스턴스에 걸친 수평 확장이 가능합니다.

Socket.IO 레이어를 활성화하려면 config의 extensions 배열에 '@asapjs/socket'을 추가하세요. 전체 config 레퍼런스는 부트스트랩을 참조하세요.

타입

RouteRequest

소켓 핸들러가 수신하는 이벤트를 설명합니다.

interface RouteRequest { path: string; // 소켓 이벤트 이름, 예: 'chat:message' roles?: string[]; // 향후 역할 기반 필터링을 위해 예약됨 }

ExecuteFunction

소켓 이벤트의 핸들러 함수 시그니처입니다.

type ExecuteFunction = ( socket: Socket, args: { body: any; user: any } ) => Promise<unknown> | unknown | void;
파라미터타입설명
socketSocket연결된 클라이언트의 Socket.IO Socket 인스턴스. 이 클라이언트에게 다시 내보내거나, 룸에 참가하거나, 다른 클라이언트에게 브로드캐스트하는 데 사용합니다.
args.bodyany클라이언트가 이벤트를 내보낼 때 전송하는 페이로드.
args.userany소켓의 HTTP 업그레이드 요청의 JWT에서 디코딩된 인증된 사용자 객체(socket.request.user로 사용 가능).

SocketOption

소켓 모듈이 getConfig().socket에서 읽는 설정 객체입니다.

interface SocketOption { adapter?: unknown; // 커스텀 Socket.IO 어댑터 (고급 사용) listener?: (socket: unknown) => void; // 원시 연결 훅 redis?: RedisClientOptions; // pub/sub 어댑터를 위한 Redis 클라이언트 설정 }
필드타입설명
adapterunknown커스텀 Socket.IO 어댑터 인스턴스. 거의 필요하지 않습니다. 내장 Redis 어댑터에는 redis를 사용하세요.
listener(socket) => void이벤트 핸들러가 등록되기 전 모든 새 연결마다 호출됩니다. 소켓별 일회성 설정에 유용합니다.
redisRedisClientOptionsredis npm 패키지의 Redis 클라이언트 옵션. 제공되면 모듈이 pub/sub 클라이언트 쌍을 생성하고 @socket.io/redis-adapter를 연결합니다.

createSocket()

import { createSocket } from '@asapjs/socket'; createSocket(request: RouteRequest, execute: ExecuteFunction): void

소켓 이벤트 핸들러를 등록합니다. *Socket.ts 파일의 모듈 스코프에서 이 함수를 호출하세요. 모듈은 initSocketModule이 자동으로 로드하므로 클라이언트가 연결되기 전에 모든 createSocket 호출이 등록됩니다.

파라미터타입설명
requestRouteRequest수신할 소켓 이벤트 이름을 지정하는 path 필드를 가진 객체.
executeExecuteFunction연결된 클라이언트가 해당 이벤트를 내보낼 때마다 호출되는 비동기 또는 동기 핸들러.

클라이언트가 연결되면 프레임워크는 등록된 각 createSocket 항목에 대해 socket.on(request.path, handler)를 호출합니다.

socketSendTo()

import { socketSendTo } from '@asapjs/socket'; socketSendTo(to: string, event: string, data: any): void

특정 소켓 ID 또는 룸 이름에 data와 함께 event를 내보냅니다. io.to(to).emit(event, data)에 위임합니다.

파라미터타입설명
tostring소켓 ID(특정 클라이언트 대상) 또는 룸 이름(해당 룸의 모든 클라이언트 대상).
eventstring수신 클라이언트가 수신하는 이벤트 이름.
dataany전송할 페이로드. JSON 직렬화 가능해야 합니다.
// 소켓 ID로 단일 클라이언트에 전송 socketSendTo(socket.id, 'notification', { message: 'Your order shipped' }); // 룸의 모든 클라이언트에 전송 socketSendTo('room:lobby', 'chat:message', { text: 'Hello everyone' });

socketSendAll()

import { socketSendAll } from '@asapjs/socket'; socketSendAll(event: string, data: any): void

모든 연결된 클라이언트에 data와 함께 event를 브로드캐스트합니다. io.emit(event, data)에 위임합니다.

파라미터타입설명
eventstring이벤트 이름.
dataany브로드캐스트할 페이로드.
// 모든 연결된 클라이언트에 시스템 공지 브로드캐스트 socketSendAll('system:announcement', { message: 'Scheduled maintenance in 5 minutes' });

getSocketIO()

import { getSocketIO } from '@asapjs/socket'; const io = getSocketIO(); // Socket.IO Server | undefined 반환

전역 Socket.IO Server 인스턴스를 반환합니다. 소켓 모듈이 아직 초기화되지 않은 경우 undefined를 반환합니다.

네임스페이스 생성, 연결된 소켓 검사, 서버 레벨에서 미들웨어 사용 등 고급 작업을 위해 Socket.IO 서버에 직접 접근할 때 getSocketIO()를 사용하세요.

const io = getSocketIO(); if (io) { const sockets = await io.fetchSockets(); console.log(`Connected clients: ${sockets.length}`); }

initSocketModule()

import { initSocketModule } from '@asapjs/socket'; const initSocketModule = async ( server: http.Server, dirname: string ): Promise<boolean>

Socket.IO 모듈을 초기화합니다.

파라미터타입설명
serverhttp.ServerSocket.IO가 바인딩할 Node.js HTTP 서버 인스턴스. Express가 사용하는 것과 동일한 서버입니다.
dirnamestring컴파일된 출력 디렉토리의 절대 경로. *Socket.js 파일을 찾기 위해 모든 하위 디렉토리를 재귀적으로 스캔합니다.

반환값: 성공 시 Promise<true>.

참고: Application.run()은 내부적으로 @asapjs/socket에서 SocketPlugin 클래스를 import하려고 시도하지만, 소켓 패키지는 현재 SocketPlugin을 export하지 않습니다. 소켓 패키지가 export하는 초기화 함수는 initSocketModule입니다. 플러그인 패턴을 통한 자동 초기화가 실패할 수 있으므로, 수동으로 initSocketModule을 호출해야 할 수 있습니다.

초기화 단계:

  1. *Socket.js에 해당하는 파일을 찾기 위해 dirname을 재귀적으로 스캔합니다(.map으로 끝나는 파일은 건너뜁니다). 매칭되는 각 파일은 require()되어 최상위 레벨의 createSocket() 호출이 실행되고 이벤트 핸들러가 등록됩니다.
  2. socketInit(server, getConfig().socket)을 호출하여 SocketServer 인스턴스를 생성합니다.
  3. config.socket.redis가 설정된 경우 Redis pub 클라이언트와 duplicate sub 클라이언트를 생성하고 둘 다 연결한 후 @socket.io/redis-adapter를 Socket.IO 서버에 연결합니다.
  4. connection 이벤트 핸들러를 등록합니다. 새 연결마다 socket.request.user를 읽고 등록된 모든 이벤트 핸들러를 소켓에 바인딩합니다.

파일 네이밍 규칙:

소켓 핸들러 파일은 Socket.ts(소스) / Socket.js(컴파일됨)로 끝나야 합니다. 예: ChatSocket.ts, NotificationSocket.ts. 파일은 dirname 아래 어떤 깊이에도 배치할 수 있습니다.

설정에서 활성화

extensions'@asapjs/socket'을 추가하고 config 최상위 레벨에 socket 키를 제공하세요:

const config = { // ... 다른 필드 extensions: ['@asapjs/sequelize', '@asapjs/socket'], // Socket.IO 설정 (단일 서버 모드 — Redis 없음) socket: {}, // Socket.IO 설정 (Redis 어댑터를 사용한 다중 서버 모드) // socket: { // redis: { // socket: { // host: process.env.REDIS_HOST || 'localhost', // port: parseInt(process.env.REDIS_PORT || '6379', 10), // }, // }, // }, };

socket.redis가 없거나 undefined이면 모듈은 기본 인메모리 어댑터로 단일 서버 모드로 실행됩니다.

전체 예제

소켓 핸들러 파일

// src/chat/ChatSocket.ts import { createSocket, socketSendAll, socketSendTo } from '@asapjs/socket'; // 연결된 모든 클라이언트의 'chat:message' 이벤트 수신 createSocket( { path: 'chat:message' }, async (socket, { body, user }) => { const payload = { from: user?.id ?? 'anonymous', text: body.text, timestamp: new Date().toISOString(), }; // 모든 클라이언트에게 메시지 브로드캐스트 (발신자 포함) socketSendAll('chat:message', payload); } ); // 'chat:whisper' 수신 — 특정 클라이언트에게 전송 createSocket( { path: 'chat:whisper' }, async (socket, { body, user }) => { const { targetSocketId, text } = body; socketSendTo(targetSocketId, 'chat:whisper', { from: user?.id, text, timestamp: new Date().toISOString(), }); } ); // 'room:join' 수신 — 룸에 참가하고 다른 사람들에게 알림 createSocket( { path: 'room:join' }, async (socket, { body, user }) => { const { roomId } = body; await socket.join(roomId); // 이미 룸에 있는 모든 사람들에게 알림 socketSendTo(roomId, 'room:member_joined', { userId: user?.id, roomId, }); // 참가하는 클라이언트에게 확인 socket.emit('room:joined', { roomId }); } );

애플리케이션 진입점

// src/index.ts import 'reflect-metadata'; import dotenv from 'dotenv'; import { Application } from '@asapjs/core'; dotenv.config(); const config = { name: 'My App', debug: false, port: 3000, basePath: 'api', extensions: ['@asapjs/sequelize', '@asapjs/socket'], auth: { jwt_access_token_secret: process.env.JWT_SECRET, }, db: { /* ... */ }, socket: { redis: { socket: { host: process.env.REDIS_HOST || 'localhost', port: 6379, }, }, }, }; const app = new Application(__dirname, config); app.run();

클라이언트 측 (브라우저 / Node.js)

import { io } from 'socket.io-client'; const socket = io('http://localhost:3000', { auth: { token: 'Bearer <your-jwt-token>' }, }); socket.on('connect', () => { console.log('Connected:', socket.id); }); socket.on('success', (data) => { console.log('Handshake:', data.message); // 'success connected' }); // 룸에 참가 socket.emit('room:join', { roomId: 'room:lobby' }); // 채팅 메시지 전송 socket.emit('chat:message', { text: 'Hello, world!' }); // 브로드캐스트 메시지 수신 socket.on('chat:message', (payload) => { console.log(`[${payload.timestamp}] User ${payload.from}: ${payload.text}`); });

모든 새 연결에서 서버는 연결하는 클라이언트에게 { message: 'success connected' }와 함께 success 이벤트를 내보냅니다.

관련 항목

  • 부트스트랩Application.run(), extensions 배열, SocketOption 설정
  • 인증socket.request.user를 채우는 JWT 미들웨어
  • 로깅 — 소켓 모듈에서 Redis 에러 및 연결 이벤트에 사용하는 로거
Last updated on