Client 예제 복습 21.05.02


const githubID = 'kimcoding'

// single ton 패턴 : 
// 전역 변수를 사용하지 않고 하나의 객체에 변수와 method를 key-value 형태로 저장하는 모듈 패턴.
// 생성된 객체를 어디에서든지 참조할 수 있는 패턴 (ex: app.init(), app.server )
const app = {
  // 서버 지정 
  server: `http://3.36.72.17:3000/${githubID}/messages`,

  // init method : 최초 실행되는 method
  init: () => {
    // addEventHandlers method 실행
    app.addEventHandlers();
    // fetch method 실행 : API를 사용하여 백엔드 서버와 비동기 요청을 하는 방식.
    app.fetch((json) => {
      // josn 데이터를 forEach로 렌더링
      json.forEach(app.renderMessage);
    });
  },

  // addEventHandlers method
  addEventHandlers: () => {
    // DOM으로 form 선택. 
    // 버튼이 아닌 form을 선택하는 이유 : 
    // submit은 폼의 정보를 서버로 전송하는 명령인 submit시에 일어난다. form 태그에 적용된다.
    let submit = document.querySelector('#send .submit');
    // addEventListener 오류(Cannot read property 'addEventListener' of null) 방지
    // 해당 오류 발생 원인 : HTML이 모두 로드 되기 전에 JS 파일에서 HTML을 참조하기 때문
    // 해결방법 : if문 사용 - submit이 true(존재하는 경우)에만
    // Boolean(document.querySelector('#send .submit')) === true
    if (submit) {
      // submit에 submit 시 handleSubmit mehtod 이벤트를 추가
      submit.addEventListener('submit', app.handleSubmit);
    }
  },

  // fetch Method : GET 요청
  fetch: callback => {
    // window 객체안에 있는 fetch method 사용
    window
      // server를 fetch하고
      .fetch(app.server)
      // 응답을 json으로 파싱해준다.
      .then(resp => {
        return resp.json();
      })
      // 그 후 인자인 callback 실행
      // 해당 method 외부에서 사용하기 위해 인자로 설정
      .then(callback);
  },

  // send method : POST 요청
  send: (data, callback) => {
    // window 객체안에 있는 fetch 사용
    window
      // POST인 경우 다음과 같은 포맷을 사용한다.
      // fetch(URL, 객체)를 작성하여 요청
      .fetch(app.server, {
        // method 설정
        method: 'POST',
        // body는 입력받은 data를 직렬화
        body: JSON.stringify(data),
        // headers에는 컨텐츠 타입 json으로 명시 
        headers: {
          'Content-Type': 'application/json'
        }
      })
      .then(resp => {
        // 받은 응답을 json화
        return resp.json();
      })
      // 그 후 인자인 callback 실행
      // 해당 method 외부에서 사용하기 위해 인자로 설정
      .then(callback);
  },

  // 메시지창을 삭제 하는 mehtod
  clearMessages: () => {
    // innerHTML로 공백을 넣어준다. 
    // 뜻 그대로 HTML 자체에 넣어준다. (성능도 좋지않고 XSS 공격에도 취약하므로 추천 X)
    document.querySelector('#chats').innerHTML = '';
  },
  // form을 비우는 method
  clearForm: () => {
    // value 값을 공백으로 넣어준다.
    document.querySelector('.inputUser').value = '';
    document.querySelector('.inputChat').value = '';
  },

  // render method
  // 파라미터로 객체를 받는다. 
  // fetch로 받은 json 객체 데이터가 인자로 할당 된다.
  // 객체 구조 분해 할당 :
  // { username, text, date, roomname } = json 객체 데이터
  renderMessage: ({ username, text, date, roomname }) => {
    // DOM으로 작성해도 되지만 직관적으로 작성하기위해 템플릿 리터럴 사용
    // 정규표현식을 사용해서 XSS 공격 방지 : 
    // .replace(/</g, '&lt;') == 전체 검색 후 '<'가 있으면 '&lt'로 변경.
    const tmpl = `<div class="chat">
                    <div class="username">${username.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</div>
                    <div>${text.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</div><div>${date}</div>
                    <div>${roomname.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</div>
                  </div>`;

    // 해당 엘리먼트의 innerHTML 값은 tmpl에 기존 해당 엘리먼트의 innerHTML 값을 더한 것
    document.querySelector('#chats').innerHTML =
      // 최근에 생성한 tmpl을 상단에 더해주기위해 앞에 써준다.
      tmpl + document.querySelector('#chats').innerHTML;
  },

  // 이벤트 핸들러
  handleSubmit: e => {
    // preventDefault : 깜빡거림을 방지하기 위해 사용
    // html 에서 a 태그나 submit 태그는 고유의 동작이 있다. 
    // 페이지를 이동시킨다거나 form 안에 있는 input 등을 전송한다던가 그러한 동작이 있는데 그 동작을 중단시킨다.
    e.preventDefault();
    // clearMessages method 실행 : 채팅 화면 비우기
    app.clearMessages();
    // send method 실행 : POST 요청
    app.send(
      // send method는 인자로 객체 데이터와 콜백 함수(fetch에서 실행)를 갖는다.
      { 
        username: document.querySelector('.inputUser').value,
        text: document.querySelector('.inputChat').value,
        roomname: '코드스테이츠'
      },
      // fetch로 GET 요청을 실행하고 가져온 각 데이터를 랜딩해준다.
      () => {
        app.fetch(json => {
          json.forEach(app.renderMessage);
        });
        // form 비우기
        app.clearForm();
      }
    );
  }
};

// 자바스크립트 실행 시 최초로 실행
app.init();

// 테스트를 위한 코드입니다.
if(window.isNodeENV){
  module.exports = app;
}

기억해야 할 점

폼엘리먼트.addEventListner(‘submit’, 이벤트 함수)

submit은 폼의 정보를 서버로 전송하는 명령인 submit시에 일어난다. form 태그에 적용된다.

let submit = document.querySelector('#send .submit');
Cannot read property ‘addEventListener’ of null
  • addEventListener 오류(Cannot read property ‘addEventListener’ of null) 방지
  • 해당 오류 발생 원인 : HTML이 모두 로드 되기 전에 JS 파일에서 HTML을 참조하기 때문
  • 해결방법 : if문 사용 - submit이 true(존재하는 경우)에만 (Boolean(document.querySelector(‘#send .submit’)) === true)
if (submit) {
    submit.addEventListener('submit', app.handleSubmit);
}
window 객체의 fetch method
send: (data, callback) => {
    // window 객체안에 있는 fetch 사용
    window
      // POST인 경우 다음과 같은 포맷을 사용한다.
      // fetch(URL, 객체)를 작성하여 요청
      .fetch(app.server, {
        // method 설정
        method: 'POST',
        // body는 입력받은 data를 직렬화
        body: JSON.stringify(data),
        // headers에는 컨텐츠 타입 json으로 명시 
        headers: {
          'Content-Type': 'application/json'
        }
      })
POST 요청의 포맷
.fetch(app.server, {
        // method 설정
        method: 'POST',
        // body는 입력받은 data를 직렬화
        body: JSON.stringify(data),
        // headers에는 컨텐츠 타입 json으로 명시 
        headers: {
          'Content-Type': 'application/json'
        }
})
callback 인자 사용

해당 method 외부에서 가져온 데이터를 사용하기 위해 콜백 함수 인자로 설정

// send method : POST 요청
send: (data, callback) => {
        // ...
        // 생략
        // ...
      .then(resp => {
        return resp.json();
      })
      .then(callback);
  },
객체 구조 분해 할당

객체 구조 분해 할당 : { username, text, date, roomname } = json 객체 데이터

renderMessage는 파라미터로 객체를 받는다. 함수 내부에서 fetch로 받은 json 객체 데이터가 인자로 할당 된다.

renderMessage: ({ username, text, date, roomname }) => { 함수 }
innerHtml x 정규표현식

DOM으로 작성해도 되지만 직관적으로 작성하기위해 템플릿 리터럴 사용.

innerHtml은 XSS 공격에 취약하지만 정규표현식을 사용하여 XSS 공격을 방지 가능.

정규표현식을 사용해서 XSS 공격 방지 : .replace(/</g, ‘<’) == 전체 검색 후 ‘<’가 있으면 ‘&lt’로 변경.

renderMessage: ({ username, text, date, roomname }) => {
    const tmpl = `<div class="chat">
                    <div class="username">${username.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</div>
                    <div>${text.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</div><div>${date}</div>
                    <div>${roomname.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</div>
                  </div>`;

    // 해당 엘리먼트의 innerHTML 값은 tmpl에 기존 해당 엘리먼트의 innerHTML 값을 더한 것
    document.querySelector('#chats').innerHTML =
      // 최근에 생성한 tmpl을 상단에 더해주기위해 앞에 써준다.
      tmpl + document.querySelector('#chats').innerHTML;
  },





© 2020.11. by creamer

Powered by CREAMer