본문 바로가기
javascript/javascript

[javascript] 이벤트 전파 막기 (preventDefault, stopPropagation)

by 산코디 2022. 12. 25.
반응형

오늘은 자바스크립트의 기능 중 이벤트의 전파와 관련된 preventDefault와 stopPropagation에 대해서 정리해 보려고 한다.
이벤트 전파를 막는 것은 웹 애플리케이션에서 중요한 기능 중 하나다. 이벤트 전파에 대해 처리를 제대로 하지 않고 기능을 구현한다면 잠재적인 오류가 빈번히 발생할 수 있고, 중복된 이벤트가 발생하여 서비스의 성능이 문제가 될 수 있다. 특히 UI를 담당하는 자바스크립트를 학습하고 있다면 자바스크립트는 동적인 처리를 많이 하기 때문에 반드시 이벤트 전파에 대한 내용을 숙지하는 것이 중요하다.




이벤트 전파란?

자바스크립트에서 이벤트 전파란 한 요소에서 발생한 이벤트가 그 요소의 부모나 자식 요소에 전달되는 현상을 말한다. 

캡처링 단계 (Capturing phase)
이벤트가 최상위 요소에서부터 시작하여 이벤트를 발생 시킨 요소를 향해 내려가는 단계다.

타겟 단계 (Target phase)
이벤트를 발생 시킨 요소에 도달하는 단계이며, 이벤트 핸들러가 실행된다.

버블링 단계 (Bubbling phase)
이벤트가 다시 최상위 요소 방향으로 전파되는 단계다.

이벤트 전파는 DOM 트리의 구조를 따라 이루어지며, 일반적으로는 캡처링 단계에서 시작하여 버블링 단계까지 진행된다. 그러나 이벤트 핸들러에서 stopPropagation() 메서드를 호출하면 해당 이벤트가 현재 요소에서 더 이상 전파되지 않도록 중단할 수 있다.

이벤트 전파는 이벤트 위임 패턴에 활용되기도 한다. 이벤트 위임은 부모 요소에 이벤트 리스너를 하나만 등록하여 자식 요소들의 이벤트를 처리하는 방식이다. 이렇게 하면 동적으로 추가되는 요소에도 이벤트를 처리할 수 있으며, 메모리 사용과 성능을 개선할 수 있다.



preventDefault

preventDefault 메서드는 주로 링크를 클릭했을 때 해당 링크로의 이동을 막을 때 사용된다. 예를 들어, HTML 폼 내에서 <a> 태그를 클릭하면 일반적으로 해당 링크로 이동한다. 그러나 이벤트 핸들러에서 preventDefault() 메서드를 호출하면 해당 기본 동작을 취소할 수 있다. 이를 통해서 링크 클릭 시 페이지 이동을 방지하고 javascript 내에서 원하는 작업을 수행할 수 있다.

그럼 예제 코드를 통해서 자세히 알아보자.

preventDefault 메서드 적용 전 예제 소스 코드

<script>
/**
 * Document load 
 */
document.addEventListener('DOMContentLoaded', () => {
    
    /**
     * 체크박스 cliek event 실행
     */
    document.querySelector('#eventCheck').addEventListener('click', function (event) {
        document.querySelector('#resultContent').textContent = '이벤트 방지 전';
    });
});

</script>
<div>preventDefault</div>
<hr>
<div>
    <input type="checkbox" id="eventCheck"/>이벤트 방지
</div>
<br>
<span id="resultContent"></span>

위의 소스 코드는 preventDefault를 실행하기 전의 상태입니다. 간단하게 체크박스를 클릭하면 체크박스의 체크 상태를 처리하는 기능이 실행된다.
체크박스를 클릭하면 click 이벤트가 실행되고, 이벤트 핸들러에서는 #resultContent 요소의 내용을 '이벤트 방지 전'으로 설정한다.

하지만 위의 코드에서는 이벤트 핸들러에서 preventDefault() 메서드를 호출하지 않았기 때문에, 체크박스를 클릭하면 기본 동작이 발생하게 된다. 따라서 체크박스를 클릭하면 체크박스가 체크되거나 해제된다.

체크 표시되는 체크박스

그러면 이어서 preventDefault를 설정해 보자.

 

preventDefault 적용한 예제 소스 코드

<script>
/**
 * Document load 
 */
document.addEventListener('DOMContentLoaded', () => {
    
    /**
     * 체크박스 cliek event 실행
     */
    document.querySelector('#eventCheck').addEventListener('click', function (event) {
        // 이벤트 방지 실행
        event.preventDefault();

        document.querySelector('#resultContent').textContent = '이벤트 방지 후';
    });
});

</script>
<div>preventDefault</div>
<hr>
<div>
    <input type="checkbox" id="eventCheck"/>이벤트 방지
</div>
<br>
<span id="resultContent"></span>

위의 코드는 preventDefault() 메서드가 사용되어 체크박스 클릭 이벤트의 기본 동작을 방지하는 코드다.

체크박스를 클릭하면 click 이벤트가 실행되고, 이벤트 핸들러에서는 먼저 event.preventDefault()를 호출하여 기본 동작을 방지한다. 따라서 체크박스를 클릭해도 기본적인 체크박스의 동작(체크 및 해제)이 발생하지 않는다.

그 후에 #resultContent 요소의 텍스트를 '이벤트 방지후'로 변경한다.

즉 preventDefault() 메서드를 사용하면 이벤트의 기본 동작을 취소할 수 있다. 이것은 특히 링크를 클릭했을 때 새로운 페이지로 이동하는 동작이나, 폼 요소를 제출했을 때 페이지가 다시 로드되는 동작을 방지할 때 유용하다.

체크 표시가 되지 않는 체크박스

위의 결과와 같이 preventDefault 이벤트가 실행되어 체크박스의 동작이 실행되지 않는 모습이다.

 


stopPropagation

stopPropagation() 메서드는 이벤트의 전파를 중지하는 데 사용된다. 이벤트가 한 요소에서 발생하고 그 부모 요소로 전파되는 것을 방지하여 이벤트의 버블링(bubbling)을 막는다.

예를 들어, 하위 요소를 포함하는 상위 요소에 이벤트 리스너가 있을 때, 하위 요소에서 이벤트가 발생하면 이벤트는 상위 요소로 전파된다. 하지만 stopPropagation()을 사용하면 이벤트가 현재 요소에서만 처리되고 상위 요소로 전파되지 않는다.

propagation() 메서드 사용 전 예제 소스 코드

<script>
/**
 * Document load 
 */
document.addEventListener('DOMContentLoaded', () => {

    /**
     * outer div click event
     */
    document.querySelector('.outer_div').addEventListener('click', function (event) {
        let content = document.querySelector('#resultContent');
        content.innerHTML = content.innerHTML + 'outer DIV<br>';
    });

    /**
     * inner div click event
     */
    document.querySelector('.inner_div').addEventListener('click', function (event) {
        let content = document.querySelector('#resultContent');
        content.innerHTML = content.innerHTML + 'inner DIV<br>';
    });
});
</script>

<div>stopPropagation</div>
<hr>
<div>
    <div class="outer-div outer_div">
        <div class="inner-div inner_div"></div>
    </div>
</div>
<br>
<span id="resultContent"></span>

<style>
.outer-div {
    background  : #7cd67c;
    width       : 150px;
    height      : 150px;
}
.inner-div {
    background  : #ffa249;
    width       : 90px;
    height      : 90px;
}
</style>

위의 소스 코드는 propagation() 메서드를 사용하지 않은 예제 코드다. 두 개의 <div> 요소가 중첩되어 있는데, 외부 요소는 내부 요소를 감싸고 있으며, 각각의 <div> 요소에는 클래스 이름이 지정되어 있다.

외부 <div>와 내부 <div>에 각각 클릭 이벤트 리스너가 추가되어 있다. 외부 <div>의 클릭 이벤트 리스너는 'outer DIV'를 결과 창에 출력하고, 내부 <div>의 클릭 이벤트 리스너는 'inner DIV'를 결과 창에 출력한다. 

하지만 내부 <div>를 클릭할 때 외부 <div>의 클릭 이벤트도 발생하는 이유는 이벤트가 이벤트 버블링에 의해 상위 요소로 전파되기 때문이다. 내부 <div>를 클릭하면 내부 이벤트 핸들러가 실행되고, 그다음 외부 이벤트 핸들러가 실행된다.


실행 결과

inner div의 이벤트 전파되는 현상

위의 결과 화면과 같이  outer div영역을 click 했을 경우에는 겹쳐지는 부분이 없어서 전파되지 않고 'outer DIV' 문구 정상 출력을 한다.하지만 inner div영역을 click 했을 경우에는 outer div영역과 겹쳐지면서 outer div영역의 click event가 동시에 실행이 된다.

그러면 stopPropagation 메서드를 실행하여 전파가 되지 않도록 수정해 보자.

 


inner_div click event에서 stopPropagation 함수를 호출하는 소스 코드

/**
 * inner div click event
 */
document.querySelector('.inner_div').addEventListener('click', function (event) {
    // 이벤트 전파 방지
    event.stopPropagation();

    let content = document.querySelector('#resultContent');
    content.innerHTML = content.innerHTML + 'inner DIV<br>';
});

위의 코드는 내부 <div> 요소의 클릭 이벤트 핸들러에 stopPropagation() 메서드가 추가된 것이다. stopPropagation() 메서드는 현재 이벤트가 상위 요소로의 전파를 중지시키는 역할을 하기 때문에 이 경우 내부 <div>를 클릭할 때 이벤트가 더 이상 상위 요소로 전파되지 않으므로 외부 <div>의 클릭 이벤트가 실행되지 않는다.

실행 결과

inner div 이벤트 전파 방지

함수 호출 후 이제 이벤트가 전파되는 현상이 없어지고 정상적으로 각각의 영역 이벤트만 실행되고 있다.

이런 현상은 자바스크립트 개발을 하다 보면 실제 화면에서 자주 보이는 현상이다. 실제 화면을 개발하는 경우에는 복잡한 화면도 많고, 한 화면에서 구현되는 기능도 많기 때문에 이벤트가 중복되어 실행되는 경우가 많다. 물론 상황에 따라서 일부러 이벤트가 겹치도록 할 순 있지만, 특수한 경우에 아니라면 중복을 방지해줘야 한다. 그래서 나는 이벤트를 생성할 때 습관처럼 중복을 확인하고 전파 방지 코드를 추가해 준다. 그냥 습관을 들이는 것이 좋다.


preventDefault()와 propagation() 메서드 사용 시 주의사항

preventDefault()와 propagation() 메서드를 사용할 때에 몇 가지의 주의사항이 있다. 상황에 따라서 굳이 사용하지 않아도 되는 경우가 있지만, 보통의 경우에는 이벤트의 중복은 이슈가 될 수 있으므로 사용을 하는 것이 좋은데, 사용하기 전에 주의사항을 살펴보는 것이 좋다.

사용 목적
preventDefault는 기본 동작을 취소하는 데 사용되며, stopPropagation은 이벤트의 전파를 중지하는 데 사용된다. 이 두 메서드를 올바르게 사용하려면 각 메서드의 목적을 명확히 이해하는 것이 중요하다.
preventDefault : 기본 동작 취소
propagation : 이벤트 전파 중지

오버 사용
preventDefault와 propagation 메서드는 필요한 경우에만 사용하는 것이 좋다. 불필요한 preventDefault 또는 stopPropagation 호출은 의도하지 않은 동작을 초래할 수 있으므로, 반드시 필요한 경우인지 고려해 본 후 적용을 해야 한다.

선언 위치
이벤트 핸들러 내에서 preventDefault 또는 stopPropagation을 호출하기 전에 항상 원하는 동작이 발생하는지 확인해야 한다. 특히 다른 이벤트 핸들러에 의해 덮어씌워지지 않도록 주의해야 한다.

테스트
preventDefault와 propagation 메서드를 사용하는 경우에는 코드를 테스트하여 예상대로 작동하는지 확인하는 것이 중요하다. 브라우저 콘솔을 사용하여 이벤트 흐름을 파악하고 예기치 않은 동작을 찾는 것이 중요하다.


마무리

오늘은 이렇게 preventDefault와 stopPropagation 함수에 대해 정리해 보았다. 자바스크립트를 배우고 학습하다 보면 동적인 움직임, 효과 등을 주며 이벤트를 처리하기 때문에 업무가 재미있지만, 그만큼 이벤트 간의 이슈가 발생할 수 있는 상황이 있다는 것이다. 그래서 오늘 정리한 내용과 같이 상황에 따라서 기본 동작을 취소하는 preventDefault, 이벤트 전파를 중지시켜 주는 propagation 메서드에 대해서 학습하고 적절한 상황에 맞게 기능을 구현하는 것이 좋다.

 

반응형