2024. 11. 20. 14:59ㆍ기술 창고/Javascript
쿠폰을 발급하는 특정 프로젝트를 진행하다가 발급 버튼을 누르면 중복으로 쿠폰이 발급되는 이슈가 발생되었습니다.
클릭 이벤트나 함수 호출 또한 한 번만 되게끔 되어있는데 어째서 중복으로 실행이 되는지 의문이라 검색해보니 이벤트 버블링 (Event Bubbling) 이라고 하는 현상을 알게 되었습니다.
이벤트 버블링은 동작 이벤트를 가지고 있는 태그가 중첩으로 구성되었을 때, 해당 동작 이벤트를 실행하게 되면 중첩된 모든 동작 태그들에 한하여 중복으로 이벤트가 발생되는 현상을 말합니다.
[HTML]
<div class="modal-footer">
<button type="button" class="btn btn-secondary col-5" data-bs-dismiss="modal">취소</button>
<a aria-current="page" class="col-7">
<button type="button" class="btn btn-primary col-12" id="getCouponButton" onclick="getCoupon()">
확인
</button>
</a>
</div>
제가 처음 만든 모달에 존재하는 쿠폰 발급 버튼입니다.
보시는 것처럼 a 태그 안에 button이 중첩되어 들어가 있고, 버튼을 클릭하면 getCoupon() script 함수가 실행되어 쿠폰이 발급되는 것인데, 이 부분이 중복으로 실행되었던 것입니다.
즉, button 태그의 동작(onclick) 이 먼저 실행되고, 부모인 중첩된 a 태그가 다시 한번 button 태그의 동작을 실행하게 되는 것입니다.
일반적으로 거의 모든 이벤트들은 버블링 현상이 발생됩니다.
제가 만든 쿠폰 발급과 같은 기능이 아닌 이상에야 반드시 막을 필요도 없습니다.
저의 경우에는 쿠폰이 중복 발급되는 일은 발생되면 안되는 것이기 때문에 막아야될 필요가 있었습니다.
버블링 이벤트 막기
이벤트 버블링을 막기 위한 방법으로 두 가지를 테스트해보았습니다.
[HTML]
<div class="modal-footer">
<button type="button" class="btn btn-secondary col-5" data-bs-dismiss="modal">취소</button>
<a aria-current="page" class="col-7">
<!-- 기존의 onclick 속성을 삭제 -->
<button type="button" class="btn btn-primary col-12" id="getCouponButton">
확인
</button>
</a>
</div>
[Javascript]
# 태그의 disabled 속성을 활용한 방법
$(document).ready(function () {
// 쿠폰 발급 하기 버튼 동작 설정
document.getElementById("getCouponButton")
.addEventListener("click", function(e){
// 더블링 이벤트 발생 시 중복을 막기 위한 disabled 처리
if(this.getAttribute("disabled") === true){
return;
}
// 쿠폰 발급 버튼 비활성화
this.setAttribute("disabled", true);
fetch("{쿠폰 발급 api url}", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
couponInfo: document.getElementById('couponInfo').value,
goodsSeq: goodsSeq,
goodsId: goodsId,
})
})
.then(response => response.json())
.then((data) => {
// 성공 후 버튼 활성화
this.setAttribute("disabled", false);
})
.catch((error) => {
// 실패 후 버튼 활성화
this.setAttribute("disabled", false);
alert("쿠폰 발급에 실패하였습니다.");
});
});
})
첫 번째는 버튼에 Event를 달아줄 때, 버튼이 클릭하게 되자 마자 해당 버튼에 disabled (비활성화) 속성을 달아주어 후속 api 실행 로직이 실패든 성공이든 완전히 실행될 때까지 비활성화 시켜 중복을 방지하는 방법입니다.
만약 중복으로 실행되려고 할 경우 disabled 속성의 유무에 따라 중복 접근이 차단될 것입니다.
# event 지원 함수를 활용한 방법
$(document).ready(function () {
// 쿠폰 발급 하기 버튼 동작 설정
document.getElementById("getCouponButton")
.addEventListener("click", function(e){
// 쿠폰 발급 클릭 이벤트 봉쇄
e.preventDefault();
fetch("{쿠폰 발급 api url}", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
couponInfo: document.getElementById('couponInfo').value,
goodsSeq: goodsSeq,
goodsId: goodsId,
})
})
.then(response => response.json())
.then((data) => {
alert("쿠폰 발급에 성공하였습니다.");
})
.catch((error) => {
alert("쿠폰 발급에 실패하였습니다.");
});
});
})
두 번째 방법은 event를 지원하는 함수를 사용하는 것입니다.
preventDefault 는 기본 동작 이벤트를 막는 함수입니다.
따라서, 처음에 이벤트를 버튼에 달아주게 되고, 버튼을 클릭하게 되면, 일단 기본 동작을 막고 이후 api 실행을 하게 되어 중복으로 동작 이벤트가 발생되는 것을 막아줍니다.
+ stopPropagation
preventDefault 말고도 stopPropagation 함수가 있는데, stopPropagation은 동작을 실행하게 될 때, 상위 DOM 요소에 이벤트가 전파되는 것을 막는 함수입니다.
중첩 실행이 되는 것이 문제이기 때문에 preventDefault 대신 stopPropagation을 사용해도 문제는 없을 것입니다.
+ stopImmediatePropagation
stopPropagation 함수는 stopPropagation 을 적용한 요소의 상위 요소들에 한해서 이벤트가 전파되는 것을 방지하지만, 다른 요소들의 동작 이벤트를 막지는 않는 다면, stopImmediatePropagation 은 이 모두를 막아버리는 함수입니다.
이제 실행해서 버튼을 클릭해보면 한 번만 동작 이벤트가 실행되는 것을 확인할 수 있습니다.
'기술 창고 > Javascript' 카테고리의 다른 글
[Javascript] canvas 캡쳐 후 다운로드 (3) | 2024.11.12 |
---|---|
[Javascript] 이미지 및 텍스트 데이터 (+ 바코드 생성 데이터) 를 통합하여 하나의 데이터로 canvas 화 (4) | 2024.11.12 |
[Javascript] Object, Array Destructuring (분해) (0) | 2023.08.03 |
[Javascript] Arrow function (화살표 함수) (0) | 2023.08.01 |
[Javascript] Javascript ES6 Map(매핑) / Filter(필터) / Reduce(감소) / find(찾기) / findIndex(인덱스 찾기) (0) | 2023.08.01 |