[VueJS] 채팅 어플리케이션 시리즈2

shw·2021년 4월 28일
0

VueJS 채팅 어플리케이션 시리즈2는 시리즈 1에 이어서
1.sendMessage - 일반 텍스트 메세지
2.sendFileMessage - Image, Video, File등의 메세지를 chunking 하여 보내보는 시리즈입니다.

sendMessage - 텍스트 메세지

메세지를 전송할때 카카오톡같은 메신저는 로컬에 메세지를 저장한후 메세지전송이 안되면 재전송을 할 수 있는 기능을 지원합니다.

메세지 전송

일반텍스트 메세지 전송은 간단합니다.
syncKey: 유니크한키(서버에서 리턴되는 synckey와 매칭하여 현재메세지가 잘전송이 되었는지 확인하는 용도)
roomId: 메세지를 전송할 채팅방 id
content: 메세지 내용

sendMessage() {
  if (!this.message) return;

  const payload = {
    syncKey: new Date().getTime().toString(),
    roomId: this.activeRoom._id,
    content: this.message,
  };
  //새로운 메세지 추가
  this.messages.push({
    _id: null,
    syncKey: payload.syncKey,
    roomId: this.activeRoom._id,
    authorId: this.me._id,
    data: {
      type: 'TEXT',
      content: this.message,
    },
  });
  this.$socket.emit('sendMessage', payload, (data) => {
    console.log(data);
  });
  this.message = null;
},

새로운 메세지 구독

메세지를 받는 부분은 시리즈 1에 이미 나온 내용이지만 한번 더 짚고 가겠습니다.
메세지를 받는 방법은 joinRoom을 할때 현재 채팅방 id를 가지고 새로운 이벤트를 subscribe(구독) 하는 것입니다.

this.sockets.subscribe(`listenMessage_${room._id}`, (message) => {
  if (message) {
    const findIdx = this.messages.findIndex((m) => m.syncKey == message.syncKey);
    if (findIdx > -1) {
      //위에서 추가한 message가 있을경우에는 단순히 업데이트만!
      this.messages[findIdx] = message;
    } else {
      this.messages.push(message);
    }
    this.scrollToBottom();
  }
});

메세지 구독 해지

반대로 방에서 나올때 구독을 해지하면 위의 이벤트 트리거를 해지할 수 있습니다.

this.sockets.unsubscribe(`listenMessage_${room._id}`);

sendFileMessage - 이미지,비디오,파일 메세지

파일자르기(chunking file)

웹소켓의 바이트 최대전송사이는 1mb입니다. 그래서 이 예제에서는 100kb 단위로 청킹하여 업로드하기로 합니다. 500kb로 청킹해도 괜찮습니다.

chunkFile(e){
  var file = e.target.files[0];
  if (file) {
    this.tempFile = file;
    this.messageKey = md5(file.name + new Date());
    const messagePayload = {
      _id: null,
      key: this.messageKey,
      roomId: this.activeRoom._id,
      authorId: this.me._id,
      progress: 0,
      data: {
        type: 'FILE',
        content: null,
      },
    };
    this.messages.push(messagePayload);
    this.scrollToBottom();

    let slice = this.tempFile.slice(0, 100000, file.type);
    this.sendFileMessage(slice);
  }
}

파일을 전송중인 화면입니다.
uploadingg...
49.xxxx 퍼센테이지를 표시하는 화면입니다.

sendFileMessage(slice) {
  this.fileReader.readAsArrayBuffer(slice);
  this.fileReader.onload = () => {
    var arrayBuffer = this.fileReader.result;
    var type = 'FILE';
    if (this.tempFile.type.startsWith('image/')) {
      type = 'PHOTO';
    } else if (this.tempFile.type.startsWith('video/')) {
      type = 'VIDEO';
    }
    this.$socket.emit(
      'sendFileMessage',
      {
        syncKey: this.messageKey,
        data: arrayBuffer,
        type: type,
        size: this.tempFile.size,
        name: this.tempFile.name,
        roomId: this.activeRoomId,
      },
      (data) => {
        console.log('currentSlice', result);
        const success = result.success;
        const data = result.data;
        if (success && !data.done && data.slice) {
          //파일업로드가 완료되면 다음 슬라이스를 청킹하여 업로드!
          var offset = data.slice * this.splicedSize;
          var slice = this.tempFile.slice(
            offset,
            offset +
              Math.min(this.splicedSize, this.tempFile.size - offset),
            this.tempFile.type,
          );
          this.sendFileMessage(slice);
        }else if(data.done) {
          //파일업로드 완료시, progress 상태 구독해재
          this.sockets.unsubscribe(`progress_${this.messageKey}`);
        } else if(!success) {
          //파일업로드 실패시, progress 상태 구독해재
          this.sockets.unsubscribe(`progress_${this.messageKey}`);
      },
    );
	
    //file upload의 progress 상태 구독하기
    this.sockets.subscribe(`progress_${this.messageKey}`, (progress) => {
      var idx = this.messages.findIndex((m) => m.syncKey == this.messageKey);
      if (idx > -1) {
        this.messages[idx].progress = progress;
      }
    });
  };
},

파일 메세지 전송이 완료된후의 화면입니다.

0개의 댓글