WebSocket을 이용한 실시간 채팅 구현하기

MinJae·2025년 1월 18일
8

실시간 채팅은 구매자와 판매자 간의 원활한 소통을 위한 필수 기능 중 하나입니다. Next.js와 WebSocket을 활용하여 양방향 통신 기반의 실시간 채팅 애플리케이션을 구현했습니다.

1. 프로젝트 구조

두 개의 주요 컴포넌트로 구성됩니다

1.ChatList: 사용자의 채팅 목록을 표시하고 관리합니다.
2.ChatRoom: 실제 채팅이 이루어지는 공간으로, WebSocket 연결을 관리합니다.

2. ChatList 컴포넌트

ChatList 컴포넌트는 사용자의 채팅 목록을 표시하고, 필터링 기능을 제공합니다.

import React, { useState, useEffect } from 'react';
import useAuthStore from '@/stores/authStore';
import { Chat } from '@/types/chat';
import { MessageSquare } from 'lucide-react';
import Link from 'next/link';

export interface ChatListProps {
  initialChats: Chat[];
  selectedChatId: number | null;
  onChatCreated?: (newChat: Chat) => void;
}

type FilterType = 'all' | 'buy' | 'sell';

const ChatList: React.FC<ChatListProps> = ({ initialChats, selectedChatId }) => {
  const { user } = useAuthStore();
  const myName = user?.username;
  const [filteredChats, setFilteredChats] = useState(initialChats);
  const [activeFilter, setActiveFilter] = useState<FilterType>('all');

  // ... (필터링 로직 및 UI 렌더링 코드)

  return (
    <div className="h-full border-r border-[#d9d9d9] bg-white flex flex-col">
      {/* ... (필터 버튼 및 채팅 목록 UI) */}
    </div>
  );
};

export default ChatList;

ChatList.tsx

3. ChatRoom 컴포넌트

ChatRoom 컴포넌트는 WebSocket을 사용하여 실시간 채팅 기능을 구현합니다.

import useAccessToken from '@/hooks/useAccessToken';
import React, { useEffect, useRef, useState, useCallback, useLayoutEffect } from 'react';
import { Send } from 'lucide-react';
import useAuthStore from '@/stores/authStore';
import { ChatRoomProps, Message } from '@/types/chat';

const ChatRoom: React.FC<ChatRoomProps> = ({ chatId }) => {
  const socketRef = useRef<WebSocket | null>(null);
  const [isConnected, setIsConnected] = useState<boolean>(false);
  const [messages, setMessages] = useState<Message[]>([]);
  const [inputMessage, setInputMessage] = useState<string>('');
  
  // ... (WebSocket 연결 및 메시지 처리 로직)

  return (
    <div className="flex flex-col h-full bg-white">
      {/* ... (메시지 표시 및 입력 UI) */}
    </div>
  );
};

export default ChatRoom;

ChatRoom.tsx

4. WebSocket 연결 관리

ChatRoom 컴포넌트에서 WebSocket 연결을 관리하는 방법을 자세히 살펴보겠습니다.

const connect = useCallback(() => {
  if (!accessToken) return;

  socketRef.current = new WebSocket(
    `${process.env.NEXT_PUBLIC_WS_URL}/${chatId}/?token=${encodeURIComponent(accessToken)}`,
  );

  socketRef.current.onopen = () => {
    setIsConnected(true);
    // ... (연결 성공 처리)
  };

  socketRef.current.onmessage = event => {
    // ... (메시지 수신 및 처리)
  };

  socketRef.current.onerror = error => {
    console.error('WebSocket error:', error);
    setIsConnected(false);
  };

  socketRef.current.onclose = () => {
    setIsConnected(false);
    // ... (재연결 로직)
  };
}, [accessToken, chatId]);

useEffect(() => {
  connect();
  return () => {
    disconnect();
  };
}, [connect, disconnect]);

5. 메시지 송수신

메시지 송수신은 다음과 같이 구현됩니다.

const sendMessage = () => {
  if (
    socketRef.current &&
    socketRef.current.readyState === WebSocket.OPEN &&
    inputMessage.trim()
  ) {
    const messageData = {
      message: inputMessage,
      sender: myName,
    };
    socketRef.current.send(JSON.stringify(messageData));
    setInputMessage('');
  }
};

// 메시지 수신 처리 (socketRef.current.onmessage 내부)
const newMessage: Message = {
  id: data.id?.toString() || Date.now().toString(),
  content: data.message || '',
  sender: (typeof data.sender === 'object' ? data.sender.username : data.sender) || 'Unknown',
  createdAt: new Date(data.created_at || Date.now()),
  chatRoom: data.chat_room_id?.toString() || chatId,
};
setMessages(prev => [...prev, newMessage]);

✅ 참고

profile
고양이 간식 사줄려고 개발하는 사람

1개의 댓글

comment-user-thumbnail
2025년 1월 20일

웹소켓.. 실시간 채팅... 메모.....

답글 달기

관련 채용 정보