Firebase Web 채팅앱 만들기 - 10. Realtime Database를 이용한 채팅기능 구현 - 채팅방 리스팅화면

  • 구현할 채팅방 리스팅 화면 입니다.

10-1

1. 코드 작성

1.1 채팅방 리스트 화면을 만들기 위해 다음 코드를 추가

          /**
           * 초기 필드 변수 할당
           */
          FirebaseChat.prototype.init = function(){
              //...생략
              this.dvInputChat = document.getElementById('dvInputChat');
              this.ulRoomList = document.getElementById('ulRoomList');
          }

          /**
           *  로그인 후 세팅
           */
          FirebaseChat.prototype.setLogin = function(user){  //user 파라미터 추
              //...생략
              this.loadRoomList();  //채팅방 목록불러오기
          }

          /**
           *  두번째 탭 채팅방 목록리스트 호출
           */
          FirebaseChat.prototype.loadRoomList = function(){
              this.roomTemplate = document.getElementById('templateRoomList').innerHTML;
              var roomRef = this.database.ref('UserRooms/'+this.auth.currentUser.uid);
              roomRef.off();
              roomRef.orderByChild('timestamp').on('value', this.getRoomList.bind(this))
          }

          /**
           * loadRoomList 에서 데이터를 받아왔을 때
           */
          FirebaseChat.prototype.getRoomList = function(snapshot){
              var arrRoomListHtml = [];
              var cbDisplayRoomList = function(data){
                  var val = data.val();
                  var arrRoomUserName = val.roomUserName.split(this.SPLIT_CHAR);
                  arrRoomUserName.splice(arrRoomUserName.indexOf(this.auth.currentUser.displayName), 1); // 방 제목 타이틀에서는 자신의 이름을 제외합니다.
                  var eachRoomTitle = arrRoomUserName.length > 1 ?
                      arrRoomUserName[0] + " 외 " + (arrRoomUserName.length - 1) + "명" : arrRoomUserName[0] +'님';

                  if(data.key === this.roomId && this.isOpenRoom){ //데이터 키가 현재 방ID와 같고 채팅방이 열려있는 경우에 현재 메세지 상단 제목을 갱신해줍니다.
                      this.spTitle.innerHTML  = eachRoomTitle;
                  }

                  arrRoomListHtml.push( _.template(this.roomTemplate)({roomId : data.key, lastMessage:val.lastMessage,
                      profileImg : val.profileImg, roomTitle : eachRoomTitle, roomUserName:val.roomUserName,
                      roomUserlist: val.roomUserlist,
                      roomType : val.roomType, roomOneVSOneTarget : val.roomOneVSOneTarget,
                      datetime :FirebaseChat.timestampToTimeForRoomList(val.timestamp)}));
              }
              snapshot.forEach(cbDisplayRoomList.bind(this));
              this.ulRoomList.innerHTML = arrRoomListHtml.reverse().join(''); // 역순 정렬

              /**
               *  roomList 클릭 이벤트 적용
               */
              var arrLiList = this.ulRoomList.getElementsByTagName('li')
              var arrLiListLength = arrLiList.length;
              for(var i=0; i < arrLiListLength; i++){
                  arrLiList[i].addEventListener('click', this.onRoomListClick.bind(this));
              }
          }

          /**
           * 룸리스트 클릭
           */
          FirebaseChat.prototype.onRoomListClick = function(event){
              this.aBackBtn.classList.remove('hiddendiv');
              this.aInvite.classList.remove('hiddendiv');
              // 메세지 로드
              this.roomId = event.currentTarget.getAttribute('data-roomId');
              this.roomTitle = event.currentTarget.getAttribute('data-roomTitle');
              this.roomUserlist = event.currentTarget.getAttribute('data-roomUserlist').split(this.SPLIT_CHAR); // 챗방 유저리스트
              this.roomUserName = event.currentTarget.getAttribute('data-roomUserName').split(this.SPLIT_CHAR); // 챗방 유저 이름
              this.openChatRoom(this.roomId, this.roomTitle);

              // 메세지 화면 이동
              this.tabMessageList.click();

          }

          /**
           * RoomList 화면 시간변환
           */
          FirebaseChat.timestampToTimeForRoomList=function(timestamp){
              var date = new Date(timestamp),
                  year = date.getFullYear(),
                  month = date.getMonth()+1,
                  day = date.getDate(),
                  hour = date.getHours(),
                  minute = date.getMinutes();
              var nowDate = new Date(),
                  nowYear = nowDate.getFullYear(),
                  nowMonth = nowDate.getMonth()+1,
                  nowDay = nowDate.getDate(),
                  nowHour = nowDate.getHours(),
                  nowMinute = nowDate.getMinutes();
              var result;
              if(year === nowYear && month === nowMonth && day === nowDay){
                  result = FirebaseChat.pad(hour) +":" + FirebaseChat.pad(minute);
              }else{
                  result = FirebaseChat.pad(year) +"-" + FirebaseChat.pad(month) + "-" + FirebaseChat.pad(day);
              }

              return result;
          }
  • 코드 설명
    • 채팅방 목록을 불러오는 코드
    • 로그인 인증이 완료된 후 수행되는 setLogin 메소드 안에서 loadRoomList메소드를 호출.
    • loadRoomList메소드를 확인을 해보면 유저리스트와 는 다르게 메세지를 받을 때마다 목록을 갱신시켜 주기위해 once메소드가 아닌 on메소드를 사용.
    • getRoomList메소드는 loadRoomList에서 채팅방 목록을 데이터를 받을 때마다 화면을 만드는 메소드. underscore라이브러리의 template 메소드로 화면을 그림.
    • 채팅방 목록 데이터는 채팅방 상단 타이틀을 갱신하기 위한 용도로도 사용.
        var arrRoomUserName = val.roomUserName.split(this.SPLIT_CHAR);
        arrRoomUserName.splice(arrRoomUserName.indexOf(this.auth.currentUser.displayName), 1); 
        var eachRoomTitle = arrRoomUserName.length > 1 ?
                                          arrRoomUserName[0] + " 외 " + (arrRoomUserName.length - 1) + "명" : arrRoomUserName[0] +'님';
          
        if(data.key === this.roomId && this.isOpenRoom){ //현재 메세지 상단 제목을 갱신해준다.
               this.spTitle.innerHTML  = eachRoomTitle;
        } 
          
    
    • 방목록을 클릭을 하면 채팅방을 오픈해야하는데 채팅방을 오픈하기 위해서 필요한 정보를 미리 각각의 채팅방을 감싸는 li태그의 data 속성으로 값을 저장
      <!-- template 채팅방리스트 영역 -->
      <script type="text/template" id="templateRoomList">
          <li id="liRoom<%=roomId %>" data-roomId="<%=roomId %>" data-roomTitle='<%=roomTitle%>' data-roomUserName="<%=roomUserName%>"
              data-roomType="<%=roomType%>" data-roomOneVSOneTarget="<%=roomOneVSOneTarget%>" data-roomUserlist="<%=roomUserlist %>" class="collection-item avatar" >
              <img src="<%=profileImg ? profileImg : 'img/noprofile.png'  %>" alt="" class="circle">
              <span class="title"><%=roomTitle%></span>
              <p><%=lastMessage %></p>
              <a href="#!" class="secondary-content"> <%=datetime %></a>
          </li>
      </script>
    
    
    • 채팅방을 오픈하는데 정보로 사용할 데이터 이외에 roomType이라는 데이터가 있는데, roomType은 ‘ONE_VS_ONE’ 또는 ‘MULTI’ 값입니다. 1대1 채팅방인지 아니면 3명이상이 사용하는 채팅방인지 구분하는 구분값입니다. 유저리스트에서 유저를 클릭할 시에 새로운 채팅방을 만들지 않고 채팅방 리스트 화면을 확인해서 채팅방을 오픈하기 위한 용도.
    • Realtime Database에서는 아쉽게도 내림차순의 정렬이 없습니다.
    • 보통 내림 차순을 위한 데이터를 저장을 하는 방법을 쓰거나 아래와 같이 데이터를 받아서 역순으로 정렬합니다. 최신의 메세지를 받은 채팅방을 가장 상위에 올리기 위해서 아래와 같이 배열 값을 반대로 바꾸었습니다.
      this.ulRoomList.innerHTML = arrRoomListHtml.reverse().join(''); // 역순 정렬
    
    • getRoomList 메소드 하단에는 채팅방을 클릭할 시 방을 오픈하는 동작을 하는 onRoomListClick 메소드가 바인딩
        var arrLiList = this.ulRoomList.getElementsByTagName('li')
        var arrLiListLength = arrLiList.length;
        for(var i=0; i < arrLiListLength; i++){
        arrLiList[i].addEventListener('click', this.onRoomListClick.bind(this));
        }
      
    • onRoomListClick 메소드는 채팅방 목록 화면에 저장해둔 data 속성들을 가지고와 채팅방을 오픈하는데 필요한 변수를 설정하고 채팅방을 오픈합니다.

1.2 유저리스트 화면 클릭시 채팅방 목록을 검색 후 오픈하는 기능을 추가하기위한 코드 수정

/**
   * 유저리스트 클릭
   */
  FirebaseChat.prototype.onUserListClick = function(event){
      this.aBackBtn.classList.remove('hiddendiv'); // 백버튼 노출
      this.aInvite.classList.remove('hiddendiv'); // 초대 버튼 노툴
      var targetUserUid = event.currentTarget.getAttribute('data-targetUserUid');
      var targetUserName = event.currentTarget.getAttribute('data-username');
      var roomListTarget = document.querySelectorAll('[data-roomType="'+ this.ONE_VS_ONE+'"][data-roomOneVSOneTarget="'+ targetUserUid +'"]')[0];
      if(roomListTarget){ // null 이 아니면
          roomListTarget.click();
      }else{
          // 메세지 로드
          this.roomTitle = targetUserName+'님';
          this.roomUserlist = [targetUserUid, this.auth.currentUser.uid]; // 챗방 유저리스트
          this.roomUserName = [targetUserName, this.auth.currentUser.displayName] // 챗방 유저 이름
          this.roomId = this.MAKEID_CHAR + this.auth.currentUser.uid + this.DATETIME_CHAR + FirebaseChat.yyyyMMddHHmmsss();
          this.openChatRoom(this.roomId, this.roomTitle);

      }
  }
  • 코드 설명 :
    • 아래 코드 구문이 채팅방 목록에서 1대1 타입의 채팅방 중에 클릭한 유저의 uid 를 찾는 코드
       var roomListTarget = document.querySelectorAll('[data-roomType="'+ this.ONE_VS_ONE+'"][data-roomOneVSOneTarget="'+ targetUserUid +'"]')[0];
    
    • 해당 타겟이 있다면 채팅방 목록에서 해당항목을 클릭하게되고, 아닌 경우는 새로운 채팅방을 오픈.
    • 유저리스트에서 채팅방을 오픈하는 것과 채팅방리스트에서 채팅방을 오픈하는 것이 완성되었습니다.

1.3 채팅방 화면에서 뒤로 가기 버튼을 눌렀을 시에 동작을 구현을 위한 코드 작성

      /**
       * 유저리스트 클릭
       */
      FirebaseChat.prototype.onUserListClick = function(event){
          this.roomFlag ='tabUserList';
          //...생략
      }

      /**
       * 룸리스트 클릭
       */
      FirebaseChat.prototype.onRoomListClick = function(event){
          this.roomFlag ='tabRoomList';
          //...생략
      }
  • 코드 설명 :
    • 유저리스트에서 채팅방을 오픈하였는지 아니면 채팅방 리스트에서 채팅방을 오픈하였는지 구분 값이 필요
  • 다시 코드 추가
        /**
           * 초기 필드 변수 할당
           */
          FirebaseChat.prototype.init = function(){
              //...생략
              this.ORIGIN_TITLE = "Firebase-Tutorial";
          }

          /**
           * 초기 이벤트 바인딩
           */
          FirebaseChat.prototype.initEvent = function(){
              //...생략
              this.aBackBtn.addEventListener('click', this.onBackBtnClick.bind(this));
          }

          /**
           * 백버튼 클릭
           */
          FirebaseChat.prototype.onBackBtnClick = function(){
              this.isOpenRoom = false;
              this.aBackBtn.classList.add('hiddendiv');
              this.aInvite.classList.add('hiddendiv');
              document.getElementById(this.roomFlag).click();
              this.spTitle.innerText = this.ORIGIN_TITLE;
              this.ulMessageList.innerHTML='';
          }
  • 코드 설명 :
    • 백버튼에 onBackBtnClick 메소드를 연결.
    • onBackBtnClick메소드는 방 오픈 여부 상태값을 false로 변경하고, 백버튼과 초대 버튼을 숨기고, 채팅방 오픈 전 탭으로 되돌아가며, 상단타이틀을 복원하는 코드

챕터완성소스 : 10. Realtime Database를 이용한 채팅기능 구현 - 채팅방 리스팅화면