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, '<') == 전체 검색 후 '<'가 있으면 '<'로 변경.
const tmpl = `<div class="chat">
<div class="username">${username.replace(/</g, '<').replace(/>/g, '>')}</div>
<div>${text.replace(/</g, '<').replace(/>/g, '>')}</div><div>${date}</div>
<div>${roomname.replace(/</g, '<').replace(/>/g, '>')}</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, ‘<’) == 전체 검색 후 ‘<’가 있으면 ‘<’로 변경.
renderMessage: ({ username, text, date, roomname }) => {
const tmpl = `<div class="chat">
<div class="username">${username.replace(/</g, '<').replace(/>/g, '>')}</div>
<div>${text.replace(/</g, '<').replace(/>/g, '>')}</div><div>${date}</div>
<div>${roomname.replace(/</g, '<').replace(/>/g, '>')}</div>
</div>`;
// 해당 엘리먼트의 innerHTML 값은 tmpl에 기존 해당 엘리먼트의 innerHTML 값을 더한 것
document.querySelector('#chats').innerHTML =
// 최근에 생성한 tmpl을 상단에 더해주기위해 앞에 써준다.
tmpl + document.querySelector('#chats').innerHTML;
},