Skip to content

Commit

Permalink
Feat: 채팅 관련 API 반영 로직 수정 및 웹소켓 기능 추가 (#26) (#28)
Browse files Browse the repository at this point in the history
- 최신 채팅 관련 API 사양에 맞춘 로직 수정
- 실시간 메시지 송수신을 위한 웹소켓 기능 추가
- 웹소켓 이벤트 핸들러 로직 개선 및 안정성 강화
- 채팅 연결 및 메시지 상태 동기화 로직 업데이트
  • Loading branch information
sunglitter committed Dec 5, 2024
1 parent b68c227 commit 4429f0d
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 174 deletions.
86 changes: 34 additions & 52 deletions src/components/common/ChatRoom.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,46 @@
import React, { useEffect, useState, useRef } from 'react';
import styled from 'styled-components';
import { FaRegComment } from 'react-icons/fa';
import {
fetchChatRoomDetails,
fetchChatMessages,
} from '../pages/community/api/chatApi';
import { fetchChatMessages } from '../pages/community/api/chatApi';
import { webSocketService } from '../../utils/webSocket';

interface Message {
senderId: string;
content: string;
timestamp: string | null;
chatRoomId?: string;
type?: 'date';
}

interface Participant {
userId: string;
nickname: string;
type?: string;
}

interface ChatRoomProps {
chatRoomId: string;
chatRoomId: number;
chatMembers: string[];
webSocketService: typeof webSocketService;
isAdmin?: boolean; // 관리자 여부
}

const ChatRoom: React.FC<ChatRoomProps> = ({
chatRoomId,
chatMembers,
webSocketService,
isAdmin = false,
}) => {
const [messages, setMessages] = useState<Message[]>([]);
const [participants, setParticipants] = useState<Participant[]>([]);
const [input, setInput] = useState('');
const currentUserId = 'user-00001'; // Mock 로그인 사용자 ID
const chatBoxRef = useRef<HTMLDivElement>(null);
const [authorId, setAuthorId] = useState<string>('');
const currentUserId = 'user-00001'; // Mock 로그인 사용자 ID

useEffect(() => {
// 채팅방 정보 및 초기 메시지 가져오기
const fetchRoomDetails = async () => {
// 채팅방 초기 메시지 및 채팅 메시지 가져오기
const fetchMessages = async () => {
try {
const { participants, authorNickname } =
await fetchChatRoomDetails(chatRoomId);
setParticipants(participants);

const author = participants.find((p) => p.nickname === authorNickname);
if (author) setAuthorId(author.userId);

const participantNicknames = participants.map((p) => p.nickname);
const fetchedMessages = await fetchChatMessages(chatRoomId);

// 입장 메시지 추가
const joinMessage: Message = {
senderId: 'system',
content: `'${[...participantNicknames].join(', ')}'님께서
채팅방에 입장하셨습니다.`,
content: `${chatMembers
.map((member) => getNicknameDisplay(member))
.join(', ')}님이 입장하셨습니다.`,
timestamp: null, // timestamp 표시하지 않음
};

Expand Down Expand Up @@ -91,45 +76,43 @@ const ChatRoom: React.FC<ChatRoomProps> = ({
timestamp: null,
};

const fetchedMessages = await fetchChatMessages(chatRoomId);
setMessages([joinMessage, groupChatNotice, ...fetchedMessages]);
} catch (error) {
console.error('Failed to fetch chat room details or messages:', error);
console.error('Failed to fetch messages:', error);
}
};

fetchRoomDetails();
}, [chatRoomId]);
fetchMessages();
}, [chatRoomId, chatMembers]);

const getNicknameDisplay = (senderId: string): string => {
if (senderId === 'system') return '';
const participant = participants.find((p) => p.userId === senderId);
if (!participant) return senderId;

if (senderId === authorId) {
return senderId === currentUserId
? '나(방장)'
: `${participant.nickname}(방장)`;
}
return senderId === currentUserId ? '나' : participant.nickname;
return senderId === currentUserId ? '나' : senderId;
};

useEffect(() => {
// WebSocket 연결
const handleIncomingMessage = (data: Message) => {
if (data.chatRoomId === chatRoomId) {
setMessages((prev) => [...prev, data]);
}
setMessages((prev) => [...prev, data]);
};

webSocketService.connect(
handleIncomingMessage,
() => console.log('WebSocket connected'),
() => {
webSocketService.subscribe(
`/sub/message/${chatRoomId}`,
(messageOutput) => {
const data = JSON.parse(messageOutput.body);
handleIncomingMessage(data);
}
);
console.log('WebSocket connected to room');
},
() => console.log('WebSocket disconnected'),
(error) => console.error('WebSocket error:', error)
() => console.error('WebSocket connection error')
);

return () => {
webSocketService.unsubscribe(`/sub/message/${chatRoomId}`);
webSocketService.close();
};
}, [chatRoomId, webSocketService]);
Expand All @@ -145,16 +128,15 @@ const ChatRoom: React.FC<ChatRoomProps> = ({
if (!input.trim()) return;

const message: Message = {
senderId: isAdmin ? 'system' : currentUserId, // 관리자는 시스템 메시지로 보냄
senderId: isAdmin ? 'system' : currentUserId,
content: isAdmin ? `[관리자 메시지] ${input.trim()}` : input.trim(),
chatRoomId,
timestamp: new Date().toISOString(),
};

// WebSocket으로 메시지 전송
webSocketService.send(`/pub/message/${chatRoomId}`, message);

// 메시지 상태 업데이트
webSocketService.send(
`/pub/message/${chatRoomId}`,
JSON.stringify(message)
);
setMessages((prev) => [...prev, message]);
setInput('');
};
Expand Down
74 changes: 26 additions & 48 deletions src/components/pages/admin/ChatRoomManagementPage.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,31 @@
import React, { useState, useEffect } from 'react';
import styled from 'styled-components';
import { useNavigate } from 'react-router-dom';
import { useLocation, useNavigate } from 'react-router-dom';
import ChatRoomModal from '../community/modal/ChatRoomModal'; // 기존 채팅 모달 컴포넌트
import Pagination from '../../common/Pagination'; // 기존 페이지네이션 컴포넌트
import {
fetchChatRooms,
fetchChatRoomDetails,
deleteChatRoom,
} from '../community/api/chatApi';
import { fetchChatRooms } from '../community/api/chatApi';
import { FaBackspace } from 'react-icons/fa';
import { webSocketService } from '../../../utils/webSocket';

const ITEMS_PER_PAGE = 12; // 페이지당 카드 수

const ChatRoomManagementPage = () => {
const navigate = useNavigate();
const location = useLocation();
const [chatRooms, setChatRooms] = useState<
{
chatRoomId: string;
chatRoomTitle: string;
participants: { userId: string; nickname: string }[];
postId: number;
roomName: string;
chatMembers: string[];
}[]
>([]); // 채팅방 데이터
const [currentPage, setCurrentPage] = useState(1);
const [selectedRoom, setSelectedRoom] = useState<{
chatRoomId: string;
chatRoomTitle: string;
participants: { userId: string; nickname: string }[];
} | null>(null); // 선택된 채팅방
const [isModalOpen, setModalOpen] = useState(false);

// URL에서 chatRoomId 동적으로 가져오기
const urlParams = new URLSearchParams(location.search);
const roomId = urlParams.get('roomId'); // URL에서 roomId 가져오기

// 총 페이지 수 계산
const totalPages = Math.ceil(chatRooms.length / ITEMS_PER_PAGE);

Expand All @@ -53,30 +49,13 @@ const ChatRoomManagementPage = () => {
startIndex + ITEMS_PER_PAGE
);

const handleCardClick = async (chatRoomId: string) => {
try {
const roomDetails = await fetchChatRoomDetails(chatRoomId); // API 호출로 채팅방 세부 정보 가져오기
setSelectedRoom(roomDetails);
setModalOpen(true);
} catch (error) {
console.error('채팅방 세부 정보를 가져오는 중 오류 발생:', error);
}
const handleCardClick = () => {
setModalOpen(true); // 모달 열기
};

const handleDeleteRoom = async (chatRoomId: string) => {
if (window.confirm('채팅방을 정말 삭제하시겠습니까?')) {
try {
await deleteChatRoom(chatRoomId); // 채팅방 삭제 API 호출
setChatRooms((prev) =>
prev.filter((room) => room.chatRoomId !== chatRoomId)
);
setModalOpen(false);
alert('채팅방이 삭제되었습니다.');
} catch (error) {
console.error('채팅방 삭제 중 오류 발생:', error);
alert('채팅방 삭제에 실패했습니다.');
}
}
const closeModal = () => {
setModalOpen(false); // 모달 닫기
navigate(-1); // URL 복구
};

return (
Expand All @@ -94,13 +73,10 @@ const ChatRoomManagementPage = () => {
<FormContainer>
<ChatRoomList>
{paginatedChatRooms.map((room) => (
<ChatRoomCard
key={room.chatRoomId}
onClick={() => handleCardClick(room.chatRoomId)}
>
<ChatRoomTitle>{room.chatRoomTitle}</ChatRoomTitle>
<ChatRoomCard key={room.postId} onClick={handleCardClick}>
<ChatRoomTitle>{room.roomName}</ChatRoomTitle>
<Participants>
참여자: {room.participants.map((p) => p.nickname).join(', ')}
참여자: {room.chatMembers.join(', ')}
</Participants>
</ChatRoomCard>
))}
Expand All @@ -110,14 +86,16 @@ const ChatRoomManagementPage = () => {
totalPages={totalPages}
onPageChange={(page) => setCurrentPage(page)}
/>
{isModalOpen && selectedRoom && (
{isModalOpen && roomId && (
<ChatRoomModal
chatRoomId={selectedRoom.chatRoomId}
chatRoomTitle={selectedRoom.chatRoomTitle}
chatRoomId={roomId} // roomId는 URL에서 가져옴
chatRoomTitle={
chatRooms.find((room) => room.postId.toString() === roomId)
?.roomName || ''
}
isOpen={isModalOpen}
onClose={() => setModalOpen(false)}
webSocketService={webSocketService} // WebSocket 서비스 객체 필요 시 추가
onDelete={() => handleDeleteRoom(selectedRoom.chatRoomId)}
onClose={closeModal}
webSocketService={webSocketService}
isAdminPage={true}
/>
)}
Expand Down
Loading

0 comments on commit 4429f0d

Please sign in to comment.