skills/websocket/SKILL.md
실시간 통신 설계 가이드를 실행합니다.
npx skillsauth add excatt/superclaude-plusplus websocketInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
실시간 통신 설계 가이드를 실행합니다.
HTTP (단방향) WebSocket (양방향)
Client → Server Client ⇄ Server
┌────────┐ Handshake ┌────────┐
│ Client │ ────────→ │ Server │
│ │ ←──────── │ │
│ │ Upgrade │ │
│ │ ⇄⇄⇄⇄⇄⇄⇄⇄ │ │
└────────┘ Full-duplex└────────┘
1. 채팅 애플리케이션
2. 실시간 알림
3. 라이브 대시보드
4. 협업 도구 (동시 편집)
5. 게임
6. 주식/암호화폐 시세
const { Server } = require('socket.io');
const io = new Server(httpServer, {
cors: { origin: '*' },
pingTimeout: 60000,
});
// 연결 처리
io.on('connection', (socket) => {
console.log('Connected:', socket.id);
// 인증
const token = socket.handshake.auth.token;
const user = verifyToken(token);
socket.data.user = user;
// 룸 참가
socket.join(`user:${user.id}`);
// 이벤트 수신
socket.on('message', async (data) => {
const message = await saveMessage(data);
// 특정 룸에 브로드캐스트
io.to(`room:${data.roomId}`).emit('message', message);
});
// 타이핑 인디케이터
socket.on('typing', (roomId) => {
socket.to(`room:${roomId}`).emit('typing', {
userId: user.id,
name: user.name,
});
});
// 연결 해제
socket.on('disconnect', (reason) => {
console.log('Disconnected:', socket.id, reason);
});
});
import { io } from 'socket.io-client';
const socket = io('http://localhost:3000', {
auth: { token: 'jwt-token' },
reconnection: true,
reconnectionDelay: 1000,
reconnectionAttempts: 5,
});
// 연결 상태
socket.on('connect', () => {
console.log('Connected:', socket.id);
});
socket.on('disconnect', (reason) => {
console.log('Disconnected:', reason);
});
socket.on('connect_error', (error) => {
console.error('Connection error:', error);
});
// 메시지 수신
socket.on('message', (message) => {
addMessageToUI(message);
});
// 메시지 전송
function sendMessage(roomId, content) {
socket.emit('message', { roomId, content });
}
// 타이핑 상태
function startTyping(roomId) {
socket.emit('typing', roomId);
}
const WebSocket = require('ws');
const wss = new WebSocket.Server({ server: httpServer });
const clients = new Map();
wss.on('connection', (ws, req) => {
const userId = authenticateRequest(req);
clients.set(userId, ws);
ws.on('message', (data) => {
const message = JSON.parse(data);
handleMessage(ws, message);
});
ws.on('close', () => {
clients.delete(userId);
});
// Ping/Pong (연결 유지)
ws.isAlive = true;
ws.on('pong', () => { ws.isAlive = true; });
});
// 연결 상태 체크
setInterval(() => {
wss.clients.forEach((ws) => {
if (!ws.isAlive) return ws.terminate();
ws.isAlive = false;
ws.ping();
});
}, 30000);
// 브로드캐스트
function broadcast(message) {
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify(message));
}
});
}
class WebSocketClient {
constructor(url) {
this.url = url;
this.handlers = new Map();
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.connect();
}
connect() {
this.ws = new WebSocket(this.url);
this.ws.onopen = () => {
console.log('Connected');
this.reconnectAttempts = 0;
};
this.ws.onmessage = (event) => {
const { type, payload } = JSON.parse(event.data);
const handler = this.handlers.get(type);
if (handler) handler(payload);
};
this.ws.onclose = () => {
this.reconnect();
};
this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
}
reconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000);
setTimeout(() => this.connect(), delay);
}
}
on(type, handler) {
this.handlers.set(type, handler);
}
send(type, payload) {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({ type, payload }));
}
}
}
const Redis = require('ioredis');
const pub = new Redis();
const sub = new Redis();
// 메시지 발행
async function publishMessage(channel, message) {
await pub.publish(channel, JSON.stringify(message));
}
// 구독 및 클라이언트 전달
sub.subscribe('chat:messages');
sub.on('message', (channel, message) => {
const data = JSON.parse(message);
// 해당 룸의 클라이언트에게 전달
io.to(`room:${data.roomId}`).emit('message', data);
});
class ConnectionManager {
constructor() {
this.connections = new Map(); // userId -> Set<socket>
}
add(userId, socket) {
if (!this.connections.has(userId)) {
this.connections.set(userId, new Set());
}
this.connections.get(userId).add(socket);
}
remove(userId, socket) {
const sockets = this.connections.get(userId);
if (sockets) {
sockets.delete(socket);
if (sockets.size === 0) {
this.connections.delete(userId);
}
}
}
sendToUser(userId, event, data) {
const sockets = this.connections.get(userId);
if (sockets) {
sockets.forEach(socket => socket.emit(event, data));
}
}
isOnline(userId) {
return this.connections.has(userId);
}
}
// 메시지 형식
{
"type": "MESSAGE_TYPE",
"id": "unique-id",
"timestamp": "2024-01-15T10:00:00Z",
"payload": { /* 데이터 */ }
}
// 타입 정의
const MessageTypes = {
// 채팅
CHAT_MESSAGE: 'chat:message',
CHAT_TYPING: 'chat:typing',
// 알림
NOTIFICATION: 'notification',
// 상태
PRESENCE_UPDATE: 'presence:update',
// 시스템
ERROR: 'system:error',
ACK: 'system:ack',
};
socket.on('message', async (data, callback) => {
try {
const result = await processMessage(data);
callback({ success: true, data: result });
} catch (error) {
callback({ success: false, error: error.message });
socket.emit('error', {
code: error.code,
message: error.message,
});
}
});
// 클라이언트당 연결 수 제한
io.use((socket, next) => {
const userId = socket.data.user.id;
const connections = getConnectionCount(userId);
if (connections >= MAX_CONNECTIONS_PER_USER) {
return next(new Error('Too many connections'));
}
next();
});
const io = new Server(httpServer, {
perMessageDeflate: {
threshold: 1024, // 1KB 이상만 압축
},
});
// 메시지 배칭
const messageBuffer = [];
const BATCH_INTERVAL = 100; // ms
function queueMessage(message) {
messageBuffer.push(message);
}
setInterval(() => {
if (messageBuffer.length > 0) {
io.emit('messages', messageBuffer);
messageBuffer.length = 0;
}
}, BATCH_INTERVAL);
## WebSocket Design
### Architecture
[Client] ⇄ [Load Balancer] ⇄ [WS Server 1] ⇄ [WS Server 2] ↕ [Redis Pub/Sub]
### Events
| 이벤트 | 방향 | 설명 |
|--------|------|------|
| message | 양방향 | 채팅 메시지 |
| typing | C→S | 타이핑 상태 |
### Implementation
```javascript
// 서버/클라이언트 코드
---
요청에 맞는 WebSocket 구현을 설계하세요.
testing
사용자 계획을 기존 도메인 모델에 대해 stress-test하는 인터뷰 세션. 용어를 날카롭게 다듬고, 결정이 굳어질 때마다 CONTEXT.md(도메인 어휘 사전)와 ADR을 인라인으로 갱신한다. 새 기능 요구사항 탐색은 `/brainstorm`을, 기존 도메인 모델·용어와의 정합성 점검은 이 스킬을 사용한다.
development
# Excel (XLSX) Spreadsheet Skill Claude Code supports comprehensive spreadsheet operations through the **xlsx** skill, enabling creation, editing, and analysis of Excel files (.xlsx, .xlsm, .csv, .tsv). ## Trigger - When user needs Excel spreadsheet creation or editing - Financial modeling or data analysis required - Spreadsheet formulas and calculations needed - Data import from CSV/TSV files ## Core Capabilities **Primary functions include:** - Creating new spreadsheets with formulas and f
tools
Generate structured implementation workflows from PRDs and feature requirements
tools
# Web Application Testing This toolkit enables testing of local web applications through native Python Playwright scripts, supporting frontend verification, UI debugging, screenshot capture, and browser log inspection. ## Trigger - When user requests testing of web applications - Frontend verification and UI debugging needed - Screenshot capture or visual validation required - Browser console log inspection needed ## Capabilities **Available Helper Scripts:** - `scripts/with_server.py` - Man