TIL #13 Chatterbox-Client Review

Joshua Song / 송성현·2019년 12월 9일
1

이머시브_16

목록 보기
15/29

Intro

이번 스프린트는, 채팅을 할 수 있는 chat application의 클라이언트 부분을 만들어 보는 것이었다. 서버는 AWS에 배포되어 있는 코드스테이츠 서버를 활용하고 fetch library 를 활용하여, GET과 POST method를 사용해 서버에 메시지를 요청해 받고, 또 메세지를 보낼 것이다.

fetch를 잘 활용해, GET method 일때는 지금까지 서버에 있는 메시지를 불러와 함수를 통해 페이지에 트위틀러에서 했던 것 처럼 usernamer과 메시지가 나오게 해주면 된다.

POST method 일때는,

var message = {
  "username": "JoshuaSong",
  "text": "hello",
  "roomname": "weWork"
} 

이런 식의 메세지 오브젝트의 형식으로 서버에 보내서 response로는 {id: number}형태의 객체를 받는다. 친절하게도, 아니면 당연하게도, API docs를 통해 코드스테이츠는 서버가 무엇을 받고 무엇을 돌려주는지 명시한다.

API DOCS

Base URL
baseURL = "http://52.78.206.149:3000"

Retrieve Messages

HTTP Method: GET
URL: {baseURL}/messages
Response: JSON, Array of Messages.
Retrieved Messages have...
id: number - unique message id
username: string
text: string
roomname: string
date: string - Example) 2017-07-28T03:54:21.134
also possible to use query parameters
e.g. URL: {baseURL}/messages?roomname=1 will provide messages with roomname is 1
Send Message

HTTP Method: POST
URL: {baseURL}/messages
Body: application/json

{ "username": string, "text": string, "roomname": string, }

Response: application/json

{ id: number }
id: unique id of your message

Pseudocode (Plan)

페어와 함께 시작하기 전 수도코드? 어떤식으로 문제를 접근할지 계획을 짜는게 먼저일 것 같아서 간단하게 정리해본다.

  1. 먼저 무슨 함수가 필요한지 생각을 해서 역할을 나눠준다.
    - init함수: 페이지가 로딩될 때 처음 실행되는 함수. 페이지에 챗들을 보여준다.
    - fetch함수: 서버에서 response를 받아와 리턴해 준다. 다른 함수들은 이 함수를 통해 response를 받아온다. GET Method라고 생각하면 된다.
    - print함수: 채팅이 추가되서 챗 메시지 개수가 늘어날 때마다 그 챗을 보여준다.
    (Advanced 항목)
    - send함수: POST method를 사용해 새로 입력한 메시지 오브젝트를 서버에 보내준다. 그리고 response를 받아준 후 그 새로운 내용을 렌더해준다.
    - clearMessages함수: 챗들이 있는 부분을 다 지워준다.
    - renderMessage함수: 인자로 들어온 메시지를 추가해준다.
    - makeRoom함수: 채팅 메시지의 room에 따라 room을 설정해준다.
    - filterByRoomname: room을 클릭하면 룸 이름에 따라 챗을 필터링 해준다. (Advanced)
    - resetFilter: 룸의 옵션 중 Room을 클릭하면 모든 챗을 모아둔 챗 화면으로 돌아간다.
  2. html에 필요한 부분들 (messagebox 및 submit 버튼)을 만들어 준후 querySelector로 가져와준다.
  3. 함수 내용에 맞게 함수를 짜준다.
  4. 나머지 miscellaneous 한 condition을 짜준다.

Actual Coding

  • It's hard to describe all the process I went thorugh because the actual coding was finished few days ago. 그래서 사실 내가 느낀 점을 정리하는게 좋을 것 같다.

Code

// eslint-disable-next-line
var message = {
  username: "크러쉬",
  text: "",
  roomname: "아메바컬쳐"
};

var idNumber = -1;

var chatArea = document.querySelector("#chats");
var submitButton = document.querySelector("#submit");
var newMessage = document.querySelector("#message");
var makeRoom = document.querySelector("#room");

const app = {
  server: "http://52.78.206.149:3000/messages",
  init: function() {
    app.print();
    app.makeRoom();
    setInterval(function() {
      app.print();
    }, 1000);
  },

  fetch: function() {
    return fetch(app.server).then(response => response.json());
  },

  print: function() {
    app.fetch().then(json => {
      if (idNumber < json[json.length - 1].id) {
        for (let i = idNumber + 1; i < json.length; i++) {
          app.renderMessage(json[i]);
        }
        idNumber = json[json.length - 1].id;
      }
    });
  },

  send: function(text) {
    fetch("http://52.78.206.149:3000/messages", {
      method: "POST",
      body: JSON.stringify(text),
      headers: {        
        "Content-Type": "application/json"
      }
    })
      .then(response => response.json())
      .then(() => {
        app.print();
        app.makeRoom();
      });
  },

  clearMessages: function() {
    document.querySelector("#chats").innerHTML = "";
  },

  renderMessage: function(json) {
    if (chatArea === null) {
      chatArea = document.querySelector("#chats");
    }

    let messageBlock = document.createElement("div");
    messageBlock.setAttribute("class", "messageBlock");
    messageBlock.innerHTML = `<div class="username">${json.username}</div>
      <div class="message">${json.text}</div>`;
    chatArea.prepend(messageBlock);
  },

  makeRoom: function() {
    app.fetch().then(json => {
      for (let element of json) {
        if (!makeRoom.innerHTML.includes(element.roomname)) {
          var roomName = document.createElement("option");
          roomName.innerHTML = element.roomname;
          makeRoom.appendChild(roomName);
        }
      }
    });
  },

  filterByRoomname: function(roomname) {
    app.fetch().then(json => {
      app.clearMessages();
      for (let element of json) {
        if (element.roomname === roomname) {
          app.renderMessage(element);
        }
      }
    });
  },

  resetFilter: function() {
    idNumber = -1;
    app.clearMessages();
    app.print();
  }
};

app.init();

submitButton.addEventListener("click", function() {
  if (newMessage.value.trim().length === 0) {
    window.alert("내용을 입력해");
  } else {
    message.text = newMessage.value;
    app.send(message);
    newMessage.value = "";
  }
});

makeRoom.addEventListener("click", function(x) {
  let roomname = x.target.value;
  if (roomname === "Room") {
    // window.location.reload();
    app.resetFilter();
  } else {
    app.filterByRoomname(roomname);
  }
});

//**옵션이 클릭되면 이벤트를 걸고, 클릭이 된 이름으로 클리어 메시지를 다 날려버리고, fetch 를 한 후 fetch 한거 중에서 filter를 해서
//**roomname이 선택한 것과 동일한 것과 렌더.

Recap

각 함수를 보면, 각 기능에 맞춰서 실행되는 함수가 다르다. 처음에 app.init을 실행시켜 주는 이유는 페이지가 로딩될 때, 지금까지 작성되어 있는 챗들을 프린트 해주기 위해서이다. 함수이름은 보시다시피 이름에 나와있는 기능을 구현한다. 함수를 실행시킬때 그 안에 실행되는 다른 함수는 흐름을 생각하고 따라가면 말이 된다.

챗들을 보여줄 구역을 index.html에 만들어 주었고 새로운 챗들을 받아와 prepend로 추가해 준다.
기본 메시지 틀을 만들어서 그 안의 내용만 계속 바꾸어주면 좀 더 편리하다. 하지만 원하면 유저네임과 방 이름을 위한 칸도 만들어서 내용을 수정해 줄수 있다. 그건 간단하다.

시간이 그래도 조금 있어 Advanced 기능도 만들어 주었다. setInterval을 통해 auto fetching을 해 다른 유저가 채팅을 추가해도 렌더를 해주게 만들었고 또 room에 따라 챗들을 필터 할 수 있게 만들었다.

Overview

  • 결국 중요한 점은 fetch를 사용할 때 무엇을 가져오고 보내주는지, 어떻게 요청을 보내고 어떻게 받아오는 부분인 것 같다. .then 을 (promise 함수)를 사용하기 때문에 비동기 처리도 가능하고, 여러모로 많은 걸 배웠다. API는 정말 편리하다... 이번 스프린트는 무엇이 서버에서 response로 오는지를 알아 그 데이터를 가지고 정리한 후 보여주면 돼 생각보다 수월했다. 페어분도 열심히 알려주시고 소통도 열심히 했다. 똑같은 기능을 구현하더라도 코드를 좀 더 쉽게, 효율적으로 짜는 점이 중요하다는 걸 느꼈다. 그리고 함수 고유의 기능도 정리해주는 것은
    중요하다! Was fun and helpful.
profile
Grow Joshua, Grow!

0개의 댓글