반응형
해당 글에서는 Socket.io를 기반으로 소켓 서버를 구축하는 방법에 이어서 PM2 클러스터링과 Redis 기반 분산처리 방식에 대해 알아봅니다.
💡 [참고] 아래의 Socket.io와 관련된 글들을 참고하시면 도움이 됩니다
분류 | 링크 |
Socket.io 기반 소켓 서버 구축 방법 -1 : 구성 요소 및 흐름 + React 기반 채팅 화면 구성 | https://adjh54.tistory.com/548 |
Socket.io 기반 소켓 서버 구축 방법 -2 : Room, Namespace 별 소켓 통신 방법 | https://adjh54.tistory.com/549 |
Socket.io 기반 소켓 서버 구축 방법 -3: PM2 클러스터링 + Redis 기반 분산 처리 | https://adjh54.tistory.com/567 |
PM2(Process Manager 2) 이해하고 주요 특징 알아보기 | https://adjh54.tistory.com/551 |
Github : Socket.io 기반 웹 소켓 소스코드 | https://github.com/adjh54ir/blog-codes/tree/main/node-wss-server |
Github : Socket.io를 이용하는 웹 클라이언트 소스코드(채팅) | https://github.com/adjh54ir/blog-codes/tree/main/react-chatting |
1) 주요 구성 요소 : Socket.io, PM2, Redis
1. Socket.io
💡 Socket.io
- 서버-클라이언트 간의 실시간 양방향 통신을 가능하게 하는 라이브러리입니다. 주로 웹 애플리케이션에서 클라이언트와 서버 간의 실시간 데이터 전송을 위해 사용이 됩니다.
- 이는 WebSocket 프로토콜을 사용하지만, 브라우저 호환성을 위해 필요한 경우에는 풀백(fallback) 메커니즘을 사용합니다.
[ 더 알아보기 ]
💡 풀백 메커니즘
- 주어진 기술이나 프로토콜이 작동하지 않을 경우, 대체할 수 있는 다른 방법을 사용하는 것을 의미합니다.
- 예를 들어, Socket.io는 WebSocket을 기본적으로 사용하지만, 만약 클라이언트의 브라우저가 WebSocket을 지원하지 않는다면, HTTP 롱 폴링 등 다른 통신 방식을 사용하여 여전히 실시간 통신을 가능하게 합니다.
💡[참고] 상세한 Socket.io에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.
2. PM2(Process Manager 2)
💡PM2(Process Manager 2)
- Node.js 기반의 애플리케이션의 프로세스 관리를 위한 ‘프로세스 관리자’를 의미합니다.
- 이는 내장된 로드 밸런서(Load Balancer)를 기반으로 애플리케이션이 계속 유지되고, 다운타임 (Downtime) 없이 다시 로드하여 시스템 관리를 용이하게 할 수 있습니다.
💡[참고] 상세한 PM2에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.
3. Redis(Remote Dictionary Server)
💡Redis(Remote Dictionary Server)
- NoSQL 데이터베이스 중 하나이며 오픈소스 소프트웨어입니다. 주요한 특징은 ‘키-값(Key-Value)’ 형태로 데이터를 저장하고 이 데이터를 ‘인-메모리 데이터 저장소’에 저장합니다.
- ‘키-값(Key-Value)’ 형태로 데이터를 저장을 하며 ‘다양한 종류의 값’을 지정 가능합니다. 값으로는 문자열, 리스트, 셋, 정렬된 셋, 해시 등을 지원합니다.
- ‘인 메모리 데이터 저장소’를 사용하기에 서버의 메인 메모리에 모든 데이터를 저장하므로, 디스크 I/O를 거치는 다른 데이터베이스 시스템보다 훨씬 빠른 성능을 보여줍니다.
- 메모리에 저장되는 데이터 베이스는 디스크에 저장하여 휘발성으로 사용되는 데이터를 영구적으로 저장하는 기능을 제공하여 서버가 다운되더라도 데이터를 복구할 수 있습니다.
- 주로 데이터베이스, 캐시, 메시지 브로커 등 다양한 용도로 사용될 수 있습니다.
💡[참고] 상세한 PM2에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.
2) Socket.io + PM2 + Redis를 활용한 구조 : Socket.io Redis Adapter
1. Socket.io Redis Adapter
💡Socket.io Redis Adapter
- Socket.io 기반 소켓 서버 ‘인스턴스 간의 통신을 가능’하게 하는 기능을 제공합니다. 즉, 해당 패키지를 사용하면 여러 Socket.IO 서버 간의 패킷을 브로드 캐스팅할 수 있습니다.
- pub/sub 메커니즘을 사용하여 통신을 수행합니다. 이는 Redis의 pub/sub(발행/구독) 기능을 활용하여 서버 인스턴스 간 실시간 메시지 교환이 이루어집니다.
1.1. Pub/Sub (Publish/Subscribe) 메커니즘
💡Pub/Sub (Publish/Subscribe) 메커니즘
- ‘발행(Publish) / 구독(Subscribe)’ 모델로 불리는 메시지 패턴입니다.
- 발신자(Publisher)는 특정 수신자를 지정하지 않고 메시지를 발행합니다.
- 구독자(Subscribe)는 관심 있는 메시지를 ‘선택적’으로 수신하는 방식입니다.
- 이러한 Pub/Sub 메커니즘 자체를 내장하고 있어서 실시간 메시징 시스템을 쉽게 구현할 수 있습니다. 이 기능을 활용하여 Socket.io 서버 인스턴스 간의 효율적인 메시지 교환이 가능합니다.
1.2. Socket.io Redis Adapter 주요 특징
특징 | 설명 |
중앙 집중식 메시지 브로커 | Redis가 중앙 메시지 브로커 역할을 수행하여 모든 Socket.io 서버 인스턴스는 이 Redis 서버와 연결됩니다. |
pub/sub 메커니즘 | Redis의 pub/sub(발행/구독) 기능을 활용하여 서버 인스턴스 간 실시간 메시지 교환이 이루어집니다. |
확장성 | 새로운 서버 인스턴스를 쉽게 추가할 수 있어, 시스템의 수평적 확장이 가능합니다 |
상태 공유 | 연결된 클라이언트의 정보, 룸 정보 등의 상태 데이터를 Redis에 저장하여 모든 인스턴스가 공유 가능합니다 |
장애 대응 | 한 서버 인스턴스에 문제가 생겨도 다른 인스턴스들이 계속 작동할 수 있어 시스템의 안정성이 향상됩니다 |
2. Socket.io Redis Adapter 처리 과정
💡Socket.io Redis Adapter 처리 과정
1. 초기화
- Socket.io 서버가 실행될 때, Redis Adapter를 초기화합니다. 이 과정에서 Redis 서버와의 연결이 설정됩니다.
2. 클라이언트 연결
- 클라이언트가 Socket.io 서버에 연결되면, 해당 연결 정보가 Redis에 저장됩니다.
3. 메시지 수신
- 클라이언트로부터 메시지가 수신되면, 서버는 이를 처리합니다.
4. 메시지 브로드캐스트
- 메시지를 다른 클라이언트에게 전달해야 할 경우, 서버는 Redis의 pub/sub 메커니즘을 사용하여 메시지를 발행합니다.
5. 메시지 전파
- Redis는 발행된 메시지를 구독 중인 모든 Socket.io 서버 인스턴스에 전달합니다.
6. 클라이언트에게 전달
- 각 서버 인스턴스는 전달받은 메시지를 해당하는 클라이언트에게 전송합니다.
7. 연결 해제
- 클라이언트 연결이 종료되면, Redis에서 해당 연결 정보가 제거됩니다.
3. Socket.io Redis Adapter 구조 활용
3.1. 채팅 앱에서 사용 방법(1단계: 연결 초기화)
💡1단계 : 연결 초기화 (Stage 1 : Connection initialization)
1. 밥이 채팅 앱을 엽니다(Bob opens up the chat app)
- 사용자가 처음으로 애플리케이션을 실행하는 단계입니다.
2. 웹소켓 연결을 설정합니다(establish a web socket)
- 클라이언트와 서버 간의 실시간 양방향 통신을 위한 웹소켓 연결이 이루어집니다.
3. 발행(publication) 연결을 설정합니다(establish a publication connection)
- 메시지를 보내기 위한 채널을 설정합니다. 이를 통해 사용자가 메시지를 보낼 수 있게 됩니다
4. 주제 구독을 위한 연결을 설정합니다(establish connections for topic subscriptions)
- 특정 주제나 채널에 대한 메시지를 받기 위한 구독 설정을 합니다.
5. 모든 멤버와 과거 메시지를 검색합니다(retrieve all members, retrieve hisorical messages)
- 현재 채팅방에 있는 모든 사용자 목록과 이전에 주고받은 메시지 기록을 가져옵니다.
6. 밥이 멤버들과 과거 메시지를 봅니다(Bob sees members and historical message)
- 검색된 정보를 화면에 표시하여 사용자에게 보여줍니다.
7. "member_add" 메시지를 발행하여 새 참가자를 알립니다(publish a “member_add” message to notify a new participant)
- 새로운 사용자가 입장했음을 다른 모든 사용자에게 알리는 메시지를 보냅니다.
8. 앨리스가 밥이 온라인 상태인 것을 봅니다(Alice seens Bob online)
- 다른 사용자(앨리스)의 화면에 밥의 온라인 상태가 표시됩니다.
3.2. 채팅 앱에서 사용 방법(1단계: 메시지 처리)
💡2단계: 메시지 처리 (Stage 2: Message Handing)
1. 앨리스에게 채팅 메시지를 보냅니다(send a chat message to Alice)
- 밥이 앨리스에게 메시지를 작성하고 전송합니다.
2. "messages" 주제로 메시지를 추가하고 발행합니다(add a message, publish to “messages” topic)
- 서버는 받은 메시지를 "messages" 주제로 Redis에 발행합니다. 이를 통해 다른 서버 인스턴스들도 해당 메시지를 받을 수 있습니다.
3. 앨리스가 메시지를 받습니다(Alice receives the message)
- 앨리스의 클라이언트는 서버로부터 새 메시지를 수신하고 화면에 표시합니다.
4. Socket.io Redis Adapter 구조 활용 예시
💡Socket.io Redis Adapter 구조 활용 예시
1. 발행자 클라이언트 생성
- createClient()을 통해서 Redis 서버에 연결할 발행자(Publisher) 클라이언트를 생성합니다. 해당 과정에서는 6379 포트의 Redis 서버에 연결합니다.
2. 구독자 클라이언트 생성
- pubClient를 복제하여 subClient를 생성합니다. 이는 Redis의 발행/구독 기능을 위해 사용됩니다.
3. 클라이언트 연결
- 두 클라이언트(pubClient와 subClient)를 Redis 서버에 비동기적으로 연결합니다.
4. Redis 어댑터 설정
- createAdapter 함수를 사용하여 Redis 어댑터를 생성하고, 이를 Socket.IO 서버의 어댑터로 설정합니다.
5. 서버 리스닝
- Socket.IO 서버가 3000번 포트에서 리스닝을 시작합니다.
import { createClient } from "redis";
import { Server } from "socket.io";
import { createAdapter } from "@socket.io/redis-adapter";
const pubClient = createClient({ url: "redis://localhost:6379" }); // 1. 발행자(Publisher) 클라이언트 생성
const subClient = pubClient.duplicate();// 2. 구독자(Subscriber) 클라이언트 생성
// 3. 클라이언트(pubClient, subClient)를 Redis 서버에 연결합니다.
await Promise.all([
pubClient.connect(),
subClient.connect()
]);
// 4. Redis 어댑터 설정
const io = new Server({
adapter: createAdapter(pubClient, subClient)
});
// 5. 서버 리스닝
io.listen(3000);
5. PM2 + Socket.io + Redis 구조에서 Redis의 역할
💡PM2 + Socket.io + Redis 구조에서 Redis의 역할
- Socket.io 소켓 서버 환경에서 PM2를 이용하여 멀티 프로세스를 지원하는 형태로 구성을 하였을 때, Redis와의 연결도 포함이 되었습니다.
- 실제적으로 아래의 Redis의 역할을 통해서 분산 환경에서 Socket.io 애플리케이션의 확장성, 안정성, 그리고 실시간 성능을 크게 향상합니다.
역할 | 설명 |
메시지 브로커 | 여러 Socket.io 서버 인스턴스 간 실시간 메시지 전달, 일관된 상태 유지 |
세션 저장소 | 클라이언트 세션 정보 저장 및 관리, 서버 간 세션 공유, 재시작 시 세션 유지 |
스케일링 지원 | 여러 서버 노드 간 통신 가능, 수평적 확장성 제공, 부하 분산 및 고가용성 향상 |
실시간 데이터 동기화 | 여러 서버 인스턴스 간 실시간 데이터 동기화, 일관된 상태 유지 |
pub/sub 메커니즘 | Redis의 pub/sub 기능 활용, 효율적인 실시간 이벤트 전파 구현 |
3) Socket.io + PM2 + Redis 구성하기
1. 로컬 Redis 환경 구성
💡로컬 Redis 환경 구성
- 로컬 Redis 환경으로 homebrew를 이용하여서 백그라운드에서 실행하도록 구성하거나 혹은 Docker를 이용하여 환경을 구성할 수 있습니다.
- 해당 환경에서는 간단하게 homebrew의 환경으로 구성합니다.
💡[참고] Redis homebrew 설치 및 docker를 이용한 Redis 구성 방법
1.1. homebrew로 redis를 설치합니다.
💡homebrew로 redis를 설치합니다
- 간단하게 homebrew를 이용하여 설치를 합니다.
- 설치를 하여 실행을 할 때는 foreground 상태에서 내가 서버를 킨 상태인 경우에 수행이 되는 경우와 background 상태에서 PC를 키는 경우 수행되는 경우 두 개의 서비스 실행방법에 대해 알아봅니다.
# redis 설치
$ brew install redis
1.2. 설치를 확인합니다.
# redis의 버전을 확인합니다.
$ redis-cli --version
1.3. 설치된 Redis를 실행합니다.(background)
# background 상태에서 redis를 실행합니다.
$ brew services start redis
# background 상태에서 실행되는
$ brew services info redis
1.4. redis에 접근하여 저장된 키 값을 조회합니다.
# 로컬 redis 서버에 접근합니다.
$ redis-cli
# 저장된 키를 조회합니다.
$ keys *
2. 라이브러리 구성
💡라이브러리 구성
- cors, express, socket.io : 소켓 통신에 이용하는 기본적인 설치 합니다
- @socket.io/redis-adapter : 소켓 통신을 유지하기 위한 redis와의 사용을 위해 설치합니다.
- pm2 : 클러스터 모드, 로드밸런싱을 위해 설치합니다.
- nodemon: 로컬 환경에서 수정사항을 즉시 반영하기 위해 설치합니다.
# 소켓 연결 및 소켓 연결을 유지하기 위한 redis를 설치합니다.
$ npm i cors express socket.io @socket.io/redis-adapter
# 전역 설치 : 프로세스 매니저를 수행하도록 설치합니다.
$ npm i -g pm2
# nodemon을 개발 단계에서 사용하도록 설치합니다.
$ npm i nodemon --save-dev
3. 기존의 구성한 ecosystem.config.js 내에 로컬 연결 환경을 지정합니다.
💡기존의 구성한 ecosystem.config.js 내에 로컬 연결 환경을 지정합니다.
- apps.env.REDIS_HOST와 apps.env.REDIS_HOST_PORT를 각각 지정하였고, 테스트에서는 앱 이름 “app-dev-redis”를 이용합니다.
- 이 설정을 통해 Redis를 사용하는 Socket.io 애플리케이션을 개발 환경에서 클러스터 모드로 실행하기 위한 구성을 마칩니다.
속성 | 값 | 설명 |
name | app-dev-redis | PM2에서 애플리케이션을 식별하는 고유 이름 |
script | ./socket/redis/app.js | 실행할 Node.js 애플리케이션의 경로와 파일명 |
instances | 4 | PM2가 생성할 애플리케이션 인스턴스 수 |
exec_mode | cluster | PM2가 Node.js 클러스터 모듈을 사용하여 실행 |
NODE_ENV | develop | Node.js 애플리케이션의 실행 환경 설정 |
PORT | 5001 | 애플리케이션이 리스닝할 포트 번호 |
REDIS_HOST | localhost | Redis 서버의 호스트 주소 |
REDIS_HOST_PORT | 6379 | Redis 서버의 포트 번호 |
module.exports = {
apps: [
{
name: "app-dev",
script: "./app.js",
instances: 4,
exec_mode: "cluster",
env: {
NODE_ENV: "develop",
PORT: 5001,
REDIS_HOST: "localhost",
REDIS_HOST_PORT: 6379,
},
},
{
name: "app-stg",
script: "./app.js",
instances: 2,
exec_mode: "cluster",
env: {
NODE_ENV: "staging",
PORT: 3001,
REDIS_HOST: "localhost",
REDIS_HOST_PORT: 6379,
},
},
{
name: "app-prd",
script: "./app.js",
instances: "max",
exec_mode: "cluster",
env: {
NODE_ENV: "production",
PORT: 8080,
REDIS_HOST: "localhost",
REDIS_HOST_PORT: 6379,
},
},
{
name: "app-dev-redis",
script: "./socket/redis/app.js",
instances: 4,
exec_mode: "cluster",
env: {
NODE_ENV: "develop",
PORT: 5001,
REDIS_HOST: "localhost",
REDIS_HOST_PORT: 6379,
},
},
],
};
4. Socket.io + Redis 구성을 설정합니다.
💡Socket.io + Redis 구성을 설정합니다.
1. WebSocket Server 설정 : port, cors 지정
- 해당 서버로 접속할 포트를 지정하고, cors()를 통해서 다른 도메인에서의 모든 요청에 대해서 허용합니다.
2. WebSocket Server 설정값 및 socket.io 구성
- HTTP 서버를 생성하고 지정된 포트로 접근 가능하도록 설정합니다.
- transports 속성을 통해서 웹 소켓 연결에 대해서 허용을 하였습니다.
- 생성된 HTTP 서버를 기반으로 소켓 서버를 설정하고 socket.io에 대한 cors에 대해 모두 허용을 설정합니다.
3. Redis 설정합니다.(Pub/Sub Redis 연결 구성)
- 발행자 클라이언트를 Redis와의 접속으로 연결하여 생성하며, 구독자 클라이언트는 이를 복사하여 생성합니다.
- 이 발행자와 구독자를 연결하여 io 서버에 Redis Adapter를 설정합니다.
4. 루트 엔드포인트로 접속 시 health Check 수행
- 루트 경로(”/”)로 접근 시 “Hello world!” 메시지를 출력하고 로그를 출력합니다.
5. 소켓 연결 시작
-"message" 이벤트 발생 시, "message" 이벤트를 등록한 소켓에게 메시지 전달합니다. -"disconnect" 이벤트 발생 시, 소켓 연결을 종료 및 방을 나갑니다.
// import library
const app = require('express')(); // 외부 라이브러리 사용
const cors = require('cors'); // 외부 라이브러리 사용
const http = require('http'); // Node 제공 모듈 사용
const redis = require('redis');
const redisAdapter = require('@socket.io/redis-adapter');
/*
* 1. WebSocket Server 설정 : port, cors 지정
*/
const port = process.env.PORT;
app.set('port', port); // 웹 소켓 포트를 지정합니다
app.use(cors({ origin: '*' })); // cors를 지정해줍니다.
/*
* 2. WebSocket Server 설정값 및 socket.io 구성
*/
const server = http.createServer(app).listen(port); // 설정 값에 따르는 서버 정보 구축
// 설정 값에 따라 socket.io 구축
const io = require('socket.io')(server, {
transports: ['websocket'], // 웹소켓 허용
cors: {
origin: '*',
methods: ['*'], // 모든 메서드 허용
},
});
/*
* 3. Redis 설정합니다.(Pub/Sub Redis 연결 구성)
*/
// 3.1. 발행자(Publisher) 클라이언트 생성
const pubClient = redis.createClient({
host: process.env.REDIS_HOST,
port: process.env.REDIS_HOST_PORT,
});
// 3.2. 구독자(Subscriber) 클라이언트 생성
const subClient = pubClient.duplicate();
// 3.3. Redis 클라이언트 연결
Promise.all([pubClient.connect(), subClient.connect()])
.then(() => io.adapter(redisAdapter.createAdapter(pubClient, subClient))) // Redis adapter 설정
.catch((error) => console.error('Redis connection error:', error));
/*
* 4. 루트 엔드포인트로 접속 시 health Check 수행
*/
app.get('/', (req, res, next) => {
res.send('Hello world!!!!!!'); // 화면상으로 출력
console.log('====== Health Check ======='); // 로그 출력
});
/*
* 5. 소켓 연결 시작
*/
io.on('connection', (socket) => {
console.log('a user connected');
/**
* [ALL] "message" 이벤트 발생 시, "message" 이벤트를 등록한 소켓에게 메시지 전달합니다.
*/
socket.on('message', (msg) => {
io.emit('message', msg);
});
/**
* [ALL] "disconnect" 이벤트 발생 시, 소켓 연결을 종료 및 방을 나갑니다.
*/
socket.on('disconnect', () => {
console.log('a user disconnected');
socket.disconnect();
socket.leave(socket.room);
});
});
💡[참고] 해당 코드는 아래의 Git Repository 내에서 확인이 가능합니다.
5. 실행 스크립트 확인
💡실행 스크립트 확인
- socket/redis/app.js 경로에 파일을 구성하였고, pacakge.json 파일 내에는 “start:redis”라는 명령어로 스크립트를 구성하였습니다.
💡스크립트 설명
- “start:redis”라는 스크립트 명령어를 정의하였습니다.
- 이는 PM2를 사용하여 애플리케이션을 실행하며 사전에 구성한 ecosystem 설정 파일을 “./config/ecosystem.config.js"참조하며, -only app-dev 옵션을 통해서 ‘app-dev’ 애플리케이션 설정말 실행하도록 지정하였습니다.
- 이를 통해서 Redis와 Socket.io를 사용하는 Node.js 애플리케이션이 PM2를 통해 클러스터 모드로 실행됩니다.
"scripts": {
"start:redis": "pm2 start ./config/ecosystem.config.js --only app-dev"
}
4) Socket.io + PM2 + Redis 실행 확인 : 웹 소켓 서버 측
1. 지정한 스크립트 명령어를 수행합니다.
💡지정한 스크립트 명령어를 수행합니다
- 아래와 같이 구성한 내용을 실행하기 위해서 이를 스크립트를 pacakge.json 내에 구성하였습니다.,
"scripts": {
"start:redis": "pm2 start ./socket/redis/app.js ./config/ecosystem.config.js --only app-dev-redis",
"stop": "pm2 delete all",
"restart": "pm2 restart all",
"list": "pm2 list",
"monit": "pm2 monit",
"monit:web": "pm2 monitor"
},
$ npm run start:redis
2. Health Check 확인
💡Health Check 확인
- 지정한 5001로 접속을 수행했을 때, Health Check Endpoint가 수행됨을 확인하였습니다.
3. Redis에서 모니터링을 수행합니다.
💡Redis에서 모니터링을 수행합니다
- 사전에 소스코드 내에서 Redis와의 연결을 수행하여 세션을 관리하기에 Redis의 모니터링을 수행합니다.
# redis 접속
$ redis-cli
# redis monitor
$ monitor
5) Socket.io + PM2 + Redis 실행 확인 : 웹 소켓 클라이언트 측(React 기반 웹 페이지)
1. 클라이언트 채팅방 화면을 구성합니다.
💡클라이언트 채팅방 화면을 구성합니다.
- 구성한 WebSocket에 연결을 하여, 웹 소켓 내에서 채팅을 수행하는 간단한 예시입니다.
1. socket
- 이전에 구성한 웹 소켓에 연결을 하는 과정입니다.
2. useEffect, initSocket()
- 화면이 수행되며 message라는 이벤트를 통해서 클라이언트 간의 메시지를 주고받으며 채팅을 수행하도록 최초 등록을 합니다. 해당 화면이 종료되면 message라는 소켓 연결을 종료합니다.
3. sendMessage()
- 서로 간의 메시지를 주고받을 때 수행하는 메서드입니다.
- 구성한 메시지를 socket.emit()을 통해서 키는 “message”로 Object 데이터를 전달합니다.
4. return() : 화면
실제 클라이언트 간의 화면을 그려주는 화면입니다. input 태그에 텍스트를 입력하고 “Send”버튼을 누르면 메시지가 전달되고, 해당 소켓에 연결된 사용자들 간에 메시지 전달이 이루어집니다.
import React, { useEffect, useState } from 'react';
import { io } from 'socket.io-client';
const socket = io('<http://localhost:5001>', { transports: ['websocket'] }); // socket.io-client를 통해서 "websocket" 연결을 수행합니다.
const SocketIoRedisComponent = () => {
// 상태 관리
const [message, setMessage] = useState<string>(''); // 입력되는 메시지
const [messages, setMessages] = useState<{ id: string; text: string }[]>([]); // 누적되는 메시지 관리
useEffect(() => {
initSocket();
return () => {
socket.off('message');
};
}, []);
/**
* Socket.io의 특정 이벤트가 발생했을 때 특정 작업을 수행하도록 등록합니다.
*/
const initSocket = () => {
// [Socket] "message"가 발생하였을때, 이벤트가 수행합니다.
socket.on('message', (message: { id: string; text: string }) => {
setMessages((prevMessages) => [...prevMessages, message]);
});
};
/**
* 메시지를 전송하는 메서드
* @param e
*/
const sendMessage = (e: React.FormEvent) => {
e.preventDefault();
const newMessage = { id: socket.id, text: message };
// [Socket] 메시지를 전송합니다.
socket.emit('message', newMessage);
setMessage(''); // 전송 이후 입력한 텍스트를 지워줍니다.
};
return (
<div>
<h1 style={{ width: '100%', textAlign: 'center' }}>
Socket.io + Redis + Pm2 기반 채팅방 (WS Session ID : {socket.id})
</h1>
<h1>Chat </h1>
<div
style={{
width: 500,
height: 300,
backgroundColor: '#f5d682',
border: '1px solid red',
}}>
<div style={{ flex: 1, flexDirection: 'column' }}>
{messages.map((item, index) => (
<div key={index}>
<strong>{item.id === socket.id ? 'You' : item.id}:</strong> {item.text}
</div>
))}
</div>
</div>
<form onSubmit={sendMessage}>
<input type='text' value={message} onChange={(e) => setMessage(e.target.value)} />
<button type='submit'>Send</button>
</form>
</div>
);
};
export default SocketIoRedisComponent;
💡[참고] 해당 내용은 아래의 Github Repository에서 확인하실 수 있습니다.
2. 데이터 전달 확인
💡데이터 전달 확인
- 왼쪽 클라이언트와 오른쪽 클라이언트 간의 ‘채팅’ 소켓 통신을 주고받고 있습니다.
- 왼쪽 클라이언트는 “안녕하세요”라고 소켓 통신을 전달하면 오른쪽 클라이언트에 전달이 되고, 오른쪽 클라이언트는 “안녕하세요 저도 반갑습니다”라고 소켓통신을 전달하면 왼쪽 클라이언트에게 전달이 되어서 서로 간의 데이터 통신이 이루어짐을 확인하였습니다.
3. Redis 모니터링 데이터 해석
💡Redis 모니터링 데이터 해석
- Redis 모니터링 시 나타나는 데이터는 Socket.IO가 Redis를 통해 메시지를 발행(publish)하는 과정을 보여줍니다.
1726389861.436939 [0 [::1]:58707] "PUBLISH" "socket.io#/#" "\\x93\\xa6KzIT6y\\x83\\xa4type\\x02\\xa4data\\x92\\xa7message\\x82\\xa2id\\xb4WrvF9VQECt5LoVzjAAAJ\\xa4text\\xaf\\xec\\x95\\x88\\xeb\\x85\\x95\\xed\\x95\\x98\\xec\\x84\\xb8\\xec\\x9a\\x94\\xa3nsp\\xa1/\\x83\\xa5rooms\\x90\\xa6except\\x90\\xa5flags\\x80"
1726389868.710738 [0 [::1]:58710] "PUBLISH" "socket.io#/#" "\\x93\\xa6Put9qC\\x83\\xa4type\\x02\\xa4data\\x92\\xa7message\\x82\\xa2id\\xb4G8pA1REPa6M0F4vaAAAJ\\xa4text\\xd9&\\xec\\x95\\x88\\xeb\\x85\\x95\\xed\\x95\\x98\\xec\\x84\\xb8\\xec\\x9a\\x94 \\xec\\xa0\\x80\\xeb\\x8f\\x84 \\xeb\\xb0\\x98\\xea\\xb0\\x91\\xec\\x8a\\xb5\\xeb\\x8b\\x88\\xeb\\x8b\\xa4\\xa3nsp\\xa1/\\x83\\xa5rooms\\x90\\xa6except\\x90\\xa5flags\\x80"
구성 요소 | 설명 | 예시 |
타임스탬프 | Unix 시간 형식 | 1726389861.436939 |
데이터베이스 번호 | 사용 중인 Redis 데이터베이스 | 0 |
클라이언트 IP:포트 | 연결된 클라이언트 정보 | [::1]:58707 |
Redis 명령어 | 수행된 Redis 작업 | "PUBLISH" |
발행 채널 | Socket.IO 네임스페이스 | "socket.io#/#" |
메시지 데이터 | MessagePack으로 인코딩된 실제 메시지 내용 | "\x93\xa6KzIT6y\x83\xa4type\x02..." |
💡메시지 데이터를 디코딩하면 아래와 같은 구조를 가집니다.
{
"type": 2,
"data": ["message", {
"id": "YlopFUA7QroD8pXbAAAH",
"text": "안녕하세요"
}],
"nsp": "/",
"rooms": [],
"except": [],
"flags": {}
}
필드 | 설명 |
type | 메시지 타입 |
data | 실제 메시지 내용 (id, text 등 포함) |
nsp | 네임스페이스 |
rooms | 메시지가 전송될 방 목록 |
except | 메시지 전송 제외 대상 |
flags | 추가 플래그 정보 |
💡 [참고] 웹 소켓 구성과 웹 클라이언트 구성은 아래의 Github Repository 내에서 확인이 가능합니다
오늘도 감사합니다. 😀
반응형
'Node > 이해하기' 카테고리의 다른 글
[Node] PM2(Process Manager 2) 이해하고 주요 특징 알아보기 (0) | 2024.09.01 |
---|---|
[Node] Socket.io 기반 소켓 서버 구축 방법 -2 : Room, Namespace 별 소켓 통신 방법 (1) | 2024.08.25 |
[Node] Socket.io 기반 소켓 서버 구축 방법 -1 : 구성 요소 및 흐름 + React 기반 채팅 화면 구성 (0) | 2024.08.18 |
[Node] Node 버전 상황에 따라 변경 방법 : 라이브러리 n 활용 (0) | 2024.04.22 |
[Node] 자바스크립트 패키지 매니져(npm/yarn) 이해하기 -1 (2) | 2022.06.26 |