이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ

게시글

강좌23 - JavaScript - 2년 전 등록 / 4달 전 수정

이벤트 리스너와 콜백(event listener, callback)

조회수:
0
이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ
이 블로그는 광고 클릭 수익으로 운영됩니다!
괜찮으시다면 광고 차단을 풀어주세요 ㅠㅠ

안녕하세요. 이번 시간은 이벤트 리스너콜백 차례입니다.

지금까지는 이벤트 리스너를 안 다뤘습니다만 이제 다룰 때가 된 것 같습니다. 이벤트 리스너와 콜백 방식도 자바스크립트에서 많이 사용되는 방식입니다. 클로저나 IIFE처럼요. 그리고 반드시 알아두어야 합니다. 이 부분에서 많은 입문자들이 고통받습니다.

이벤트 리스너

이벤트 리스너는 말 그대로 해당 이벤트에 대해 대기중인 겁니다. 항상 리스닝 중이죠. 해당 이벤트가 발생했을 때 등록했던 이벤트 리스너가 실행됩니다.

window.onload = function () {
  alert('I\'m loaded');
};

위의 코드를 보신 적이 있을지 잘 모르겠습니다만, 이 코드가 대표적인 이벤트 리스너입니다. window가 load될 때에 function 부분이 실행되는거죠. load됐다는 것을 누가 알려주냐면, 브라우저가 알려줍니다. 비슷한 코드로, 그리고 자주 쓰이는 코드로 onclick이 있습니다. 이벤트 리스너는 항상 on + '이벤트명'으로 명명됩니다.

자주 쓰이는 이벤트의 목록을 알려드리자면, onblur(객체가 focus를 잃었을 때), onchange(객체의 내용이 바뀌고 focus를 잃었을 때), onclick(객체를 클릭했을 때), ondblclick(더블클릭할 때), onerror(에러가 발생했을 때), onfocus(객체에 focus가 되었을 때), onkeydown(키를 눌렀을 때), onkeypress(키를 누르고 있을 때), onkeyup(키를 눌렀다 뗐을 때), onload(문서나 객체가 로딩되었을 때), onmouseover(마우스가 객체 위에 올라왔을 때), onmouseout(마우스가 객체 바깥으로 나갔을 때), onreset(Reset 버튼을 눌렀을 때), onresize(객체의 크기가 바뀌었을 때), onscroll(스크롤바를 조작할 때), onsubmit(폼이 전송될 때) 등이 있습니다. 이외에도 몇 가지 더 있으니 찾아보세요.

document.getElementById('clickMe').onclick = function () {
  alert('I\'m clicked!');
};

window 말고도, 여러 태그에 각각 이벤트를 설정할 수 있습니다. 모든 DOM들이 이벤트 리스너를 등록할 수 있는 속성들을 갖고 있습니다. 상상할 수 있는 간단한 이벤트는 거의 다 있습니다. 마우스클릭, 키보드 입력, 드래그, 마우스올려놓기 등등요.

이벤트를 붙이는 다른 방법으로 addEventListener가 있습니다. 저는 on으로 붙이는 것보다 이 방식을 더 추천합니다. 여러 이벤트를 등록할 수 있고, 특정 이벤트를 제거(removeEventListener)할 수도 있거든요.

function onClick() {
  alert('I\'m clicked!');
}
function onClick2() {
  alert('또다른 이벤트');
}
document.getElementById('clickMe').addEventListener('click', onClick); // 이벤트 연결
document.getElementById('clickMe').addEventListener('click', onClick2); // 또 하나의 이벤트 연결
document.getElementById('clickMe').removeEventListener('click', onClick); // 연결할 이벤트 중 하나 제거

이렇게 click 위치에는 다른 이벤트에서 on만 떼고 이벤트명을 집어넣으면 됩니다.

콜백

위 두 코드에서 function 부분을 부르는 게 콜백이라고 합니다. 영어로는 callback인데 call + back 즉, 다시 전화주는 거죠. 이벤트가 실행됐을 때, 사용자에게 다시 알려주는 겁니다. onload, onclick 같은 속성에 콜백을 등록하면 됩니다. 위처럼 대입할 수도 있고요.

꼭 이벤트가 아니더라도 콜백을 활용할 수 있습니다. 주어진 숫자부터 1까지 더하는 간단한 함수를 만들어 보겠습니다.

var cbExample = function(number, cb) {
  setTimeout(function() {
    var sum = 0;
    for (var i = number; i > 0; i--) {
      sum += i;
    }
    cb(sum);
  }, 0);
};
cbExample(10, function(result) {
  console.log(result);
}); // 55
console.log('first');

위의 예를 잘 보세요. cbExample 함수의 매개변수 cb가 바로 콜백 함수를 받는 부분입니다. cbExample을 호출할 때 두 번째 인자로 function을 넣은 게 보이죠? 그 함수가 콜백함수로 등록되어 계산이 끝난 후 실행됩니다. 콜백만 등록해두면 계산이 끝난 후 알아서 알려주죠. 위의 예시를 콘솔에 쳐보시면 first가 55보다 아래 줄에 있음에도 먼저 실행됩니다.

여기서 주의할 게 setTimeout 부분입니다. setTimeout 함수를 사용해야 비동기적으로 실행됩니다(0초만에 실행하게 설정해놨는데도 비동기적으로 실행됩니다). 이 부분을 빠뜨리면 55가 first보다 먼저 표시됩니다. 콜백의 의미가 없어지는 거죠.

위의 예에서는 워낙 간단한 계산이라 별로 효용을 느끼지 못하지만, 만약 cbExample의 일이 10초가 걸리는 일이라한다면, cbExample이 끝나고 다음 코드를 실행할 때까지 10초나 기다려야합니다. 그러지말고 콜백을 등록해둔 다음에 바로 다음 코드로 넘어가는 겁니다. 알아서 일이 완료된 후에 알려주도록 만들고요. 이렇게 콜백은 언제 끝날 지 모르는 동작에 대해 그 결과를 전달받을 때 유용합니다. 하염없이 기다릴 필요없이 다 됐을 때 알아서 알려주니까 엄청 편하죠. 꼭 기억해두세요!

이벤트 버블링

이벤트 버블링이라고 들어보셨나요? DOM에 연결한 이벤트는 버블링이 일어납니다. 버블링이란 자식의 이벤트가 부모에도 전달되는 것을 말합니다.

<div id="first"><div id="second"><div id="third"></div></div></div>

위와 같은 구조가 있을 때 div#third를 클릭한 경우, 부모와 조상 태그인 second, first 순으로 같은 클릭 이벤트가 발생합니다. 이 현상을 기억해두세요.

이벤트 객체

DOM에 대한 이벤트에 연결한 함수는 이벤트 객체를 매개변수로 사용할 수 있습니다. 이벤트 객체에는 이벤트에 대한 정보들과 이벤트를 조작하는 메소드들이 들어있습니다.

document.onclick = function(event) {
  event.preventDefault();
  event.stopPropagation();
  event.stopImmediatePropagation();
};

대표적인 메소드로 preventDefaultstopPropagation, stopImmediatePropagation이 있습니다. preventDefault 메소드는 태그의 기본 동작(예를 들면, a 태그는 클릭 시 링크이동, form 태그은 폼 내용 전송)을 하지 않게 막아주는 역할을 합니다. stopPropagation 메소드는 태그를 클릭 시 부모에게 이벤트가 전달(버블링)되지 않도록 합니다.

stopImmediatePropagation은 버블링을 막음과 동시에 같은 이벤트의 다른 리스너도 실행되지 않도록 합니다. 만약 여러 개의 클릭 이벤트를 동시에 연결한 경우, stopPropagation으로 클릭 이벤트를 막아도 다른 클릭 이벤트는 실행됩니다. 하지만 stopImmediatePropagation으로 클릭 이벤트를 막으면 부모에게는 어떠한 이벤트도 버블링되지 않으면서 다른 클릭 이벤트도 실행되지 않습니다. 단, 다른 종류의 이벤트(마우스 오버 등)는 막지 못합니다.

메소드 외에도 이벤트 객체에는 많은 정보들이 들어있습니다. event.target 안에 이벤트가 발생한 태그의 정보가 들어갑니다. 클릭을 했을 때는 event.pageX, event.pageY로 클릭한 좌표를 알 수 있고, 키보드를 친 경우에는 event.key로 어떤 키를 쳤는지 알 수 있습니다. 이벤트 객체를 활용해서 다양한 이벤트를 만들어보세요!

주의사항

가끔 html 자체에 이벤트 리스너를 연결하시는 분이 있습니다.

<button onclick="showResult()">클릭</button>

이 형식은 별로 권장하지는 않습니다. HTML과 자바스크립트는 분리가 원칙입니다. 그래야 나중에 유지보수가 쉽거든요. 그리고 위와 같이 하면 이벤트가 발생할 때 eval이라는 자바스크립트 내부 메소드가 실행되는 데(가짜 eval이긴 합니다만) 이 eval은 자바스크립트에서 반드시 피해야하는 것 중 하나입니다.

<button id="clicker">클릭</button>
<script>
  document.getElementById('clicker').onclick = showResult;
</script>

이렇게 코드를 분리합시다. HTML은 HTML의 역할만, 자바스크립트는 자바스크립트의 역할만 충실히 하는 모습입니다. 보기 좋습니다.

다음 시간에는 함수의 메소드에 대해서 알아보겠습니다.

목록
투표로 게시글에 관해 피드백을 해주시면 많은 도움이 됩니다. 오류가 있다면 어떤 부분에 오류가 있는지도 알려주세요! 잘못된 정보가 퍼져나가지 않도록 도와주세요.
Copyright © 2016- 무단 전재 및 재배포 금지

댓글

5개의 댓글이 있습니다.
3달 전
html에 이벤트 리스너를 연결하기보다는 코드를 분리하는게 낫다고 하셔서 분리하려는데요

함수와 인자가 한번에 onclick으로 있는 경우엔 어떡하나요??

<div id="firstoval" class="oval" onclick="dooduji('firstoval')"></div>

처럼요
3달 전
document.getElementById('firstoval').onclick = function() {
dooduji('firstoval');
};
3달 전
감사합니다!! 해결했어용

그런데 알려주신거는 다른 함수에 넣어서 그 함수가 실행될떄 onclick에 잘 들어갔는데.. 함수에 안넣고 그냥 이벤트 추가 구문 쓰면 안되더라구요.

그래서 밑에 나왔는거 대로 다 해봤는데 안되서.. 이건 어떻게 해야 할까요?

시작부터 onclick에 startButton();함수와 starChange(); 함수가 있어야 하는데 그냥 HTML에 넣어야 할까요?

window.onload = function () {
remover.addEventListener('click', startButton);
remover.addEventListener('click', startChange);
};


window.onload = function () {
remover.onclick = function () {
startButton();
};
remover.onclick = function () {
startChange();
};
};


const prepare = () => {
remover.onclick = function () {
startButton();
};
remover.onclick = function () {
startChange();
};
};

prepare();


3달 전
저거 window.onload같은 것은 돼야 합니다. 안 되면 코드에 문제가 있는 거에요. 근데 remover같은 것들도 window.onload 안에서 선언하세요.
3달 전
remover을 전역으로 선언한거인데.. 지금 또 뭐 바꾸다 보니까 안건데 아예 let remover = document.getElementById('start');을 전역으로 선언해서 함수들에서 쓰려니까 안되더라구요.

이게 왜 안되는지를 모르겠네요 이벤트 리스너 추가가 안되는것도 이것 때문인것 같은데..

document는 전역 변수에서 사용하면 안되나요?
3달 전
혹시 스크립트가 HTML 태그보다 위에 있나요?
3달 전
<head>
<meta charset="utf-8" />
<title>두더지 게임</title>
<script src="./dooduji.js"></script>
<link rel="stylesheet" href="./style.css">
</head>

이렇게 js랑 html이랑 나눠서 하고 있습니당
3달 전
아 그리고 let remover; 만 전역으로 선언하고 함수 안에서 따로 remover = document.getElementbyId('start'); 해주면 잘 됩니당.. 왜 이런걸까요...

let remover = document.getElementbyId('start');를 전역으로 선언할때만 안되네요
3달 전
스크립트가 HTML 태그보다 위에 있으면 당연히 에러납니다. 스크립트 로딩할 때 HTML이 없으니까요. 그래서 스크립트는 보통 바디 제일 아래에 두는 거고요.
3달 전
아 스크립트는 바디 제일 아래에 두는군요.. head에 두는줄 알았습니다. 감사합니다!!
4달 전
안녕하세요. 위 내용을 따라서 thired 태그에 click 과 mouseover 이벤트를 걸고 event.stopImmediatePropagation(); 를 태스트 한 결과, mouseover 이벤트가 발생 하더라구요. third 태그에 click event 를 두개 셋팅 한경우에 event.stopImmediatePropagation(); 를 설정해둔 click 이벤트만 동작하더라구요. event.stopImmediatePropagation() 개념을 살짝 다르게 설명 하신거같아용!!
4달 전
수정했습니다 감사합니다
9달 전
정말 잘보고갑니다. 많은 도움이 됐습니다.
일 년 전
중요한 건 아니지만... 오타가 보여서 알려드려요.
꼭 이벤트가 아니더라고 콜백을 활용할 수 있습니다.
-> 꼭 이벤트가 아니더라'도' 콜백을 활용할 수 있습니다.

덕분에 잘 배우고 있습니다. 감사합니다 :-)
일 년 전
감사합니다!
2년 전
잘 배워갑니다~