socket.io
에 대한 설정도 마쳤으니, 이제 실제로 화면에 메시지를 입력하는 기능들을 만들어보자.
👍 사용되는 기능들
chatbox
: 텍스트를 입력할 입력상자auto - size
: 입력 텍스트 양에 따라 chatBox 자동 사이즈 조절enter submit
: 키보드의 enter 입력에 따른 메시지 제출react-mention
: 해당 채팅방에 존재하는 유저 언급 기능 (@ 사용)⭐️ client
|
└── 🗂 pages
| ├── 🗂 Channel
| └── 🗂 DirectMessage
|
└── 🗂 components
└── 🗂 ChatBox
우선, 메시지를 입력하기 위한 텍스트박스 컴포넌트를 생성하자.
✅ Channel / Direct Message 컴포넌트
<ChatBox
onSubmitForm={onSubmitForm} // 제출
chat={chat} // 데이터
onChangeChat={onChangeChat} // 입력변환
placeholder={`Message #${channel}`} // 기본입력값
...
/>
✅ ChatBox 컴포넌트
const ChatBox: FC<Props> = ({onSubmitForm,chat,onChangeChat,placeholder}) => {
const textareaRef = useRef<HTMLTextAreaElement>(null);
...
return (
<ChatArea>
<Form onSubmit={onSubmitForm}>
<MentionsTextarea
id="editor-chat"
value={chat} // 데이터
onChange={onChangeChat} // 입력변환
placeholder={placeholder} // 기본입력값
inputRef={textareaRef} // 입력 텍스트 감지
allowSuggestionsAboveCursor // 커서 위의 제안 허용
/>
...
<SendButton>제출</SendButton>
</Form>
</ChatArea>
);
};
제출 버튼을 클릭할 수도 있지만, 간편하게 키보드의 Enter를 눌러 제출이 가능하게 설정하자.
const onKeydownChat = useCallback(
(e: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === 'Enter') { // enter키 클릭시
if (!e.shiftKey) { // shiftkey 클릭이 아니라면...
e.preventDefault();
onSubmitForm(e); // 제출
}
}
},
[onSubmitForm]
);
return <MentionsTextarea onKeyPress={onKeydownChat} />
텍스트에 따른 chatBox 자동 사이즈 조절을 설정하자.
useEffect(() => {
if (textareaRef.current) {
autosize(textareaRef.current);
}
}, []);
DM, Channel 컴포넌트에서 해당 채팅방에 속한 사용자 데이터를 받아와 mention 기능에 추가해주자. 그러면 @입력시 언급 가능한 사용자 목록이 출력된다.
✅ Channel / Direct Message 컴포넌트
// Channel
const { data: channelMembersData } = useSWR<IUser[]>(
userData ? `/api/workspaces/${workspace}/channels/${channel}/members` : null,
fetcher
);
return <ChatBox data={memberData}/>
// Direct Message.
const { data: memberData } = useSWR<IUserWithOnline[]>(
userData ? `/api/workspaces/${workspace}/members` : null,
fetcher
);
return <ChatBox data={memberData}/>
✅ ChatBox 컴포넌트
const ChatBox: FC<Props> = ({data}) => {
...
const renderUserSuggestion: (suggestion,search,highlightedDisplay,index,focused)
=> React.ReactNode = useCallback( (member, search, highlightedDisplay, index, focus)
=> {
if (!data) return null;
return (
<EachMention focus={focus}>
<img src={gravatar.url(data[index].email, { s: '20px', d: 'retro' })} alt={data[index].nickname} />
<span>{highlightedDisplay}</span>
</EachMention>
);
},
[data]
);
...
return (
<Mention
appendSpaceOnAdd
trigger="@"
data={data?.map(v => ({ id: v.id, display: v.nickname })) || []}
renderSuggestion={renderUserSuggestion}
/>
);
};
💡 설정에 대한 자세한 내용 : React-mentions 공식문서