본문 바로가기
Front-End/Project

ChatterBox 만들기

by 연제원 2021. 2. 8.

밋밋하지만 원하던 기능은 완성..!

설명


클라이언트(chatterBox)에서 채팅 입력칸에 이름과 내용을 적어 제출(POST 요청)을 하면, 서버는 그에 대한 응답(저장)을 한다.

그리고 클라이언트는 다시 서버에게 GET요청을 보내 이를 바탕으로 JS에서 DOM을 구현한다.

 

 

✍ 배운 것


지금까지 배운 지식들과 새로 배운 HTTP 개념과 node.js를 이용하여 chatterBox를 만들어 봤다.

이전까진 단순히 JS로 HTML을 조작하는 것만 구현했었다면, 이번에는 직접 서버와 클라이언트를 구현하고 서버에서 데이터를 가져와 클라이언트는 이 데이터를 바탕으로 JS로 HTML을 조작하는 것이었다.

프로젝트를 통해 HTTP 통신 방법과, 이를 위한 node.js 사용법을 조금이나마 이해하고 사용할 수 있었다.

 

1. HTTP

HTTP는 인터넷에서 데이터를 주고받을 수 있는 프로토콜, 규칙이다. 이렇게 규칙을 정해두었기 때문에, 모든 프로그램이 이 규칙에 맞춰 서로 데이터를 주고 받는다. HTTP 통신

애매했던 HTTP에 대한 개념들을 직접 서버와 클라이언트를 제작하고 구현하면서, 어느정도 정리가 된 듯하다. 

클라이언트는 요청(request)을 보내면 서버는 응답(request)반드시 보내줘야 한다. 교환이 제대로 되든, 되지 않던간에! 

그리고 클라이언트에서는 fetch함수를 통해 서버에 요청을 할 수 있었다.

2. node.js

기본적으로 내장되어 있는 http, fs 모듈을 통해 서버를 생성하고 데이터를 저장하고 다양한 일들을 할 수 있었다.

 

Client


1. 데이터 요청(GET) 및 활용

fetch함수를 통해 서버의 api를 요청하고 데이터를 응답받을 수 있다.

나 같은 경우에는 서버에 데이터 저장할 때, txt 파일에 JSON 형태로 다음과 같이 저장했다. 

{"results": [{데이터}, {데이터}, ...]}

 

따라서 클라이언트에서는 이 데이터를 활용하기 위해 객체 상태로 만들어줘야 했고 response.json() 이란 fecth에 내장되어있는 메서드를 활용했다.

 

// fetch API를 통해 get 요청
  fetch: (callback) => {
    window.fetch(app.server)
    // .then((res) => console.log(res))
    .then((res) => res.json()) // 배열인데 요소는 객체 상태  '['{''name': 1}]'
    // .then((json) => console.log(json))
    .then(callback)
  }

 

2. 데이터 제출(POST)

참고한 서버의 api를 봤을 때, 메시지 추가하는 요청을 할 때 fetch('url', {여기}) 요청 body를 작성해줘야 했다.

send: (message) => {
    window.fetch(app.server, { // 요청하는 객체방식
      method: 'POST',
      body: JSON.stringify(message), // stringify 과정이 반드시 필요합니다. 왜일까요?
      headers: {
        'Content-Type': 'application/json'
      },
    })
    .then(response => response.json())
    .then(json => {
      console.log(json)
      console.log('새 글을 작성했습니다')
    });
  },

 

3. 그 외 기능

1) 같은 방끼리 필터링 및 맨 위로 보내기 버튼 구현

이벤트리스너 함수를 직접 적용하는데 어려움을 겪고 있었는데, 연습해볼 수 있어 좋았다.

⭐ 또한 arr.filter()는 기존 배열을 변화시키지 않으므로 다른 변수에 할당해줬어야 했다.

 

2) 폰트 변경

⭐ JS로 CSS를 다룰 때, font-family와 같이 - 가 존재하는 속성은 - 다음 첫 문자를 대문자로 표기해주면 된다.

document.querySelector('#~~~').style.font-family = ~~~~

위를 아래와 같이 변경

document.querySelector('#~~~').style.fontFamily = ~~~~

 

 

3) 보기 편하도록 항상 스크롤이 맨 밑 

HTML
<div id="chats"></div>

CSS
#chats {
  overflow-y: scroll; /* 스크롤 바 */
}
#chats::-webkit-scrollbar { 
  display: none; /* Chrome, Safari, Opera*/
}

JS
const chatsList = document.querySelector('#chats');
chatsList.scrollTop = chatsList.scrollHeight; // 스크롤바 항상 밑으로 가있도록

 

4) 현재 메시지 수 표기 등..

 

Server


Node.js - HTTP 모듈 

HTTP 트랜젝션 해부를 차근차근 읽으며 구현해봤다. HTTP 통신은 요청이 있으면 항상 응답을 해줘야한다.

POST요청의 응답을 구현하는 것을 이해하기 어려웠는데, 정리를 해보자면 다음과 같다.

 

  1.  클라이언트에서 POST 요청을 할 때 HTTP 메시지의 요청 body를 작성해서 보낸다.
  2.  서버는 클라이언트에게 받은 요청 body를 저장해야하는데 이때 통신하는 데이터가 각 'data' 이벤트에서 청크(buffer)를 발생시키고 이를 수집한 다음 'end' 이벤트에서 이어 붙인 다음 문자열 형태(JSON)로 저장한다.
const requestHandler = function (request, response) {
  // node server 의 requestHandler는 항상 request, response를 인자로 받는다.

  const headers = defaultCorsHeaders;
  // 응답 헤더에 응답하는 컨텐츠의 자료 타입을 헤더에 기록 한다.
  headers["Content-Type"] = "text/plain";
  
  //! OPTIONS 요청 응답(CORS)
  if(request.method === "OPTIONS"){
    response.writeHead(200, defaultCorsHeaders);
    response.end();
  }
  //! GET 요청 응답
  if(request.method ==='GET' && request.url ==='/messages'){
    response.writeHead(200, headers);
    response.end(JSON.stringify(saveData));
  }
  //! POST 요청 응답
  if(request.method ==='POST' && request.url ==='/messages'){
    response.writeHead(201, headers);
    let body=[];
    request.on('data', (chunk) => {
      body.push(chunk);
    }).on('end', () => {
      body = Buffer.concat(body).toString();
      saveData.results.push(JSON.parse(body));
      fs.writeFile('server/message.txt', JSON.stringify(saveData), 'utf8', (err) => {
        if (err) throw err;
        console.log('Complete Data')
       }
      )
      response.end(JSON.stringify({id: saveData.results.length-1})); // id 리턴해줌
    });
  }
  //! 에러 요청에 응답하기
  else {
    response.writeHead(404, headers);
    response.end();
  }
};

const defaultCorsHeaders = {
  "access-control-allow-origin": "*",
  "access-control-allow-methods": "GET, POST, PUT, DELETE, OPTIONS",
  "access-control-allow-headers": "content-type, accept",
  "access-control-max-age": 10 // Seconds.
};


Node.js - fs 모듈

위 코드에서 POST 요청일 때, 속에 fs.writeFile() 메서드를 볼 수 있다. 이는 filename의 파일에 [options]의 방식으로 data 내용을 쓴 후 callback 함수를 호출한다. (비동기적)

fs.writeFile(filename, data, [options], callback)

 

즉, 나는 어떠한 파일에 POST 요청을 받은 데이터를 따로 저장하기 위해 사용했다.

그리고 저장한 데이터를 나중에 다시 사용하기 위해 fs.readFile() 메서드를 사용해, 기존에 있던 데이터를 가져와 클라이언트가 GET요청을 하면 이 데이터를 전송해줬다.

 

❗response.writeHead(), response.end() 는 필수

response는 영어 단어 뜻 그대로 응답하는 것이다.
response.writeHead() : 응답 헤더 작성
response.end() : 응답 바디 작성
HTTP 응답 메시지의 헤더는 필수적으로 작성해줘야 하기 때문에 writeHead()를 작성해주고, end()는 바디 메시지 작성 역할을 하므로 상황에 따라 작성해준다.

 

❗URL, Method를 확인하는 이유

라우팅(Routing)을 위해!
라우팅 : 어떤 네트워크 안에서 통신 데이터를 보낼 때 최적의 경로를 선택하는 과정

 

❗ 오류

POST 함수 속에만 fs.writeFile()을 넣었을 때, 임의로 저장한 파일을 지우면 서버 실행에 에러가 생겼다.
이는 함수 밖에 fs.readFile() 메서드를 사용했었는데, 파일을 불러오지 못한 것 같았다.
그래서 임의로 함수 밖에 fs.writeFile()을 한번 더 실행시켜 항상 파일을 생성시키도록 해줬는데 다른 방법을 더 찾아봐야 할 것 같다. 분명히 답은 있다!

 

 

마무리


이전에는 코드스테이츠에서 제공해준 서버와 연결해서 데이터를 다뤘었다.

하지만 이번 프로젝트에선 직접 Node.js를 통해 서버를 구축하고 데이터를 다루는 연습을 해보니, 추상적이기만 했던 공부들이 눈으로 직접 보여 더욱 재밌어졌다.

이론도 중요하지만 직접 코드를 하나씩 쳐보며 어떤 반응, 결과가 나오는 지 눈으로 직접 확인해봐야 내 것이 되는 것 같다.

갈 길은 멀지만 차근차근 정리를 하며 쌓아가다보면 내가 생각한 것들을 구현할 수 있는 개발자가 될 수 있을 것이다!

댓글