Final Project 21일차 - 채팅 마무리 작업 (feat. 에러 해결)

📅 TIL #117




🔔 21일차


오늘은 역대급으로 Error Handling 이슈를 많이 작성한 날이다.. Error Handling 이슈카드만 3개를 작성했다.. ㄷㄷㄷ


그래도 굉~~장히 뿌듯한 날이다. 정말 걱정 많이 했던 오류들을 일단 싹 해결했다. socket 채팅을 구현하면서 정말 끝났다고 생각하면 또 오류 발견하고.. 정말 다 해결 됐다고 생각하고 풀 리퀘스트를 하면 또 오류 발견하고….


정말 미치는 줄 알았지만.. ㅠㅠ 일단 내 로컬에서 발생하는 socket 에러는 다 해결한 것 같다!!


오늘은 프로젝트를 진행하면서 만났던 오류들을 설명하고 해결한 방법을 적어보려고 한다!!




💪 오늘은 어떻게 프로젝트에 기여했나요?


첫번째로 만난 에러는 chat 에서 인피니티 스크롤을 구현한 후 위로 스크롤을 올리면 데이터를 불러오지만, 스크롤 위치가 뜻대로 움직이질 않았다.


인피니티 스크롤 에러


처음에는 위와 같은식으로 스크롤 후 데이터를 잘 받아오지만 스크롤이 제일 아래로 내려갔다. 원인은 useEffect를 통하여 scrollToBottom() 이라는 메소드를 실행해서 스크롤 위치를 렌더링 될 때마다 밑으로 내려가도록 해 주었는데 이게 문제가 되었다.


그래서 채팅을 치거나, 채팅방을 옮길때만 저 메소드를 적용시켜주었다. 그랬더니 이번엔 다른 문제가 생겼다.

인피니티 스크롤 에러2


스크롤이 자동으로 내려가지지도 않는 현상이 일어났다…!!


recruit 페이지에서 아래로 인피니티 스크롤을 할 때는 데이터가 불러와지면서 자동으로 스크롤 위치도 중간쯤으로 이동했는데, 채팅방은 위로 스크롤을 해서 그런지, 아니면 스크롤이 라이브러리이기 때문인지 자동으로 되지는 않았다.


그래서 어쩔 수 없이, 데이터를 불러올 때 마다 스크롤 위치를 계산해서 수동으로 이동해주는 방법을 택했다. 그랬더니 또 문제가 생겼다.


인피니티 스크롤 에러3-끊킴


이 문제는 한 번 맨 밑으로 내려갔다가, 그 다음 내가 수동으로 설정해놓은 위치로 스크롤이 이동을 한다.


이 부분은 채팅을 입력했을 때도 직면했던 문제인데, socket 데이터를 받아오는 텀이 있고, 스크롤 위치를 고정값으로 지정해놓아서 저런 이슈가 발생한 것이라고 생각했다.


그래서 그 다음 생각해낸 방법은 setTimeOut으로 텀을 주고, 고정값이 아닌 전체 스크롤 위치에서 현재 스크롤 위치를 빼주었다.


const onScrollFrame = useCallback(
  values => {
    //생략...
    setTimeout(() => {
      if (scrollbarRef.current) {
        const scrollLocation = scrollbarRef.current.getScrollHeight() - values.scrollHeight;
	scrollbarRef.current.scrollTop(scrollLocation);
      }
    }, 50);
//생략...


getScrollHeight(현재 스크롤 높이) - values.scrollHeight(스크롤바의 스크롤 높이)를 해주었더니 원하는 위치로 이동할 수가 있었다!!


인피니티 스크롤 에러 해결





두번째로 만난 에러에 대한 설명이다.


  • 각 채팅 기록마다 기록한 유저의 프로필 이미지가 있고, 그것을 클릭하면 프로필 모달창이 나온다.


  • 프로필 모달창을 띄우고, 다른 유저의 프로필 모달창도 띄우면 중복해서 모달창이 띄워진다.


  • 그리고 채팅창 밑 쪽에 있는 프로필 이미지를 클릭하면 프로필 모달창이 채팅창 밖으로 벗어나는 이슈가 있었다.


채팅 프로필 모달 오류


채팅 프로필 모달창 위치 오류



에러 핸들링 방법

  • 모달창 중복 해결
const onCloseModal = useCallback((e: globalThis.MouseEvent) => {
  if (!modalRef.current?.contains(e.target as Node)) {
    setShowChatProfileModal(false);
  }
}, []);

useEffect(() => {
  document.addEventListener('click', onCloseModal);
    return () => {
      document.removeEventListener('click', onCloseModal);
    };
}, []);


이런식으로 해당 컴포넌트 이외의 것을 클릭하면 상태를 변경시켜서 모달창을 하나씩 띄울 수 있도록 하였다. 하지만 나는 이렇게 해도 해결을 못했었다.


프로필 이미지에 ref로 접근을 하고 onClick을 완전 이상하게 적어놓았다.


// 잘못된 로직
const onShowChatProfileModal = useCallback(
  (e: React.MouseEvent<HTMLDivElement, MouseEvent>): void => {
    e.preventDefault();
    e.stopPropagation();
    setShowChatProfileModal(true);
  },
  []
);


이게 맨 처음에 적어놨던 onClick 이벤트에 넣어 놓은 함수인데, 이 부분을


const onShowChatProfileModal = useCallback((): void => {
  setShowChatProfileModal(true);
}, []);


이렇게 적어주니 바로 해결이 되었다… 뭐지? 이 문제를 주말에도 시도했다가 포기를 했었다.


우선순위로 중요한 것들을 처리하고 다시 이 문제를 해결하려고 왔는데.. 단순히 저렇게 바꿔주니 해결할 수 있었다.


차이점은 event를 제외시킨 것 뿐이다. 그래서 나는 내가 넣어준 event가 상태 변경이 되지 않게 오히려 막았다는 것을 알 수 있었고, 콘솔로그에서는 true를 찾아볼 수 없었다. 그렇기 때문에 false인 상태에서 모달창이 띄워지는 현상이 발생했다.


이벤트 버블링효과인지 긴가민가하다. 컴포넌트에 적용시킨 div event가 상위 요소인 global event를 막은 것인가? 이 개념에 대해서 많이 부족하다는 것을 깨닫는 계기가 되었다.


결과는 이런식으로 해결이 되었다!!


채팅 프로필 모달 오류 해결




  • 채팅 위치에 따른 모달창 CSS Position


이 부분도 해보지 않은 부분이라 막막했지만, 차근차근 생각해보면서 하다보니 해결할 수 있었다.


먼저 컴포넌트의 위치를 찾고, 일정 위치 아래에 있을 때 마다 Position top을 각각 설정해주면 되는 것이였다.


구글링을 한 결과 getBoundingClientRect() 함수로 컴포넌트의 위치를 찾아낼 수 있다는 것을 알아냈고, 콘솔로그로 찍어가며 어느정도 y값이 되었을 때 Position top을 유동적으로 변경시켜 줄 지를 찾아보았고, y값이 520 이상이면 position을 변경시켜주었다. (getBoundingClientRect()의 y값은 위로 이동할수록 음수이다.)


//결과
const onClickChatItem = useCallback(() => {
  if (modalRef.current) {
    const location = modalRef.current.getBoundingClientRect();
    if (location.y > 520) {
      setChatLocation(`-${String(location.y - 520)}px`);
    }
  }
}, []);


컴포넌트가 520 이상일 때(밑 쪽에 있을 경우) 현재 위치에서 520을 뺀 후에 그 만큼 다시 위로 올려서 화면에 보이도록 해 주었다!!


채팅 프로필 모달창 위치 오류 해결




마지막 에러는 채팅에 인피니티 스크롤 구현을 위해 order라는 상태를 추가한 뒤로 채팅 room을 이동할 때 마다 전에 있었던 room의 채팅 기록들을 다 가지고 room을 이동하는 이슈를 발견하였다.


room 이동 시 생겨나는 에러


useEffect(() => {
  // TODO: room이 바뀌면 인피니티 스크롤을 위한 order 초기화
  setOrder(0);
  setChatBucket([]);
}, [room]);


스크롤을 올리면 order가 1씩 증가되면서 채팅 기록을 30개씩 불러오는 구조이다. 하지만 order의 상태값이 계속 쌓여 있어서 room을 이동해도 전 room의 데이터를 다 끌고 오는 것이였다.


그래서 위와 같은 코드로 room이 바뀌면 order 상태도 초기화를 시켜준다. 그 후 상태를 초기화 시키고 데이터를 불러온다.


그 후에 chat 기록도 초기화를 시키고 그 room에 대한 chat으로 다시 데이터를 채워넣는다.


room을 이동할 때 마다 orderchatBucket(chat 데이터를 담고 있는 배열)을 초기화 시켜주면 각 room에 해당하는 chat 데이터만 뿌려줄 수 있다!


// 트리거에서 chatBucket => order로 변경
useEffect(() => {
  // TODO: 전체 채팅 내용에서 30개씩 불러온다.
  socket?.on("getAllMessages", ({ chats, isEnd }: AllMessagesDataType) => {
    setIsEnd(isEnd);
    setChatBucket([...chats, ...chatBucket]);
  });
}, [order]);

// 이 코드는 변경 사항 없음
useEffect(() => {
  // TODO: room이 바뀌면 room과 다시 연결한다.
  socket?.emit("getAllMessages", { room, order });
  socket?.emit("leaveRoom", room);
  socket?.emit("joinRoom", room);
}, [room, order]);


또 유의할 점은 채팅 데이터를 불러올 때 트리거를 chatBucket => order로 변경하였다는 점이다. order만으로 채팅 데이터를 쌓을 수 있을까 싶었는데 채팅한 후에 chatBucket에 데이터가 추가되고 메세지를 받아오는 부분으로 인해 room 자체가 렌더링 되면서 order가 계속 콘솔을 통해 찍히는 것을 확인할 수 있었다.


그 점을 통해 order만으로 데이터를 30개씩 불러올 수 있었다. chatBucket을 의존성 배열(트리거)에 넣으면 인피니티 스크롤 할 때 데이터가 미친듯이 불려와질 뿐 만 아니라 room을 이동할 때 마다 데이터를 다 끌고 온다.


그리고 마지막으로 인피니티 스크롤을 위하여, order가 0일 경우 스크롤은 자동으로 맨 아래에 가도록 설계를 해 두었다! 스크롤을 맨 위로 올리지 않는 이상 order가 증가할 일은 없게 해두었다!


room 이동 시 생겨나는 에러 해결






🔥 내일은 프로젝트에 기여하기 위해 무엇을 해야 하나요?


배포 후 생기는 채팅 에러 점검, 랜딩 페이지 검토