💙 들어가며
프라미스에 대해서 배우고
콜백지옥에서 벗어나보자!
✏️ 학습내용 정리
#동기 코드처리
HTML파일과 js파일을 하나 준비하고
div태그에 img태그를 추가해보자.
img 태그의 너비를 100으로 줄이는 코드도 추가했다.
HTML에서 정상적으로 img 너비가 100으로 출력된다.
하지만 이것은 당연한 것이 아니다.
동기적으로 처리되었기 때문에
이미지의 너비를 조정할 수 있었던 것이다.
이미지는 원격(서버)에 있는 거고
문서에 올리고 있는 상황인데,
원래는 문서가 읽히는 속도가
이미지가 업로드 되는 속도보다 빠르다.
즉, 지금 문서는 동기적으로 실행이 되고 있다.
이미지를 텍스트로 넣었고,
이미지가 객체화 되기까지의 시간을 기다려주었다.
그래서 width를 조정할 수 있었던 것이다.
window.addEventListener("load", function(){
let root = this.document.querySelector("#root");
let template = `<img src="image/8.jpg">`;
root.insertAdjacentHTML("beforeend", template);
/* 이미지는 원격(서버)에 있는 거고
문서에 올리고 있는 상황인데,
문서가 읽히는 속도가 이미지가 업로드 되는 속도보다 빠르다.
즉, 지금 문서는 동기적으로 실행이 되고 있다.
이미지를 텍스트로 넣었고,
이미지가 객체화 되기까지의 시간을 기다려주었다.
그래서 width를 조정할 수 있었던 것이다. */
root.firstChild.style.width="100px";
});
#프라미스(Promise)란?
💡 프라미스(Promise)란?
비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타내는 객체이다.
자바스크립트는 중첩이 많다.
//(1차 중첩)
//페이지 로드가 비동기이기 때문에
//로드 되는 시점을 알수가 없어서 addEventListener를 사용했다
window.addEventListener("load", function(){
let root = this.document.querySelector("#root");
let template = `<img src="image/8.jpg">`;
root.insertAdjacentHTML("beforeend", template);
/* 이미지는 원격(서버)에 있는 거고
문서에 올리고 있는 상황인데,
문서가 읽히는 속도가 이미지가 업로드 되는 속도보다 빠르다.
즉, 지금 문서는 동기적으로 실행이 되고 있다.
이미지를 텍스트로 넣었고,
이미지가 객체화 되기까지의 시간을 기다려주었다.
그래서 width를 조정할 수 있었던 것이다. */
root.firstElementChild.classList.add("opacity:0");
//(2차 중첩)
//0.5초 후라는 개념이 애매하다.
//어느 시점에 코드를 넣어야 하는지 모르기 때문에
this.setTimeout(()=>{
root.firstElementChild.classList.remove("opacity:0");
root.firstElementChild.classList.add("opacity:33");
//(3차 중첩)
//opacity가 끝나는 시점에 width를 줄이고 싶다면?
//언제 끝나는지를 알아야 함
root.firstElementChild.ontransitionend=function(){
root.firstElementChild.style.width="200px";
}//call back
},500);
});
중첩이 많아지면 코드의 가독성이 떨어지고 복잡도가 높아진다.
처리하기 위한 방법이 필요하다.
지금까지는 중첩된 코드에서
완료라는 이벤트 개념을 콜백으로 대신했다.
(완성되면 실행해주는 함수)
그러나 콜백함수를 사용하다보니
흐름에 복잡도가 높아졌다.
HTML은 버전이 높아짐에 따라서
Promise 객체를 활용해
콜백을 대체할 수 있는 API를 제공하고 있다.
프라미스는 이런 중첩을
최대한 수직으로 처리하려고 노력하였다.
#비동기 작업의 return: 콜백함수(Callback)
request의 onload가 콜을 했을 때보다
18번이 실행되는 것이 더 먼저다.
따라서 18번에서 list를 출력하려고 해도
출력할 수 있는 것이 없다.
기존에 반환값을 얻기 위해서 지금까지는 콜백함수를 썼다.
동기일 때는 그냥 return을 쓰면 되었지만,
함수가 비동기로 처리 할 때는
작업이 완료되는 시점을 알 방법이 없어서
콜백으로 해결할 수밖에 없었다.
기존 방식대로 콜백함수를 만들어 보자.
=======================================
function get(url, resultCallback){
let request = new XMLHttpRequest();
// request.open("GET", `/api/menus?c=${el.dataset.id}`, true);
request.open("GET", url, true);
request.onload = function(){
//콜백이라서 get을 불렀을 때 반환하는게 없음
//반환한게 없으니까 undefined
//비동기로 있는 녀석을 라이브러리로 만들기 어렵다.
let list = JSON.parse(request.responseText);
resultCallback(list);
};
request.send();
}
=======================================
button.onclick = function(){
get(`http://localhost:8080/api/menus`, function(list){
console.log(list);
});
};
실행순서와 코드 복잡도가 엄청 높아졌다.
이제는 비동기 형식의 return을 콜백으로 하지 않고
promise라는 다른 방법으로 할 수 있게 되었다.
(동기는 그냥 return, 비동기의 return은 콜백)
promise덕분에 동기형 반환도 가능해졌다.
중첩을 없애는 방법으로도 가능하다.
#CORS(Cross-Origin Resource Sharing)
💡 CORS(Cross-Origin Resource Sharing)란?
origin은 보통 원격에 있는 서버를 의미한다.
cross origin이란 클라이언트가 다른 서버에게 요청을 보내는 것이다.
CORS (Cross-Origin Resource Sharing)는
웹 브라우저에서 실행되는 JavaScript 코드가
동일한 출처 (Origin)에서 로드한 웹 페이지에서
다른 출처의 리소스와 상호 작용할 수 있도록 하는 보안 기능이다.
각 웹 페이지의 출처이 다르면
브라우저는 보안 상의 이유로
스크립트에서 다른 출처의 리소스에 접근하는 것을 제한한다.
(★ ★ ★ 브라우저가 제한하는 것임!!! ★ ★ ★ )
이러한 제한을 해결하고
서로 다른 출처 간에 데이터를 공유하려면
CORS를 설정해야 한다.
반복되는 XHR을 이용한 데이터 호출코드를
get이라는 함수를 사용해서 처리하려고 하였다.
function get(url){
let request = new XMLHttpRequest();
// request.open("GET", `/api/menus?c=${el.dataset.id}`, true);
request.open("GET", url, true);
request.onload = function(){
return JSON.parse(request.responseText);
};
request.send();
}
//다른 서버로 보내는 거라서 앞에 포트번호까지 다 적어줘야 함
button.onclick = function(){
let list = get(`http://localhost:8080/api/menus`);
console.log(list[0]);
};
보내려고 했더니 CORS 정책에 의해 접근이 제한되었다
클라이언트가 A서버에게 요청했는데
만약 B서버에서도 볼 수 있다면 어떨까?
B서버에서 A서버에 다량의 요청을 보낸다면?
디도스 공격이 된다.
(많은 양의 요청을 넣어서
진짜 요청을 처리할 수 없게 만든다.)
이런 부분을 고려하여 보안을 위하여
CORS라는 보안기능이 생겼다.
CROS를 막고 있으니 접근하려는 API에서
Access-Control-Allow-Origin이라는 header를
처리해주어야 한다는 오류 메세지가 뜬다.
내 API 컨트롤러에 가서
접근하려는 url에 @CrossOrigin이라는 어노테이션을 적는다.
이때 안의 내용에는 (origins = "http(s)://크로스 하려는 서버의 주소:포트번호/")를 적어준다.
@GetMapping //목록을 제공하는 API
@CrossOrigin(origins = "http://도메인 혹은 IP주소:포트번호/")
public List<MenuView> list(
@RequestParam(name = "p", defaultValue = "1") Integer page,
@RequestParam(name = "q", required = false) String query,
@RequestParam(name = "c", required = false) Long categoryId
){
💙 마치며
1.
Promise는 콜백지옥을 해결해주었다!
그래도 콜백함수의 실행순서와
코드의 흐름을 잘 이해해야겠다.