Today Sangmin Learned
article thumbnail
728x90

글이 너무 길어질 것 같아서 나눠서 포스팅한다. 채널과 DM 목록을 만들었으므로 이제 각 페이지 내부에서 실질적으로 사용되는 채팅을 구현하기 전 먼저 채팅 박스와 내부 스타일링 먼저 구현해본다. 그 이후에 콘솔에 내가 작성한 DM을 띄우는 것까지 해볼 예정이다.

옆에 뜨는 에러들은 추후 리팩토링 예정(500은 서버에러임..)

이전과 마찬가지로, 코드부터 보고 시작한다.

1. DM 페이지 내부

1-1. 프로필(gravatar 사용)

GitHub를 이용해본 사람이라면 이러한 이모티콘을 많이 봤을 것이다. 이 이모티콘은 gravatar에서 제공하는 것으로, 라이브러리를 다운받아서 import해오면 누구나 손쉽게 사용할 수 있다.

gravatar.url(email, options);

이런식으로 불러와서 사용할 수 있다. 이메일은 필수 옵션이다!

<Header>
<img src={gravatar.url(userData.email, { s: '24px', d: 'retro' })} alt={userData.nickname} />
<span>{userData.nickname}</span>
</Header>

이 부분에 적용이 되어있는데, 이메일은 userData.email, 그러니까 로그인 한 유저 데이터의 이메일이 들어가고, 두 번째 옵션에는 s와 d를 설정했는데, 여기서 s는 사이즈이고 d는 디자인이다. 나는 사이즈는 24픽셀, 디자인은 레트로로 선택하였고, 이외에도 다양한 옵션이 있다. 자세한 사항은 How to Create A Gravatar를 참고하기 바란다. 마지막으로 alt는, 문제가 생겨 이미지가 업로드되지 않았을 때 이미지 대신 붙이는 설명 이라고 생각하면 된다.

아무튼, 사람마다 로그인한 이메일이 다 다르기 때문에, 각자 캐릭터를 누를 때마다 다른 아바타가 보인다. 보다보면 귀엽다.

저 계정들 모두 회원가입한 것들이다.

이외의 스타일링은 그냥 전부 슬랙 개발자 도구에서 그대로 가져왔다.

1-2. 채팅창

아직 구현하지 못했으므로 넘어간다. 추후에 포스팅하기로..

1-3. Chatbox (채팅을 작성하는 박스): 컴포넌트

우선, Chatbox에 많은 내용을 입력했을 때 Chatbox를 그만큼 길어지게 하는 것은 autosize 라이브러리를 사용해야 한다.
MentionsTextarea는 역시나 슬랙에서 가져온 스타일을 입히는 것이고, textarea에 대한 스타일링이 적용되어 있다. children을 받아오지 않기 때문에 VFC typing을 하였고, 파라미터로 들어간 chat(실제로 우리가 chatbox에 타이핑할 내용), onSubmitForm(엔터 키를 눌러서 채팅을 보내기), onChangeChat(chatbox에 내용을 적거나 지우거나 할 때 발생되는 이벤트), placeholder(말그대로 placeholder.. 글 적기 전에 불투명하게 나타나는 안내 글자?)는 인터페이스로 타입 정의를 하였다. onSubmitForm과 onChangeChat에 대해서는, 맨 위 코드에도 있지만 다시 써보면

 const [chat, onChangeChat, setChat] = useInput('');
 const onSubmitForm = useCallback(
   (e) => {
     e.preventDefault();
     console.log(chat);
     if (chat?.trim()) { // chat이 있는 경우
       axios
       .post(`/api/workspaces/${workspace}/dms/${id}/chats`, {
         content: chat, // 채팅 값을 content로 등록하고
       })
       .then(() => {
         revalidate();
         setChat('');
       })
       .catch(console.error);
     }
     setChat('');
   },
 [chat],
);


onSubmitForm이 실행이 되면, 우선 SPA를 고려하여 e.preventDefault();가 실행이 될 것이고(새로고침 방지), 우리가 적은 그 채팅 내용이 콘솔에 찍히게 된다. 그 이후 `/api/workspaces/${workspace}/dms/${id}/chats` 로 우리가 적은 chat을 content라는 변수에 담아 보내게 된다. 그리고 이게 성공할 경우, revalidate를 실행하고 Chatbox를 빈칸으로 바꾼다. 전형적인 서버와의 API 통신이다.
axios문이 끝난 뒤에 다시 한 번 Chatbox를 공란으로 바꾸는 setChat('')을 한 이유는, 실패하더라도 Chatbox를 비워야 하기 때문이다. 마지막으로, chat의 값이 변경되므로 useCallback의 deps에 chat을 넣었다.

onChangeChat의 경우 예~전에 포스팅했던 useInput Custom Hook을 사용하였다. 거기에 적은 값이 그대로 e.target.value로 설정이 되게끔 하는 Custom Hook이었다. chat은 그냥 우리가 적는 그 값이 chat이다.

그 다음으로 ref에 대해서는 useRef Hook을 사용했다. 쉽게 얘기하면 HTMLTextAreaElement, 그러니까 textarea의 성질을 갖고 있으며 그 외에 null로 초기화되어있는 textareaRef객체가 있는데, 그 바로 아래에 useEffect를 통해 이 Ref에 성질을 부여하는 것이라고 생각하면 된다. 즉, 현재 MentionsTextarea에 ref 성질이 textareaRef로 부여가 되어있는데, 이 textareaRef는 초기 성질은 null이고 HTMLTextAreaElement 타입을 갖고 있다는 것이다.
이제 바로 아래 useEffect의 if문을 통해 autosize를 적용시켜서 상단 움짤처럼 공백을 넣으면 그만큼 Chatbox가 길어지게 되는 것이다.

마지막으로 onKeydownChat이 있는 이유는, Chatbox 내에서 줄 띄어쓰기나 공백을 만들 수 있게 하기 위해서이다.
엔터키를 눌렀을 때, 쉬프트를 누른 상태가 아니라면 submit의 기능을 수행하게 하는 것이다. 이 말은 곧, 엔터키와 쉬프트키를 동시에 눌렀을 경우 엔터키가 본래 가진 기능(줄바꿈 및 공백 생성)을 수행하게 할 수 있다는 것이다.
정리하면, 이 onKeydownChat useCallback 함수를 통해, 엔터키를 누를 경우 본인이 쓴 채팅 내용이 전송되게 되고(아직 구현은 안했지만) 쉬프트와 엔터키를 동시에 누를 경우 전송이 되는 것이 아니라 공백이 생기게 된다는 것이다.

이제 이렇게 작성을 했으면, 그 아래 SendButton에 대해 알아보자. SendButton은 맨 위 움짤에서 보면 Chatbox 내에 값이 들어갔을 때 종이비행기와 근처의 배경색이 바뀌는 것을 알 수 있다. 여기서도 역시 삼항 연산자를 사용했는데, chat?.trim()을 여부로 판단하였다. 즉 chat(우리가 적는 값)에서 공백을 제거한(.trim()) 값이 있으면 처음에 정의한 'c-button-unstyled ~'만 className이 되고, 값이 없으면 그 뒤에 'c-texty_input ~'이 추가로 className에 들어가게 된다. 그리고 이렇게 바뀌는 className에 따라서 다른 스타일링이 되는 것이다. 뒤에 'c-texty_input ~'이 추가로 className에 들어가게 되면, 버튼을 클릭할 수 없게 된다. 결국, Chatbox에 값이 있냐 없냐를 기준으로 있을 때만 SendButton이 제대로 작동할 수 있게 하고 있는 것이다!

맨 위 움짤을 이렇게 다시 보면, 서버에러가 뜨긴 하지만 콘솔에 우리가 적은 내용이 출력되는 것을 볼 수 있다. 뿐만 아니라 지금까지 적은 내용이 여기 다 반영되어 있다!

다음 번에는, 실제로 socket을 사용하여 실시간 채팅을 하는 기능을 구현한 뒤 포스팅을 할 예정이다.

profile

Today Sangmin Learned

@steadily-worked

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!