본문 바로가기
  • 구름빵의 개발 블로그
Front-End/JavaScript,TypeScript

[JavaScript] Event 다루기

by 은빛구름빵 2025. 9. 9.

개요

프론트엔드에서 제일 중요한 것중 하나가 이벤트를 다루는 것이라고 생각한다.

사용자의 요청을 받는 수단으로써 이벤트를 원하는대로 다룰 수 있는 것이 프론트엔드에서 제일 중요한 능력 중 하나라고 생각한다.

 

이벤트 종류

이벤트 종류에는 굉장히 많이 있다. 그 중에서 대표적인 것들 몇 가지만 나열해보고자 한다.

1. 마우스 이벤트

마우스 이벤트에는 아래와 같은 이벤트들이 대표적이다.

- click: 마우스 단일 클릭

- Dbclick: 마우스 더블 클릭

- contextmenu: 마우스 우클릭

- mouseover: 특정 요소에 마우스 커서가 올라갔을 때

- mousemove: 마우스 커서를 움직일 때

- mouseout: 마우스 커서가 특정 요소 밖으로 벗어났을 때

- mousedown: 마우스를 눌렀을 때 ( = 홀드할 때를 의미한다. 따라서 click = down + up이 빠른 시간에 일어날 때를 의미한다 )

- mouseup: 마우스 버튼을 떼었을 때

 

2. 키 이벤트

키보드로부터 입력을 받는 이벤트이다. 특정 키가 입력되었을 때, 혹은 특정 키에 이벤트를 매핑할 때 많이 사용된다.

- keydown: 키보드를 눌렀을 때

- keyup: 키보드를 떼었을 때

- keypress: 키보드를 누르고 있는 상태일 때

 

3. 폼 이벤트

다양한 형태의 데이터들을 전달할 때 사용하는 Form 객체에 대한 이벤트이다. HTML의 form 태그에 대한 이벤트를 처리한다.

- focus: 특정 요소에 포커스가 이동 되었을 때

- blur: 특정 요소에서 포커스가 벗어났을 때

- change: 특정 요소 내부의 값이 변했을 때

- submit: submit 타입의 버튼을 눌렀을 때

- reset: reset 타입의 버튼을 눌렀을 때

- select: input이나 textarea 요소 내에 있는 텍스트를 드래그해서 선택했을 때

 

4. 그 외

위에 말한 분야 외에도 윈도우 창이나 문서에 대한 이벤트도 존재한다. 

- load: 페이지 로딩이 완료되었을 때

- abort: 이미지 로딩이 중단되었을 때

- unload: 페이지를 다른 곳으로 이동할 때

- resize: 요소의 사이즈가 변경될 때

- scroll: 스크롤 바를 움직였을 때

 

이벤트 핸들러

위와 같은 이벤트를 인식하고 처리할 수 있도록 하기 위한 이벤트 감지 객체이다.

특정 요소의 특정 이벤트에 대해 로직을 처리할 수 있고 함수를 매핑할 수 있게 해준다.

특정 요소에 이벤트 핸들러를 등록하는 방법은 다양하게 존재한다.

// 01. in-line 방식
<button type='button' onclick="changeColor();"> Button </button>

// 02. addEventListener 사용
<button type='button' id='btn'> Button </button>

<script>
	const btnElement = document.getElementById( 'btn' );
    // 요소.addEventListener( 이벤트 타입, 함수 )
	btnElement.addEventListener( 'click', changeColor );
</script>

 

이벤트 핸들러를 등록하면 제거하기 전까지는 유지된다. 

상황에 따라 특정 요소에 연결된 이벤트 리스너를 제거해야 하는 경우도 있다.

이런 경우 아래와 같이 사용하면 이벤트 리스너를 제거할 수 있다.

// 요소.removeEventListener( 이벤트 타입, 함수 );
btn.removeEventListener( 'click', changeColor );

위와 같이 하면 특정 요소의 특정 이벤트 타입에 매핑된 특정 함수를 제거할 수 있다.

 

이벤트 객체

이벤트를 더 효율적이고 다양하게 사용하기 위해 이벤트 객체를 사용하는 경우가 많다.

이벤트 객체의 경우, e라는 값으로 받아와서 사용할 수 있다.

btn.addEventListener( 'click', (e) => {
	console.log( e.target );
});

위처럼 파라미터로 e라는 값을 받고 이를 함수 내에서 사용해주면 이벤트 객체를 사용할 수 있다.

이 이벤트 객체에는 이벤트에 대한 다양한 내용이 들어가 있다.

이 중에서 자주 사용하는 것들만 아래 작성해두었다.

function changeColor( e ) {
	// 이벤트가 발생한 요소를 가져올 때 사용: target
	console.log( e.target );
    // 이벤트 리스너가 등록된 요소를 가져올 때 사용: currentTarget
    console.log( e.currentTarget );
}

target과 currentTarget은 엄연히 다르다.

이벤트 리스너는 이벤트 전파라는 개념이 있기 때문에 이벤트 리스너의 상하위 요소들에서 이벤트가 작동하는 경우도 있다.

이 부분에 대해 자세히 알아보자.


<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title> Examples of Event ECMAScript </title>
        <style>
            #div1 {
                background-color: red;
                width: 500px;
                height: 500px;
            }
            #div2 {
                background-color: green;
                width: 300px;
                height: 300px;
                top: 100px;
                left: 100px;
                position: relative;
            }
            #div3 {
                background-color: blue;
                width: 100px;
                height: 100px;
                top: 100px;
                left: 100px;
                position: relative;
            }
        </style>
    </head>
    <body>
        <div id='div1'>
            <div id='div2'>
                <div id='div3'></div>
            </div>
        </div>

        <script>
            const div1 = document.getElementById( 'div1' );
            
            div1.addEventListener( 'click', (e) => {
                console.log( "target: ", e.target.id );
                console.log( "Add on: ", e.currentTarget.id );
            });
        </script>
    </body>

</html>

위 코드를 실행시켜서 각 사각형 범위를 눌러보면 currentTarget으로는 우리가 직접 이벤트 리스너를 연결한 div1이라는 것이 나오는 반면 빨강, 초록, 파랑 범위를 누르면 각 범위의 실제 요소들이 나온다는 것을 알 수 있다.

즉, 우리는 div1이라는 하나의 요소에 이벤트 리스너를 연결했음에도 해당 요소의 하위 요소들에게도 이벤트 리스너가 작동한다는 것을 알 수 있다.

 

이벤트 전파

위와 같이 하위 요소들에도 이벤트 리스너가 영향을 미치는 것을 이벤트 전파라고 한다.

이벤트가 전파되는 것이 기본이며, 이 방향을 정할 수도 있고 전파를 차단할 수도 있다.

 

이벤트 전파 방향에 따라 Capturing과 Bubbling으로 나눌 수 있다.

이벤트 전파 방향이 상위 요소에서 하위 요소로 오는 거라면 Bubbling

이벤트 전파 방향이 하위 요소에서 상위 요소로 가는 거라면 Capturing 이라고 한다.

<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title> Examples of Event ECMAScript </title>
        <style>
            #div1 {
                background-color: red;
                width: 500px;
                height: 500px;
            }
            #div2 {
                background-color: green;
                width: 300px;
                height: 300px;
                top: 100px;
                left: 100px;
                position: relative;
            }
            #div3 {
                background-color: blue;
                width: 100px;
                height: 100px;
                top: 100px;
                left: 100px;
                position: relative;
            }
        </style>
    </head>
    <body>
        <div id='div1'>
            <div id='div2'>
                <div id='div3'></div>
            </div>
        </div>

        <script>
            const div1 = document.getElementById( 'div1' );
            const div2 = document.getElementById( 'div2' );
            const div3 = document.getElementById( 'div3' );
            
            div3.addEventListener( 'click', (e) => {
                console.log( 'this is div3 event listener' )
            } );

            div2.addEventListener( 'click', () => {
                console.log( 'this is div2 event listener' )
            });
			// Bubbling
            div1.addEventListener( 'click', () => {
                console.log( 'this is div1 event listener' )
            }, false );
            // Capturing
            div1.addEventListener( 'click', () => {
                console.log( 'this is div1 event listener' )
            }, true );
        </script>
    </body>

</html>

위 코드에서 Bubbling 결과를 보면 div3 -> div2 -> div1 에 연결된 이벤트 리스너 순서대로 작동한다.

반대로 Capturing 결과를 보면 div1 -> div2 -> div3 순서로 이벤트 리스너가 작동하는 것을 볼 수 있다.

이처럼 이벤트 리스너가 작동하면 이벤트는 전파되어 자신을 포함하고 있는 상위 요소에게 흐르게 된다.

 

자신을 최하위에 두고 상위로 존재하는 모든 요소에 이벤트를 전파하게 되며, Capturing / Bubbling에 맞게 이벤트 작동 순서만 조절하게 된다.

 

하지만 상황에 따라 이벤트 전파를 차단하고자 할 때가 있다.

이런 경우, 아래와 같이 작성해주면 이벤트 전파를 차단할 수 있다.

const div1 = document.getElementById( 'div1' );
const div2 = document.getElementById( 'div2' );
const div3 = document.getElementById( 'div3' );

div3.addEventListener( 'click', (e) => {
    console.log( 'this is div3 event listener' )
    e.stopPropagation();
} );

div2.addEventListener( 'click', () => {
    console.log( 'this is div2 event listener' )
});

div1.addEventListener( 'click', () => {
    console.log( 'this is div1 event listener' )
}, false );

div3 요소에 stopPropagation()이라는 함수를 수행하도록 지정했다. 이는 이 요소에서 전파를 진행하지 않는다는 의미이다.

따라서, div1과 div2는 전파를 허용하나 div3은 허용하지 않는다.

예제를 실행해보면 div1과 div2를 눌렀을 때는 전파가 되는 것을 볼 수 있지만 div3을 누르면 console에 div3만 나오고 div1과 div2는 나오지 않는 것을 볼 수 있다.

이처럼 부모 요소들에 이벤트 전파를 하지 않도록 제한할 수도 있다.

 

기본 이벤트 방지

이 외에 특이한 점으로 일부 HTML 태그의 경우 이벤트를 자체적으로 갖고 있는 경우가 있다.

예를 들어, a태그의 경우 Text를 주소로 삼아 클릭하면 해당 주소로 이동하는 것이 기본이며, button의 submit 타입의 경우 클릭하면 상위 form 객체를 전달하는 것이 기본이다.

하지만 상황에 따라 이런 기본 이벤트를 제한해야 할 상황이 생긴다.

이런 경우 아래와 같이 사용하면 이를 방지할 수 있다.

const aElement = document.getElementById( 'aElement' );

aElement.addEventListener( 'click', (e) => {
	e.preventDefault();
});

위와 같이 만들면 aElement를 클릭해도 a태그의 기본 기능이 작동하지 않는다.

 

결론

JavaScript를 다루면서 제일 중요한 이벤트에 대해 알아볼 수 있었다. 

항상 형식적으로만 사용해왔지 정확히 어떤 기능을 갖고 있고 왜 그렇게 하는지 이해하지 못하거나 잘못 알고 있던 부분이 많았다.