본문 바로가기
websocket & webRTC

1. 시그널링 서버

by spare8433 2025. 4. 17.

1. 시그널링 서버

WebRTC에서 P2P 연결을 설정하기 위한 초기 정보 교환을 중계하는 서버입니다. WebRTC는 브라우저 간 직접 통신을 지원하지만, 이 연결을 설정하기 위해서 초기 메타데이터 교환을 위한 시그널링 서버가 필요합니다.





주요 역활

  • 세션 정보(SDP) 교환: 오디오, 비디오, 네트워크 설정 등의 정보
  • ICE 후보(candidate) 교환: 네트워크 연결 정보
  • 사용자 발견 및 세션 관리: 사용자 ID 할당, 방(room) 생성 및 관리
  • 상태 동기화: 연결 상태, 참여자 목록 등의 정보 공유










2. Socket.IO

웹소켓 기반의 실시간 양방향 통신 라이브러리로, 시그널링 서버를 구현하기에 최적화되어 있습니다.





주요 기능

  • 신뢰성 있는 연결: 웹소켓 연결이 불가능할 경우 롱폴링, AJAX 등 대체 방식으로 자동 전환
  • 방(Room) 관리: 효율적인 그룹 통신 지원 및 멀티캐스트 기능
  • 이벤트 기반 통신: 명확한 이벤트 이름으로 메시지 타입 구분
  • 자동 재연결: 네트워크 불안정 시 자동 재연결 처리
  • 바이너리 데이터 지원: 이미지, 오디오 등의 바이너리 데이터 전송 지원










3. MongoDB 어댑터

NoSQL 형식의 데이터베이스인 MongoDB 어댑터를 활용하여 시그널링 서버(Socket.IO)에 연결하여 사용 가능합니다.



Socket.IONoSQL 형식의 데이터베이스인 MongoDB 를 연결하여 상태 관리와 데이터 지속성을 제공하는 어댑터입니다.





주요 기능

  • 분산 시스템 지원: 여러 서버 인스턴스 간 상태 동기화 가능
  • 데이터 유지 관리: 서버 재시작 후에도 연결 정보와 세션 데이터 유지
  • TTL(Time-To-Live) 인덱스: 만료된 데이터 자동 정리 기능
  • 확장성: 대규모 사용자와 방을 효율적으로 관리
  • 메타데이터 저장: 연결 시간, 사용자 정보 등 자동 기록










4. 구현

 

실제 구현시 추천하는 폴더 및 파일 구조

이해를 돕기 위해 예시코드에는 모든 내용을 한번에 작성했지만 실제 사용시 기능별로 분리 해서 사용 가능함



// example
/server
├── index.ts               // 서버 진입점
├── socket.ts              // 소켓 이벤트 정의 특히 path 별로 각각의 소켓 서비스를 구분할 경우 분리하는 것을 추천
├── db.ts                  // MongoDB 연결 및 컬렉션 세팅 (adapter 내용도 db 에서 처리하는 것도 방법)
├── adapter.ts             // MongoDB 어댑터 설정 어뎁터까지
├── env.ts                 // 환경 변수 설정










예시

import { createAdapter } from "@socket.io/mongo-adapter"; // db 연결 관련
import dotenv from "dotenv";
import express from "express";
import { createServer } from "http";
import { MongoClient } from "mongodb";
import { Server } from "socket.io";

dotenv.config(); // 환경 변수 로드

// 1. 서버 설정
const app = express(); // express 서버
const httpServer = createServer(app);

// 2. MongoDB 연결 설정
const DB_URL = process.env.MONGODB_URL;
const DB_NAME = process.env.MONGODB_NAME;

// 3. mongo db 연결
const mongoClient = new MongoClient(DB_URL);
await mongoClient.connect();
const db = mongoClient.db(DB_NAME);

// 4. collection 생성
// 소켓 연결 정보를 저장할 컬렉션 생성
const collection = db.collection("socket_chat");

// TTL(Time To Live) 인덱스 생성 - 오래된 채팅 데이터 자동 삭제
collection.createIndex(
  { createdAt: 1 },
  {
    expireAfterSeconds: 3600, // 3600초(1시간)가 지나면 자동으로 삭제
    background: true, // 백그라운드에서 인덱스 생성
  },
);

// 5. Socket.IO 서버 설정
const chatServer = new Server(httpServer, {
  cors: { origin: ["develop server url"], credentials: true },

   // 서버 경로 설정
   // 클라이언트에서 연결 시 예시: const socket = io('https://서버주소', { path: '/server-chat' });
   // 실제 네트워 요청 URL 예시: https://서버주소/server-chat/socket.io/?EIO=4&transport=websocket
  path: "/server-chat", 

  // MongoDB 어댑터 연결
  adapter: createAdapter(collection, {
    addCreatedAtField: true, // 문서가 저장될 때 자동으로 createdAt 필드를 추가
  }),
});

// 6. 소켓 연결 이벤트 핸들러 설정
chatServer.on("connection", (socket) => {
  console.log(`🔵 클라이언트 연결됨: ${socket.id}`);

  // 📖 소켓 이벤트 핸들러 설정시 마지막 인자로 콜백함수를 받을 수 있으며 실제 서버내에서 실행되는 것이 아닌 클라이언트에서 실행 됩니다.
  // 소켓 이벤트 핸들러의 마지막 인자로 전달되는 콜백함수(done)는 서버에서 실행되지 않고, 서버에서 클라이언트로 다시 전송되어 클라이언트 측에서 실행됩니다. 
  // 이는 Socket.IO의 "확인(acknowledgement)" 메커니즘으로 이벤트 처리 완료를 클라이언트에게 알리고 데이터를 반환하는 용도로 사용됩니다.

  // 연결 해제 이벤트 핸들러
  socket.on("disconnect", (reason) => {
    console.log(`🔴 클라이언트 연결 해제: ${socket.id}, 이유: ${reason}`);
  });

  // 방 입장 이벤트 핸들러
  socket.on("enter_room", (roomName: string, done: (roomName: string) => void) => {
    socket.join("videoChatRoom"); // "videoChatRoom" 라는 room name 을 가지 room 에 입장
    console.log(`📌 ${socket.id} joined room: ${roomName}`);
    done(roomName);
  });

  // 방 퇴장 이벤트 핸들러
  socket.on("leave_room", (roomName: string, done: () => void) => {
    socket.leave(roomName);
    console.log(`User left chat: ${socket.id}`);
    done();
  });
});

// 7. 서버 실행 및 포트 설정
httpServer.listen(3000, () => console.log("✅ 시그널링 서버 실행"));

// 8. 프로세스 종료 시 자원 정리
process.on("SIGINT", async () => {
  console.log("서버를 종료합니다...");
  try {
    await mongoClient.close();
    console.log("MongoDB 연결을 종료했습니다.");
    process.exit(0);
  } catch (error) {
    console.error("서버 종료 중 오류 발생:", error);
    process.exit(1);
  }
});