firebase로 채팅 구현하기 2

이명진·2023년 9월 15일
1
post-thumbnail

고민을 했던 채팅 부분..

문제를 극복하고 이제 추가적인 기능 구현을 도전하게 되었다.

구현 할 기능

  • 신규 채팅 알림
  • 5분 이상 대화 없을 경우 대화 자동 종료
  • 채팅창 새로운 창으로 열기

1. 신규 채팅 알림

새로운 채팅이 올경우 채팅 리스트가 아니고 다른 곳에서 작업할때 신규 채팅이 왔음을 알려야 한다.
채팅의 개수를 리턴하는 메소드는 딱히 없었고 직접 제작해야 했다.
이전에 사용했던 query.docChanges() 에서 add와 modify 일 경우 추가 하는 것은 알겠는데.

다른 곳에 갔다가 신규 채팅 알림 버튼을 누를 경우 개수를 0으로 초기화 해줘야 하고
다른곳에 있을 경우 다시 스택을 쌓아야 한다.

0개까지 변환하는 것까지는 구현했는데 header이다 보니 다른 곳을 방문할때마다 재 랜더링이 되어서
읽었던 채팅 개수까지 끌고 오는 난항을 겪에 되었다.

어떻게 구현 하면 좋을 생각하다가. 새로운 채팅의 조건에 대해서 생각해보게 되었다.

새로운 채팅이란 status 상으로 끝나지 않은 경우 (채팅이 종료되면 status를 end 값으로 주었고 신규는 wait으로 설정해두었다 ) + 최근 시간으로 1초전 일 경우 였다.

최근 시간을 위해 고객과 상담측에서 메시지를 보낼때마다 recentTimeStamp를 계속 지금 시간으로 변경해주었다.

조건을 주니 다행히 잘 작동하였다.

다만 문제가 있다면 하나의 채팅방에서 채팅이 올라오는 것도 계속 카운트 하는 것이었다.

이를 위해 방 리스트들을 array로 저장해두고 있었는데 최근 방 리스트 와 지금 리스트가 같다면 카운팅을 안하는 조건이 있지만 새로운 대화도 그냥 카운팅 하는 것도 나을것 같아서 일단 조건을 적용하지 않았다 .

2. 5분 이상 대화 없을 시 자동 종료

여기에서 조금 어렵기도 하였다. 훅을 만들까 생각도 하고 gpt에게 물어보니 useState로 1초마다 계속 세면서 계산 하라고 알려주었다. 성능과 비용측면에서 낭비가 되지 않을까 걱정하며 다른 방법을 모색하게 되었다.

이전 신규 채팅 알림으로 recentTimestamp를 계속 업데이트 해주었기 때문에 이를 다시 활용하기로 하였다.

그리고 status를 5분이 지나면 end로 교체해주어야 하는데 굳이 firebase 까지 가서 데이터를 저장해줘야 할까 생각하게 되었다. 물론 firebase 데이터도 계속 최신화 해주면 좋을것 같지만 다른 컴포넌트에서 계속 listen을 해줘야 하니 비용과 성능측면에서 안좋을것이라 생각하였다.

그래서 status를 firebase에서 1차로 가공하고 5분뒤 자동종료는 리액트 자체에서 검토하고 수정하는 것으로 코드를 짜게 되었다.

대략적인 코드는 아래와 같다.

const changeStatus = (
    status: "wait" | "chatting" | "end",
    recentTimeStamp: any,
    documentId: string
  ) => {
    if (recentTimeStamp === undefined) {
      return "wait";
    }
    const now = new Date();
    const recentTime = new Date(
      recentTimeStamp.seconds * 1000 + recentTimeStamp.nanoseconds / 1000000
    );
    const timeDiff = now.getTime() - recentTime.getTime();

    if (status !== "end" && timeDiff >= 300000) {
      // 메시지 보내고 종료
      addDocumentToSubcollection(
        "상담원",
        "새로운 대화 없이 5분이상이 지나면 자동으로 채팅이 종료됩니다.",
        documentId
      );
      messagesRef.doc(documentId).update({
        status: "end",
      });
      return "end";
    }
    return status;
  };

status를 가공할때 함수를 이용해서 가공을 했는데 5분을 측정하면서 5분이 넘는것은 메시지보내는 함수를 추가로 넣어주었다. 바로바로 적용되지는 않았지만 다행히도 firebase에서 add를 리슨하고 있을때마다 재 랜더링이 되서 새로운 값을 받아올때마다 채팅종료되었다는 문구로 변경이 되었다. 차피 5분 이내에 채팅방도 알아서 채팅종료 할것이라 생각이 들어서 굳이 바로 변경이 되지 않아도 되겠다 생각이 들었다.

3. 채팅창 새로운 창으로 띄우기

채팅이 여러개 들어올수도 있기 때문에 새창으로 여러개 띄울수 있게 해야 한다.
새로운 창으로 띄우는것은 a 태그나 option 으로 ‘_blank”를 넣어주면 되는 것으로 알고는 있었지만

새로 열릴때마다 전체 화면으로 열렸기 때문에 크기를 고정해주어야 했다.

열때는 window.open()함수를 이용하고 닫을때는 window.close() 태그를 이용했다.

참고로 window.open으로 (스크립트로) 열지 않은 이상 window.close명령어는 먹히지 않는다.

window.open 함수는 첫번째 파라미터로 url을 받고 그다음 옵션으로 새창을열지 말지 값을 받고
세번째에서 넓이를 고정해줄수 있었다.

const url = `${mainUrl}chat?&documentId=${documentId}`;
    const width = 400; // 새 창의 너비
    const height = 820; // 새 창의 높이
    const left = (window.innerWidth - width) / 2; // 가로 가운데 정렬
    const top = (window.innerHeight - height) / 2; // 세로 가운데 정렬
    // 새 창 열기
    window.open(
      url,
      "_blank",
      `width=${width},height=${height},left=${left},top=${top},resizable=yes,scrollbars=yes`
    );

코드는 이런식으로 사용 할수 있다.

이렇게 코드를 짜니 클릭할때마다 고정된 크기와 높이로 잘 열렸다.

끄는 버튼도 window.close로 만들었는데 잘 작동되었다.

채팅종료 버튼으로 채팅이 종료되고 firebase status 도 잘 변경이 되었는데. 다만 에러가 있다면

익스플로어 자체의 닫기 버튼을 누르면 그냥 꺼져버린다.. 이를 해결해야 한다…

짧게 작성했지만 거의 많은 시간을 투자해서 고민하고 짠 코드들이다.

계속 하면서 정리를 해나가 보겠다.

profile
프론트엔드 개발자 초보에서 고수까지!

0개의 댓글