Use Cases

Comunicacion en tiempo real con WebSocket y Claude Code

Aprenda sobre comunicacion en tiempo real con WebSocket usando Claude Code. Incluye ejemplos practicos de codigo.

リアルタイム通信開発でClaude Codeを活用する

チャット、通知、共同Editなど、リアルタイム通信が必要なアプリケーションは増えています。Claude Codeはサーバーサイドとクライアントサイドの両方を理解した上で、一貫した実装を支援します。

Socket.IOによるチャット機能の実装

> Socket.IOを使ったリアルタイムチャットのサーバーを作成して。
> ルーム機能、タイピングインジケーター、既読機能を実装して。

サーバーサイド

import express from 'express';
import { createServer } from 'http';
import { Server, Socket } from 'socket.io';

const app = express();
const server = createServer(app);
const io = new Server(server, {
  cors: { origin: 'http://localhost:5173' },
});

interface ChatMessage {
  id: string;
  roomId: string;
  userId: string;
  content: string;
  timestamp: Date;
}

const rooms = new Map<string, Set<string>>();

io.on('connection', (socket: Socket) => {
  const userId = socket.handshake.auth.userId;
  console.log(`User connected: ${userId}`);

  socket.on('join-room', (roomId: string) => {
    socket.join(roomId);
    if (!rooms.has(roomId)) rooms.set(roomId, new Set());
    rooms.get(roomId)!.add(userId);

    socket.to(roomId).emit('user-joined', {
      userId,
      members: Array.from(rooms.get(roomId)!),
    });
  });

  socket.on('send-message', async (data: { roomId: string; content: string }) => {
    const message: ChatMessage = {
      id: crypto.randomUUID(),
      roomId: data.roomId,
      userId,
      content: data.content,
      timestamp: new Date(),
    };

    // Save to DB
    await saveMessage(message);

    io.to(data.roomId).emit('new-message', message);
  });

  socket.on('typing-start', (roomId: string) => {
    socket.to(roomId).emit('user-typing', { userId, isTyping: true });
  });

  socket.on('typing-stop', (roomId: string) => {
    socket.to(roomId).emit('user-typing', { userId, isTyping: false });
  });

  socket.on('mark-read', (data: { roomId: string; messageId: string }) => {
    socket.to(data.roomId).emit('message-read', {
      userId,
      messageId: data.messageId,
    });
  });

  socket.on('disconnect', () => {
    rooms.forEach((members, roomId) => {
      if (members.delete(userId)) {
        io.to(roomId).emit('user-left', { userId });
      }
    });
  });
});

server.listen(3000, () => console.log('Server running on port 3000'));

クライアントサイド(React)

import { useEffect, useState, useCallback } from 'react';
import { io, Socket } from 'socket.io-client';

let socket: Socket;

export function useChat(roomId: string, userId: string) {
  const [messages, setMessages] = useState<ChatMessage[]>([]);
  const [typingUsers, setTypingUsers] = useState<Set<string>>(new Set());

  useEffect(() => {
    socket = io('http://localhost:3000', { auth: { userId } });
    socket.emit('join-room', roomId);

    socket.on('new-message', (message: ChatMessage) => {
      setMessages(prev => [...prev, message]);
    });

    socket.on('user-typing', ({ userId: uid, isTyping }) => {
      setTypingUsers(prev => {
        const next = new Set(prev);
        isTyping ? next.add(uid) : next.delete(uid);
        return next;
      });
    });

    return () => { socket.disconnect(); };
  }, [roomId, userId]);

  const sendMessage = useCallback((content: string) => {
    socket.emit('send-message', { roomId, content });
  }, [roomId]);

  const startTyping = useCallback(() => {
    socket.emit('typing-start', roomId);
  }, [roomId]);

  const stopTyping = useCallback(() => {
    socket.emit('typing-stop', roomId);
  }, [roomId]);

  return { messages, typingUsers, sendMessage, startTyping, stopTyping };
}

Server-Sent Events(SSE)による通知

WebSocketほどの双方向性が不要な場合、SSEが軽量な選択肢です。

// server
app.get('/api/notifications/stream', (req, res) => {
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    Connection: 'keep-alive',
  });

  const userId = req.query.userId as string;

  const sendEvent = (data: object) => {
    res.write(`data: ${JSON.stringify(data)}\n\n`);
  };

  // 新しい通知があれば送信
  const unsubscribe = notificationBus.subscribe(userId, sendEvent);

  req.on('close', () => {
    unsubscribe();
    res.end();
  });
});
// client
function useNotifications(userId: string) {
  const [notifications, setNotifications] = useState<Notification[]>([]);

  useEffect(() => {
    const eventSource = new EventSource(
      `/api/notifications/stream?userId=${userId}`
    );

    eventSource.onmessage = (event) => {
      const data = JSON.parse(event.data);
      setNotifications(prev => [data, ...prev]);
    };

    return () => eventSource.close();
  }, [userId]);

  return notifications;
}

接続の安定性を高める

本番環境では、再接続ロジックやハートビートの実装が重要です。

const socket = io('http://localhost:3000', {
  auth: { userId },
  reconnection: true,
  reconnectionAttempts: 10,
  reconnectionDelay: 1000,
  reconnectionDelayMax: 5000,
  timeout: 20000,
});

socket.on('connect_error', (err) => {
  console.error('Connection error:', err.message);
});

socket.on('reconnect', (attempt) => {
  console.log(`Reconnected after ${attempt} attempts`);
  // Reconnect後にルームに再参加
  socket.emit('join-room', currentRoomId);
});

Summary

Claude Codeを活用すれば、WebSocketやSSEを使ったリアルタイム通信機能をサーバー・クライアント両方一貫して実装できます。プロンプトの書き方次第でコードの質が大きく変わるため、プロンプトテクニックを身につけておくことが重要です。個人開発でチャット機能を実装する際は個人開発を爆速にする方法も参考にしてください。

詳しくはAnthropic公式ドキュメントSocket.IO公式ドキュメントをご覧ください。

#Claude Code #WebSocket #real-time #Socket.IO #Node.js