미리보기
📁 채팅 기능 미리보기
실시간 소통을 위해서 웹소켓 통신 방법을 사용하였고, 채팅 데이터를 저장하기 위해서 redis를 사용했다.
redis 를 사용하게 된 이유는 기존에 사용하던 mysql 데이터베이스는 상대적으로 실시간 데이터를 가져오기에는 느릴것이라고 판단했고
redis는
- 빠른 읽기/쓰기 성능 제공, 실시간으로 데이터 접근하고 업데이트, 빠른 응답 시간 유지
- Pub/Sub 기능 제공 (특정 집단에 메세지 전달 유용할것이라고 판단)
같은 장점을 가지고 있기때문에 사용하게 되었다.
이번 게시물에서는 채팅 기능 중 프론트 쪽 코드에 대해서 적어보고자 한다.
기능 설명
프론트
Auth 페이지
- 로그인하면 login통신으로 로그인한 유저 email과 socketId를 보내줌.
Message 페이지
- useEffect 내부 소켓 이벤트 핸들러
- "RECEIVE_MESSAGE" : 새로운 메세지 도착시 알람
- "REQUEST_DATA" : 처음 페이지를 로딩할때 현재 유저 id를 백엔드로 보내서 유저 채팅 데이터 받기
- "RESPOND_DATA" : 유저가 속한 모든 채팅 데이터, 서버가 보낸 데이터 받아서 정리
- "BEFORE_DATA" : 유저가 선택한 특정 채팅방의 데이터 받기
- peopleClick : 유저 검색을 통해서 새로운 채팅방을 시작할려고 했을때 사용
- handleSendMessage : 사용자가 채팅 메시지를 보낼 때 사용
- handleChatRoomClick : 사용자가 특정 채팅방을 선택할 때 사용
실제 구현한 코드
Auth 페이지
로그인하면 login통신으로 로그인한 유저 email과 socketId를 보내줌
const onLogin = (data: any) => {
customAxios
.post("/login", {
email: data.email,
password: data.password,
})
.then(async (response) => {
alert("로그인 성공");
dispatch(changeState(true));
// 로그인하면 login통신으로 로그인한 유저 email과 socketId를 보내줌.
socket.emit("login", { email: data.email, socketID: socket.id });
})
.catch((error) => {
// ... 에러 처리
});
};
message 페이지
"RECEIVE_MESSAGE" 이벤트 핸들러
- 목적: 새로운 메시지가 도착했을 때 사용자에게 알림을 제공
- 기능:
- 새 메시지가 도착하면 경고창(alert)으로 사용자에게 알립니다.
"REQUEST_DATA" 이벤트 발송
- 목적: 서버에 현재 사용자의 채팅방 데이터를 요청
- 기능:
- 현재 사용자의 ID를 서버에 전송하여, 해당 사용자의 채팅방 데이터를 요청.
"RESPOND_DATA" 이벤트 핸들러
- 목적: 서버로부터 받은 채팅방 데이터를 처리.
- 기능:
- 받은 데이터를 각 채팅방별로 분류하고, 메시지를 날짜 순으로 정렬.
- 정렬된 데이터를 상태에 저장하여, 채팅방 목록을 업데이트.
"BEFORE_DATA" 이벤트 핸들러
- 목적: 특정 채팅방의 이전 메시지 데이터를 처리.
- 기능:
- 받은 채팅방 ID에 해당하는 이전 메시지 데이터를 상태에 저장합니다.
- 선택된 채팅방 ID를 저장하여 후에 해당 채팅방에서 메세지를 보낼 때 사용.
useEffect(() => {
socket.on("RECEIVE_MESSAGE", (data: ChatMessage[]) => {
alert("새로운 메세지 도착");
});
socket.emit("REQUEST_DATA", {
id: id,
});
socket.on("RESPOND_DATA", (data: ChatMessage[]) => {
let sortedChatRooms: ChatRooms = {};
data.forEach((message) => {
if (!sortedChatRooms[message.roomId]) {
sortedChatRooms[message.roomId] = [];
}
sortedChatRooms[message.roomId].push(message);
});
// 각 채팅방에서 메시지를 날짜 순으로 정렬
Object.keys(sortedChatRooms).forEach((roomId) => {
sortedChatRooms[roomId].sort((a, b) => {
let dateA = new Date(a.date);
let dateB = new Date(b.date);
return dateA.getTime() - dateB.getTime();
});
});
setChatRoomData(sortedChatRooms);
});
socket.on("BEFORE_DATA", (data: string) => {
console.log(data);
setSelectedMessages(chatRoomData[data]);
setSelectedRoomId(data);
});
return () => {
socket.off("RECEIVE_MESSAGE", (data: any) => {});
};
}, [socket]);
peopleClick 함수
- 목적: 사용자가 다른 유저를 검색하여 새로운 채팅방을 시작하려고 할 때 사용.
- 기능: 유저가 검색을 통해서 원하는 유저를 선택하면
- 새로운 채팅방 ID를 생성하여 selectRoomId에 저장
- 선택된 사용자의 ID를 상태에 저장하고, 소켓을 통해 서버에 새 채팅방 시작을 알림(START_CHAT).
const peopleClick = (user_id: number, data: any) => {
setModalIsOpen(false);
const roomId = `chat-${[id, user_id].sort().join("-")}`; // 채팅방 ID 생성
setSelectedRoomId(roomId); // 선택된 채팅방 ID 업데이트
setSelectUser(user_id);
socket.emit("START_CHAT", {
users: [id, user_id],
roomId: roomId,
date: new Date().toISOString(),
});
};
handleSendMessage 함수
- 목적: 사용자가 채팅 메시지를 보낼 때 사용
- 기능:
- 메시지가 유효한 경우에만 처리
- 새 메시지 객체를 생성하고, 소켓을 통해 이를 서버에 전송합니다.
- 전송된 메시지를 로컬 채팅방 데이터에 추가합니다.
const nowDate = new Date();
const time = new Date(nowDate.getTime())
.toISOString()
.replace("T", " ")
.slice(0, -5);
const score = parseInt(
time.replace(" ", "").replace(/-/g, "").replace(/:/g, ""),
10
);
const handleSendMessage = (receiveUser: number) => {
if (!selectedRoomId || message.trim() === "") return;
const newMessage = {
send: id,
receive: receiveUser,
message: message,
date: time,
score: score,
roomId: selectedRoomId,
};
socket.emit("SEND_MESSAGE", newMessage);
setMessage("");
// 선택된 채팅방의 메시지 목록 업데이트
const updatedMessages: ChatMessage[] = [...selectedMessages, newMessage];
setSelectedMessages(updatedMessages);
console.log(newMessage);
// 전역 채팅방 데이터 업데이트
const updatedChatRoomData: { [key: string]: ChatMessage[] } = {
...chatRoomData,
[selectedRoomId]: updatedMessages,
};
setChatRoomData(updatedChatRoomData);
};
handleChatRoomClick 함수
- 목적: 사용자가 특정 채팅방을 선택할 때 사용
- 기능:
- roomId를 받아와서 선택된 채팅방의 메시지를 표시.
- 채팅방 ID를 분석하여, 현재 사용자가 아닌 다른 사용자의 ID를 추출하고 상태에 저장.
const handleChatRoomClick = (roomId: string) => {
setSelectedMessages(chatRoomData[roomId]);
setSelectedRoomId(roomId);
const parts = roomId.split("-");
// 'chat' 문자열과 현재 사용자 ID를 제외하고 나머지 부분을 추출
const otherUserId = parts.find(
(part) => part !== "chat" && part !== id.toString()
);
if (otherUserId == undefined) {
setSelectUser(id);
} else {
console.log(otherUserId);
setSelectUser(parseInt(otherUserId));
}
};
모든 데이터를 가져온 뒤에 각 채팅방마다 가장 마지막으로 보낸 메세지를 가지고 와서 보여주기
{Object.keys(chatRoomData).map((roomKey) => {
const lastMessage =
chatRoomData[roomKey][chatRoomData[roomKey].length - 1];
return (
<div
className="peopleBox hover:bg-slate-200"
key={roomKey}
onClick={() => handleChatRoomClick(roomKey)}
>
<div>
<div className="font-bold pl-3">{roomKey}</div>
<div className="pl-3">{lastMessage.message}</div>
</div>
</div>
);
})}
오른쪽 부분 채팅 화면 : 선택된 채팅방의 데이터를 가져와서 현재 로그인한 유저와 메세지를 보낸 사람의 id가 같으면 파란색으로 표시. 아니면 회색으로 표시
<div className=" messageBox">
{selectedMessages.map((message, i) => (
<div
className={
id == message.send
? "flex justify-end"
: "flex justify-start"
}
key={i}
>
<div
className={id == message.send ? "myChatBox" : "chatBox"}
key={i}
>
{message.message}
</div>
</div>
))}
</div>
<div className=" flex p-2">
<input
className="input"
type="text"
placeholder="전송하려는 메세지를 입력하세요."
value={message}
onChange={(e: any) => setMessage(e.target.value)}
/>
<button
className="sendButton w-10"
onClick={() => {
handleSendMessage(selectUser);
}}
>
전송
</button>
</div>
</div>
'sns 프로젝트' 카테고리의 다른 글
실시간 채팅 기능 수정하기(실시간 채팅 업데이트) (1) | 2024.01.24 |
---|---|
메세지 채팅 기능 구현하기(백엔드 코드, redis 연결 ) (0) | 2024.01.24 |
SearchBar 검색기능 만들기 (1) | 2024.01.23 |
트위터 팔로우 기능 구현하기(React, CSS hover기능) (1) | 2024.01.22 |
Explore 페이지 만들기 (0) | 2024.01.22 |