Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 56 additions & 79 deletions src/components/ChatRoom.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import ChatService from '../services/ChatService';
import webSocketService from '../services/WebSocketService';
import './ChatStyles.css';

// Define API_BACKEND_URL
const API_BACKEND_URL = 'http://localhost:8090';

const ChatRoom = ({ roomId: propRoomId, onLeaveRoom, refreshRooms }) => {
const { roomId: paramRoomId } = useParams();
const roomId = propRoomId || paramRoomId; // 속성으로 받은 값 우선, 없으면 URL 파라미터 사용
Expand Down Expand Up @@ -153,79 +156,59 @@ const ChatRoom = ({ roomId: propRoomId, onLeaveRoom, refreshRooms }) => {

if (!newMessage.trim()) return;

// 먼저 입력 필드 초기화 (UX 향상)
const messageText = newMessage;
setNewMessage('');

// 새 메시지 객체 생성
const newMsg = {
chatMsgId: `local-${Date.now()}`, // 로컬 메시지 ID
userId: userInfo.id,
nickname: userInfo.nickname,
message: messageText,
sendAt: new Date().toISOString()
};
try {
// 현재 사용자 정보 가져오기
const userResponse = await fetch(`${API_BACKEND_URL}/api/user/chatinfo`, {
credentials: 'include'
});
const userData = await userResponse.json();

if (!userData || !userData.username) {
throw new Error('사용자 정보를 가져올 수 없습니다.');
}

// 메시지 목록에 바로 추가 (낙관적 UI 업데이트)
const updatedMessages = [...messages, newMsg];
setMessages(updatedMessages);
// 새 메시지 객체 생성
const newMsg = {
chatMsgId: `local-${Date.now()}`,
userId: userData.username,
nickname: userData.nickname || userData.username,
message: messageText,
sendAt: new Date().toISOString()
};

// 로컬 스토리지에 메시지 저장
saveChatMessages(roomId, updatedMessages);
// 메시지 목록에 바로 추가 (낙관적 UI 업데이트)
setMessages(prev => [...prev, newMsg]);

// 메시지 전송 시도
try {
// 1. 웹소켓으로 먼저 시도
// 웹소켓으로 메시지 전송
const wsSuccess = await webSocketService.sendMessage(
roomIdInt,
messageText,
userInfo.id,
userInfo.nickname
userData.username,
userData.nickname || userData.username
);

console.log('Message sent via WebSocket, success:', wsSuccess);

// 2. 웹소켓 실패 시 REST API로 시도
if (!wsSuccess) {
console.log('WebSocket send failed, trying REST API');
try {
const response = await ChatService.sendMessage(roomId, messageText);
if (!response || !response.success) {
console.error('API message send failed:', response);
throw new Error(response?.message || '메시지 전송에 실패했습니다.');
}
} catch (apiErr) {
console.error('REST API send failed:', apiErr);
// 실패 알림 표시 (기존 메시지는 유지)
const failedMessages = messages.map(msg =>
msg.chatMsgId === newMsg.chatMsgId
? { ...msg, failed: true, failReason: '전송 실패' }
: msg
);
setMessages(failedMessages);

// Update localStorage with failed status
saveChatMessages(roomId, failedMessages);

// 사용자에게 알림
alert(apiErr.message || '메시지 전송에 실패했습니다.');
// 웹소켓 전송 실패 시 REST API로 시도
const response = await ChatService.sendMessage(roomId, messageText);
if (!response.success) {
throw new Error('메시지 전송 실패');
}
}
} catch (err) {
console.error('Error in message send flow:', err);
// 실패 표시 추가
const failedMessages = messages.map(msg =>
msg.chatMsgId === newMsg.chatMsgId
? { ...msg, failed: true, failReason: '전송 실패' }
: msg
console.error('Error sending message:', err);
alert(err.message || '메시지 전송에 실패했습니다.');

// 실패한 메시지 표시
setMessages(prev =>
prev.map(msg =>
msg.chatMsgId === `local-${Date.now()}`
? { ...msg, failed: true, failReason: '전송 실패' }
: msg
)
);
setMessages(failedMessages);

// Update localStorage with failed status
saveChatMessages(roomId, failedMessages);

// 사용자에게 알림
alert('메시지 전송에 실패했습니다.');
}
};

Expand Down Expand Up @@ -299,31 +282,25 @@ const ChatRoom = ({ roomId: propRoomId, onLeaveRoom, refreshRooms }) => {
};

// 현재 로그인한 사용자 정보 가져오기
const getCurrentUser = () => {
// 이 부분은 실제 애플리케이션에서 로그인한 사용자 정보를 가져오는 방식에 따라 다를 수 있음
// OAuth2 사용자 정보 가져오기 (localStorage나 sessionStorage에서)
const userInfoStr = localStorage.getItem('userInfo');
if (userInfoStr) {
try {
const user = JSON.parse(userInfoStr);
setUserInfo({
id: user.id,
nickname: user.nickname || '사용자'
});
} catch (err) {
console.error('Error parsing user info:', err);
// 기본값 설정
setUserInfo({
id: 1, // 테스트용 임시 ID
nickname: '사용자'
});
const getCurrentUser = async () => {
try {
const response = await fetch(`${API_BACKEND_URL}/api/user/chatinfo`, {
credentials: 'include'
});
const userData = await response.json();

if (!userData || !userData.username) {
throw new Error('사용자 정보를 가져올 수 없습니다.');
}
} else {
// 사용자 정보가 없으면 기본값 설정

setUserInfo({
id: 1, // 테스트용 임시 ID
nickname: '사용자'
id: userData.username,
nickname: userData.nickname || userData.username
});
} catch (err) {
console.error('Failed to get current user:', err);
alert('사용자 정보를 가져올 수 없습니다. 로그인 페이지로 이동합니다.');
navigate('/', { replace: true });
}
};

Expand Down
32 changes: 24 additions & 8 deletions src/services/ChatService.js
Original file line number Diff line number Diff line change
Expand Up @@ -286,20 +286,36 @@ const ChatService = {

sendMessage: async (roomId, message) => {
try {
if (!roomId || !message) {
if (!roomId || !message?.trim()) {
throw new Error('채팅방 ID와 메시지 내용은 필수입니다.');
}

if (message.trim() === '') {
throw new Error('메시지 내용은 비어있을 수 없습니다.');
// 사용자 정보 가져오기
const userResponse = await apiClient.get('/api/user/chatinfo');
console.log('User info:', userResponse);

if (!userResponse || !userResponse.username) {
throw new Error('사용자 정보를 찾을 수 없습니다.');
}

console.log('Sending message to room', roomId, ':', message);
const response = await apiClient.post(`/chat/msg/${roomId}`, { message });
console.log('Send message response:', response);
return response;
// HTTP API를 통해 메시지 저장
const response = await apiClient.post(`/chat/msg/${roomId}`, {
message: message.trim()
});

if (!response.success) {
throw new Error('메시지 저장 실패');
}

return {
...response,
userData: {
id: userResponse.username,
nickname: userResponse.nickname || userResponse.username
}
};
} catch (error) {
console.error('Error sending message:', error);
console.error('Message send error:', error);
throw error;
}
},
Expand Down
44 changes: 20 additions & 24 deletions src/services/WebSocketService.js
Original file line number Diff line number Diff line change
Expand Up @@ -320,45 +320,41 @@ class WebSocketService {

// 메시지 전송 메서드
async sendMessage(roomId, message, userId, nickname) {
// Validate inputs
if (!roomId || !message || !userId) {
console.error('Missing required fields for sending message:', { roomId, message, userId });
return false;
}
try {
if (!roomId || !message) {
console.error('Missing required fields for sending message:', { roomId, message });
return false;
}

// Ensure roomId is a number
const roomIdNum = parseInt(roomId);
// 사용자 정보 가져오기
const userResponse = await fetch(`${API_BACKEND_URL}/api/user/chatinfo`, {
credentials: 'include'
});
const userData = await userResponse.json();

try {
// 연결 확인 및 필요시 연결
if (!this.isConnected) {
try {
await this.connect();
} catch (err) {
console.warn('Failed to connect WebSocket for sending message:', err);
return false;
}
if (!userData || !userData.id) {
console.error('Failed to get user info');
return false;
}

const chatMessage = {
type: 'CHAT',
roomId: roomIdNum,
userId: userId,
from: nickname || '사용자',
roomId: parseInt(roomId),
userId: userData.id,
from: userData.nickname,
username: userData.username,
email: userData.email,
message: message,
sendAt: new Date().toISOString()
};

console.log('Sending message:', chatMessage);
console.log('Sending message with user info:', chatMessage);

// WebSocket connected, send the message
if (this.isConnected && this.webSocket && this.webSocket.readyState === WebSocket.OPEN) {
if (this.isConnected && this.webSocket?.readyState === WebSocket.OPEN) {
this.webSocket.send(JSON.stringify(chatMessage));
return true;
} else {
// Queue the message for when the connection is established
this.pendingMessages.push(chatMessage);
console.log('WebSocket not ready, queuing message');
return false;
}
} catch (error) {
Expand Down