- 만들려는 기능 -
팝업을 띄우는 기능을 공통함수로 만들려고 한다.
함수에 url 만 파라미터로 넣으면 팝업이 뜨도록 한다.
XMLHttpRequest 을 이용한다.
파라미터로 넣어준 url 을 호출하면 page 를 던져준다.
받아온 page 안에서 팝업에 해당하는 특정 구역만 골라서 긁어온다.
새로 받아온 response 를 현재 document 안에 삽입한다.
- 초안 -
function makeRequest() {
const xhr = new XMLHttpRequest();
const xhrUrl = "${pageContext.request.contextPath}/mypage";
const callbackFunc = function(data) {
// 조건문 안 넣으면 한 번에 3번씩 호출됨.
if(xhr.readyState === xhr.DONE) {
if(xhr.status === 200 || xhr.status === 201) {
// 문자열로 넘어오는 response 를 일단 DOM element 로 생성하기
const divWrapper = document.createElement("div");
divWrapper.innerHTML = data.currentTarget.response;
// popup만 긁어와서 현재 body 에 붙여넣기
const popupComplete = divWrapper.querySelector("div#popupCenter");
document.body.append(popupComplete);
// popup 에서 가져다 쓸 script 도 같이 붙여넣기
const popupScript = divWrapper.querySelector("script#popupScript");
const newScript = document.createElement("script");
newScript.innerHTML = popupScript.innerHTML;
document.body.appendChild(newScript);
}
}
}
xhr.onreadystatechange = callbackFunc;
xhr.open("GET", xhrUrl);
xhr.send("testparam=hellowrold");
}
- 문제점 -
> 아래 조건을 넣지 않으면 콜백 함수가 3번씩 호출되는 현상
>> if(xhr.readyState === xhr.DONE) { }
> response 로 받아오는 데이터에서 원하는 요소를 바로 꺼내오지 못함
>> divWrapper 같은 껍데기를 임시로 만들어 두고 그 안에 response 를 넣었다가 divWrapper 에 querySelector 를 적용함.
> innerHTML 보안 이슈
>> sparrow 나 fortify 등 보안 프로그램에서 XSS 위험 항목 위반으로 사용 불가
- 해결 -
> 콜백 함수가 3번씩 실행됐던 이유는 onreadystatechange 에 콜백 함수를 걸었기 때문.
>> 즉, XMLHttpRequest 를 호출하는 과정에서 ready state가 바뀔 때마다 콜백함수를 호출하고 있었음
>> 콜백함수가 호출되는 시점을 XMLHttpRequest 가 모두 load된 이후로 지정함으로써 해결함
>> 콜백 함수를 onload 에 걸어주었더니 XMLHttpRequest 가 모두 load 된 후 콜백 함수가 한 번만 실행됨.
>> 콜백 함수를 걸어둘 수 있는 라이프 사이클 이벤트는 아래에 따로 정리해 둠
> response 로 받아오는 데이터에서 원하는 요소를 바로 꺼내오지 못했던 이유는 response 를 단순한 text 로 인식했기 때문.
>> XMLHttpRequest 를 호출하고 받아온 reponse 는 데이터 타입이 text 로 지정되어 있었음.
>> 따로 지정하지 않았는데, 그럴 경우에는 기본값으로 "text" 가 지정됨.
>> 호출 결과로 받아오는 response 의 데이터 타입을 document 로 지정해줌으로써 해결함.
>> 데이터 타입이 document 이다 보니 response 에 바로 querySelector 를 쓸 수 있었음.
>> xhr.responseXML.querySelector("main.main-container");
>> 데이터 타입으로 지정할 수 있는 값은 아래에 정리해 둠.
> innerHTML 보안 이슈가 걸렸던 문제는 innerHTML 를 사용하지 않는 방법뿐이었음.
>> 우회해서 사용하거나 sanitizing, filtering 하는 방법 등을 시도해 봤으나 효과가 없었음.
>> 아마 innerHTML 라는 실행문 자체를 인식하는 듯했음.
>> innerHTML 를 대신하여 사용할 수 있는 기능을 찾아서 대체하는 방법뿐.
>> innerText, textContent 등.
- 수정안 -
function openPopupByUrl(popupUrl) {
const xhr = new XMLHttpRequest();
const callbackFunc = function(data) {
// 받아온 response 로부터 특정 구역 긁어오기
const response = xhr.responseXML.querySelector("main.main-container");
response.setAttribute("popup-id", "my-popup-container");
// 현재 body 에 넣기
document.body.append(response);
// popup 에서 쓸 script 도 같이 붙여넣기
const popupScriptList = response.querySelectorAll("script");
popupScriptList.forEach((ele, idx, arr)=>{
const tempScript = document.querySelector("script");
tempScript.textContent = ele.textContent;
if(ele.src) {
tempScript.src = ele.src;
}
response.append(tempScript);
ele.remove();
});
// 팝업 닫는 함수
const funcClosePop = function() {
document.querySelector("main[popup-id=my-popup-container]").remove();
}
// 닫기 버튼 클릭하면 팝업 삭제
const closeBtnList = response.querySelectorAll("button.popup-close");
funcAddEventToList(closeBtnList, funcClosePop);
}
xhr.onload = callbackFunc;
xhr.open("GET", popupUrl);
xhr.responseType = "document";
xhr.send();
}
- XMLHttpRequest 지정할 수 있는 이벤트 -
// abort
// > 요청이 중단되었을 때 실행
const xhr = new XMLHttpRequest();
xhr.onabort = (event)=>{ }
xhr.addEventListener("abort", (event)=>{ });
// error
// > 요청에 오류가 발생했을 때 실행
const xhr = new XMLHttpRequest();
xhr.onerror = (event)=>{ }
xhr.addEventListener("error", (event)=>{ });
// load
// > XMLHTTPRequest 트랜잭션이 성공적으로 완료됐을 때 실행
const xhr = new XMLHttpRequest();
xhr.onload = (event)=>{ }
xhr.addEventListener("load", (event)=>{ });
// loadstart
// > 요청이 데이터를 로드하기 시작했을 때 실행
const xhr = new XMLHttpRequest();
xhr.onloadstart = (event)=>{ }
xhr.addEventListener("loadstart", (event)=>{ });
// loadend
// > 요청이 완료되면 실행. 요청에 성공하든 실패하든 실행됨
const xhr = new XMLHttpRequest();
xhr.onloadend = (event)=>{ }
xhr.addEventListener("loadend", (event)=>{ });
// progress
// > 요청이 데이터를 주고받을 때 주기적으로 실행
const xhr = new XMLHttpRequest();
xhr.onprogress = (event)=>{ }
xhr.addEventListener("progress", (event)=>{ });
// readystatechange
// > readyState 속성이 변경될 때마다 실행
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = (event)=>{ }
xhr.addEventListener("readystatechange", (event)=>{ });
// timeout
// > 사전 설정된 시간 만료로 인해 진행이 종료되면 실행
const xhr = new XMLHttpRequest();
xhr.ontimeout = (event)=>{ }
xhr.addEventListener("timeout", (event)=>{ });
- XMLHttpRequest 지정할 수 있는 response type -
// ""
// 공백으로 넣으면 기본값 "text 와 같음
const xhr = new XMLHttpRequest();
xhr.responseType = "";
// "arraybuffer"
// 이진 데이터를 포함하는 JavaScript ArrayBuffer
const xhr = new XMLHttpRequest();
xhr.responseType = "arraybuffer";
// "blob"
// 이진 데이터를 포함하는 Blob 객체
const xhr = new XMLHttpRequest();
xhr.responseType = "blob";
// "document"
// HTML Document 또는 XML XMLDocument
const xhr = new XMLHttpRequest();
xhr.responseType = "document";
// "json"
// JSON 구문으로 만든 JavaScript 객체
const xhr = new XMLHttpRequest();
xhr.responseType = "json";
// "text"
// 텍스트 문자열
const xhr = new XMLHttpRequest();
xhr.responseType = "text";
- 241202
'JavaScript' 카테고리의 다른 글
| [JavaScript] HTML 요소 안에서 inline 으로 이벤트 걸어줄 때 이벤트 객체 받아오기 (0) | 2025.01.30 |
|---|---|
| [JavaScript] 목록 채우기 - 퍼블리싱이 적용된 템플릿을 복사하여 채우기 (0) | 2025.01.30 |
| [JavaScript] 숫자만 입력 가능한 정규식 조건 만들기 (0) | 2023.04.11 |
| [JavaScript] 문자열을 그대로 html 태그로 DOM에 추가하고 싶을 때 (0) | 2023.01.16 |
| [JavaScript] 값이 null 일 경우 parameter 에 key 조차 존재하면 안 될 때 (0) | 2023.01.09 |