이미지 가로 싱크가 안맞는데, 기존에 개발한 프로젝트에선 프로필 사진이 보이는 채팅창을 구현했는데, 보안상 잘라냈기 때문이다. (기존 프로젝트 실행화면임)
이번에 내가 포스팅할 채팅창 기능은 다음과 같다.
3. 새로운 채팅 전송 시 '나'가 보낸 채팅으로 간주, 우측 정렬
4. 채팅 입력창에서 "shift + enter" 입력 시 개행되며, "enter"만 입력했을 때는 전송.
3. 새로운 채팅 전송 시 '나'가 보낸 채팅으로 간주, 우측 정렬
Home.tsx의 textarea에 onKeyUp 이벤트와 onKeyDown 이벤트를 추가하고, 거기에 우리가 만들 함수를 넣어준다.
그 전에, onKeyUp과 onKeyDown 이벤트가 뭔지부터 설명하도록 하겠다.
onKeyUp은 키를 놨을 때 발생하는 이벤트이고,
onKeyDown은 키를 누르기 시작할 때 발생하는 이벤트이다. 이때, onKeyDown은 키가 눌려있는 동안 여러 번 발생할 수도 있다.
우리는 여기에 각각 handleKeyUp과 handleKeyDown이라는 함수를 넣어줄 것이다.
// Home.tsx
function Home() {
...
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {};
const handleKeyUp = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {};
return (
...
<textarea
className="w-full h-12 mx-3 focus:outline-none resize-none flex-1"
style={{ resize: "none" }}
placeholder="메세지를 입력해주세요."
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
/>
<button>
입력
</button>
...
)
}
이제, textarea에서 글을 입력했을 때, '나'가 보낸 것으로 간주할 수 있는 함수를 만들어 줄 것이다.
함수 이름은 handleSendMessage로 하겠다.
우선, 문자열의 앞뒤에 있는 공백문자를 제거하기 위해, handleSendMessage 안에 input.trim() 을 통해 문자열 공백 확인 작업을 한다.
예를 들어, input에 들어오는 값이 " Hello "라면, input.trim()은 "Hello"를 반환한다. 만약 input이 " "처럼 공백 문자로만 이루어져 있다면, input.trim()은 "" (빈 문자열)을 반환한다.
if(input.trim()) 조건문 안에 이전에 만들었던 setMessages useState 함수에 이전 메세지 배열이 비어있는지 확인하고, 비어있지 않으면 마지막 메세지의 id를 가져오는 코드를 작성하여 textarea에 입력된 값이 위와 같은 조건으로 저장될 수 있도록 한다.
// Home.tsx
function Home() {
...
//메세지 전송 핸들러
const handleSendMessage = () => {
if (input.trim()) {
setMessages((prevMessages) => {
//이전 메세지 배열이 비어 있는지 확인하고, 비어 있지 않으면 마지막 메세지의 id를 가져옴
const newId =
prevMessages.length > 0
? prevMessages[prevMessages.length - 1].id + 1
: 1;
return [
...prevMessages,
{
id: newId,
nickname: "아무게",
profile: People,
chatting: input,
time: new Date().toLocaleTimeString(),
isMe: true,
},
];
});
setInput("");
}
};
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {};
const handleKeyUp = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {};
return (
...
<textarea
className="w-full h-12 mx-3 focus:outline-none resize-none flex-1"
style={{ resize: "none" }}
placeholder="메세지를 입력해주세요."
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
/>
<button onClick={handleSendMessage}>
입력
</button>
...
)
}
코드에 대한 설명을 보충하자면,
setMessages에서 return 부분에 이전에 전송된 메세지가 있을 경우, 이를 불러와서 저장시키고,
id 부분에 새로운 newId 상태변수를 통해 얻은 id를 저장하고,
isMe: true를 통해, textarea에 작성한 메세지가 내가 보낸 메세지로 저장되도록 한다.
(nickname의 경우, 이전 메세지에서 isMe: true인 데이터의 nickname을 불러와야 하지만, 지금은 예시코드이기 때문에 "아무개"로 저장했다.)
채팅 내용은 chatting에 저장한다. (input 값이 textarea로 입력된 값이다.)
마지막으로, "입력" 버튼을 눌렀을 때, messages 상태변수에 저장될 수 있도록, button의 onClick 이벤트에 handleSendMessage 함수를 지정해준다.
4. 채팅 입력창에서 "shift + enter" 입력 시 개행되며, "enter"만 입력했을 때는 전송.
다음으로, 앞에서 만든 handleSendMessage 함수를 handleKeyDown과 handleKeyUp 함수에 적용시켜서 textarea에서 입력한 내용이 messages 상태변수에 저장될 수 있도록 한다.
단, 우리가 원하는 조건인 "enter"를 클릭했을 때만 값이 저장될 수 있도록 하며,
"shift+enter"를 클릭했을 땐, textarea에서 개행될 수 있도록 한다.
// Home.tsx
function Home() {
...
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
handleSendMessage();
}
};
const handleKeyUp = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();
handleSendMessage();
}
};
return (
...
<textarea
className="w-full h-12 mx-3 focus:outline-none resize-none flex-1"
style={{ resize: "none" }}
placeholder="메세지를 입력해주세요."
onKeyDown={handleKeyDown}
onKeyUp={handleKeyUp}
/>
<button onClick={handleSendMessage}>
입력
</button>
...
)
}
위 코드에 대한 보충설명을 좀 더 하자면,
오직, "Enter" 키를 눌렀을 때만 입력 내용을 제출(submit) 할 수 있도록,
"if(e.key === "Enter" && !e.shiftKey)" 조건절을 추가하고, 그 안에서 e.preventDefault();를 통해 Enter 키를 눌렀을 때 textarea에 새 줄이 추가되는 기본 동작을 막아주고, 이를 통해 메시지가 전송될 수 있도록 하며,
handleSendMessage() 함수를 작동시켜서 messages 상태변수에 채팅 내용이 저장될 수 있도록 한다.
이번 포스팅은 여기서 마치고, 다음 포스팅을 통해
5. 채팅 데이터에서 채팅 내용이 개행됐을 경우, <br/>처리를 통해 화면에서도 개행 처리 해줌.
6. 조합문자(아직 완성되지 않은 문자, 예를 들어 한글, 중국어, 일본어 등)을 사용할 경우, 채팅 입력 시 마지막 글자가 중복 처리되는 것을 막기 위해, 조합상태를 감지하는 로직 구현.
기능을 구현하는 포스팅을 하도록 하겠다.
끝..!
'개발 > React' 카테고리의 다른 글
[React, Typescript] 채팅 프론트엔드 구현 (3편) (0) | 2024.08.01 |
---|---|
[React, Typescript] 채팅 프론트엔드 구현 (1편) (0) | 2024.07.18 |
[React] 프로젝트 초기 세팅 (Typescript, ESLint, Prettier, Tailwind CSS) (0) | 2024.07.11 |
3.7 State Practice (0) | 2023.06.11 |
3.4 State Functions (0) | 2023.06.10 |