결과

내용
- 왼쪽의 플러스 버튼 클릭시 모달창이 뜨고, 귓속말 select에서 유저를 선택할 수 있다
- 내용 입력 후 보내면 내가 보낸 창은 오른쪽에, 받는 채팅은 왼쪽에 출력
- {보내는사람}님에게 귓속말을 보냈습니다. {받는사람}님이 귓속말을 보냈습니다.
- 귓속말 모드일때 placeholder로 현재 귓속말 모드이고, 종료 방법을 알려줌
- esc로 귓속말 모드를 종료할 수 있다
- 귓속말 모드일땐 상대방에게 타이핑 여부를 알리지 않는다

코드
client
나를 제외한 유저만 담기
<select onChange={handleWhisper}>
<option value="" >
귓속말
</option>
{userList
.filter((me) => me !== user?.nick)
.map((user) => (
<option key={user.member_no} value={user}>
{user}
</option>
))}
</select>
귓속말 대상 유저 state에 반영하기
const handleWhisper = (e) => {
setWhisperUser(e.target.value);
};
서버로 보내기
const handleSend = () => {
if (message) {
if (message.length === 0) {
alert("채팅 내용을 입력해주세요.");
return;
}
if (whisperUser) {
socket.emit("/rooms/message", {
chat: message,
roomNo: props.roomNo,
memberNo: user?.member_no,
nick: user?.nick,
type: "SEND_WHISPER",
whisperUser: whisperUser,
});
} else {
socket.emit("/rooms/message", {
chat: message,
roomNo: props.roomNo,
memberNo: user?.member_no,
nick: user?.nick,
type: "USER_TEXT",
});
}
setIsTyping(false);
setMessage("");
}
};
서버에서 받기
socket.on("send whisperUser", (data) => {
const notice = {
...data,
isMyMessage:
data.memberNo === user?.member_no && data?.type === "SEND_WHISPER",
};
setMessages((messages) => [...messages, notice]);
});
return
<div className="talk_chatList">
{isMyMessage && type === "SEND_WHISPER"
? `${whisperUser}님에게 귓속말을 보냈습니다. : ${chat}`
: !isMyMessage && type === "SEND_WHISPER"
? `${nick}님이 귓속말을 보냈습니다. : ${chat}`
: chat}
</div>
귓속말 모드 알려주기
const handleEnterOnMessage = (e) => {
if (e.key === "Enter") {
handleSend();
}
if (e.key === "Escape") {
setWhisperUser("");
}
};
return(
<input
className="chatInput"
placeholder={
whisperUser
? `${whisperUser}님에게 귓속말: * 귓속말 종료시 esc를 누르세요.`
: "Write a message.."
}
onChange={handleMessage}
onKeyUp={handleEnterOnMessage}
value={message}
/>
)
귓속말 모드일땐 상대방에게 타이핑 여부를 알리지 않는다
const handleMessage = (e) => {
e.preventDefault();
setIsTyping(true);
setMessage(e.target.value);
if (!whisperUser) {
socket.emit("/rooms/typing", {
roomNo: props.roomNo,
...user,
isTyping,
type: "SYSTEM_USER_TYPING",
});
}
};
전체코드
socket.on("send whisperUser", (data) => {
console.log("귓속말", data);
const notice = {
...data,
isMyMessage:
data.memberNo === user?.member_no && data?.type === "SEND_WHISPER",
};
setMessages((messages) => [...messages, notice]);
});
const handleSend = () => {
if (message) {
if (message.length === 0) {
alert("채팅 내용을 입력해주세요.");
return;
}
if (whisperUser) {
console.log("서버로 귓속말 보내기 ", whisperUser);
socket.emit("/rooms/message", {
chat: message,
roomNo: props.roomNo,
memberNo: user?.member_no,
nick: user?.nick,
type: "SEND_WHISPER",
whisperUser: whisperUser,
});
} else {
socket.emit("/rooms/message", {
chat: message,
roomNo: props.roomNo,
memberNo: user?.member_no,
nick: user?.nick,
type: "USER_TEXT",
});
}
setIsTyping(false);
setMessage("");
}
};
const handleWhisper = (e) => {
setWhisperUser(e.target.value);
};
return(
<ul key={key} className={isMyMessage ? "talk_myChatWrap" : "otherMsg"}>
{!isMyMessage && (
<div className="imgBox">
<img alt="profileImg" src="/img/profile.jpeg" />
</div>
)}
<li className={isMyMessage ? "talk_myChatWrap" : "talk_chatWrap"}>
<div>
{!isMyMessage && <div className="profileName">{nick}</div>}
<div className="talk_chatList">
{isMyMessage && type === "SEND_WHISPER"
? `${whisperUser}님에게 귓속말을 보냈습니다. : ${chat}`
: !isMyMessage && type === "SEND_WHISPER"
? `${nick}님이 귓속말을 보냈습니다. : ${chat}`
: chat}
</div>
<div className="time">{time}</div>
</div>
</li>
</ul>
<button
className="plusButton"
onClick={() => {
setPlusButton(true);
}}
>
➕
</button>
{plusButton && (
<div className="plusBox" ref={modalRef}>
<ul>
<li>
<select onChange={handleWhisper}>
<option value="">귓속말</option>
{userList
.filter((me) => me !== user?.nick)
.map((user) => (
<option key={user.member_no} value={user}>
{user}
</option>
))}
</select>
</li>
<li>
<label className="imgPlusLabel">
앨범
<input
className="imgPlus"
id="img"
type="file"
accept="image/*"
/>
</label>
</li>
<li>
<label className="filePlusLabel">
파일
<input className="filePlus" id="file" type="file" />
</label>
</li>
</ul>
</div>
)}
<input
className="chatInput"
placeholder={
whisperUser ? `${whisperUser}님에게 귓속말:` : "Write a message.."
}
onChange={handleMessage}
onKeyUp={handleEnterOnMessage}
value={message}
/>
<button className="sendButton" onClick={handleSend}>
Send
</button>
</div>
);
server
socket.on("/rooms/message", (data) => {
const { roomNo, type, whisperUser, nick } = data;
if (type === "USER_TEXT")
sql = `INSERT INTO chat(member_no, room_no, chat, sended) VALUES (:memberNo, :roomNo, :chat, now())`;
else
sql = `INSERT INTO chat(member_no, room_no, chat, sended, type, whisper_user) VALUES (:memberNo, :roomNo, :chat, now(), :type, :whisperUser)`;
_db.qry(sql, data).then(() => {
if (type === "USER_TEXT") io.in(roomNo).emit("/rooms/message", { data });
else
io.to(nick).to(whisperUser).in(roomNo).emit("send whisperUser", data);
});
});