addEventListener로 이벤트 등록, 이벤트 객체의 target·key·preventDefault, 클릭·마우스·키보드 이벤트 종류, removeEventListener와 메모리 관리까지 사용자 상호작용의 모든 것을 배웁니다.
🎯 학습 목표
이 강을 마치면 다음을 할 수 있습니다.
addEventListener로 이벤트를 등록하고removeEventListener로 해제할 수 있습니다.- 이벤트 객체(
event)의target,currentTarget,type을 활용할 수 있습니다. preventDefault()와stopPropagation()의 역할을 이해하고 사용할 수 있습니다.- 클릭, 마우스, 키보드, 포커스 이벤트를 처리할 수 있습니다.
🎧 addEventListener — 이벤트 등록
이 섹션에서는 DOM 요소에 이벤트 리스너를 등록하는 방법과 기본 원리를 살펴보겠습니다.
웹 페이지에서 사용자의 행동(클릭, 키 입력, 마우스 이동 등)은 모두 이벤트(Event)로 표현됩니다. 자바스크립트는 이 이벤트가 발생했을 때 실행할 함수를 등록해 두는 방식으로 사용자와 상호작용합니다. 이를 이벤트 리스너(Event Listener) 패턴이라고 합니다.
const btn = document.querySelector("#myButton");
// 기본 형태: addEventListener(이벤트명, 콜백 함수)
btn.addEventListener("click", function(event) {
console.log("버튼 클릭!");
});
// 화살표 함수로 (더 간결)
btn.addEventListener("click", (event) => {
console.log("버튼 클릭!");
});
// 함수를 분리해 등록 (재사용 및 나중에 해제 가능)
function handleClick(event) {
console.log("클릭 이벤트!", event.type); // "click"
}
btn.addEventListener("click", handleClick);
// 이벤트 해제 — 동일한 함수 참조 필요!
btn.removeEventListener("click", handleClick);
// 주의: 아래처럼 익명 함수면 해제 불가
// btn.removeEventListener("click", () => {}); // ❌ 새 함수라 매칭 안 됨
// 한 번만 실행하는 이벤트
btn.addEventListener("click", handleClick, { once: true });
// 클릭 후 자동으로 리스너 제거됨
addEventListener는 구형 방식인 onclick = function에 비해 여러 장점이 있습니다. 첫째, 같은 이벤트에 여러 리스너를 등록할 수 있습니다. 둘째, 세 번째 인수로 옵션을 설정할 수 있습니다(once, passive, capture). 셋째, removeEventListener로 명시적으로 해제할 수 있습니다. 이벤트 리스너는 등록할 때 사용한 함수와 동일한 참조여야 해제할 수 있으므로, 나중에 해제할 리스너는 변수에 담아두어야 합니다.
📦 이벤트 객체 — event
이 섹션에서는 이벤트 발생 시 콜백 함수에 전달되는 이벤트 객체의 주요 속성을 살펴보겠습니다.
이벤트 리스너의 콜백 함수는 이벤트 객체를 자동으로 첫 번째 인수로 받습니다. 이 객체에는 어디서, 무슨 이벤트가 발생했는지, 어떤 값으로 발생했는지 등 모든 정보가 담겨 있습니다.
document.addEventListener("click", (event) => {
// 이벤트 기본 정보
console.log(event.type); // "click"
console.log(event.timeStamp); // 이벤트 발생 시각 (ms)
// 어떤 요소에서 발생했나
console.log(event.target); // 실제 클릭된 요소
console.log(event.currentTarget); // addEventListener가 등록된 요소
// target: 실제로 클릭된 요소 (자식일 수 있음)
// currentTarget: 이벤트 리스너가 붙은 요소
// 마우스 위치
console.log(event.clientX, event.clientY); // 뷰포트 기준 좌표
console.log(event.pageX, event.pageY); // 페이지 기준 좌표
});
// 키보드 이벤트 객체
document.addEventListener("keydown", (event) => {
console.log(event.key); // "a", "Enter", "ArrowLeft" 등
console.log(event.code); // "KeyA", "Enter", "ArrowLeft" (물리 키)
console.log(event.ctrlKey); // Ctrl 키 눌렸나? (boolean)
console.log(event.shiftKey); // Shift 키?
console.log(event.altKey); // Alt 키?
// Ctrl+S 단축키 감지
if (event.ctrlKey && event.key === "s") {
console.log("저장!");
event.preventDefault(); // 브라우저 기본 동작(파일 저장) 막기
}
});
event.target과 event.currentTarget의 차이는 중요합니다. target은 실제로 이벤트가 발생한 요소이고, currentTarget은 리스너가 등록된 요소입니다. 부모 요소에 리스너를 등록했을 때 자식 요소를 클릭하면 target은 자식, currentTarget은 부모가 됩니다. 이 개념은 21강의 이벤트 버블링과 이벤트 위임의 핵심입니다.
🚫 preventDefault와 stopPropagation
이 섹션에서는 브라우저의 기본 동작을 막고 이벤트 전파를 제어하는 방법을 살펴보겠습니다.
HTML 요소 중에는 자체 기본 동작이 있는 것들이 있습니다. <a> 태그를 클릭하면 페이지를 이동하고, 폼을 제출하면 페이지가 새로고침됩니다. 자바스크립트로 이 동작을 막아 원하는 처리를 할 수 있습니다.
// 링크 기본 동작 막기
const link = document.querySelector("a");
link.addEventListener("click", (event) => {
event.preventDefault(); // 페이지 이동 막기
console.log("링크 클릭! 이동 안 함");
// 여기서 자바스크립트로 원하는 동작 수행
});
// 폼 제출 막기 — AJAX 처리를 위해 자주 사용
const form = document.querySelector("form");
form.addEventListener("submit", (event) => {
event.preventDefault(); // 페이지 새로고침 막기
// 대신 자바스크립트로 처리
const formData = new FormData(form);
console.log(formData.get("username"));
// fetch로 비동기 전송 (26강에서 학습)
});
// 우클릭 컨텍스트 메뉴 막기
document.addEventListener("contextmenu", (event) => {
event.preventDefault();
// 커스텀 컨텍스트 메뉴 표시
});
// stopPropagation — 이벤트 버블링 중단
const outer = document.querySelector(".outer");
const inner = document.querySelector(".inner");
outer.addEventListener("click", () => {
console.log("outer 클릭");
});
inner.addEventListener("click", (event) => {
event.stopPropagation(); // outer까지 이벤트 전파 막기
console.log("inner 클릭"); // 이것만 출력됨
});
preventDefault()는 브라우저의 기본 동작을 막습니다. stopPropagation()은 이벤트가 부모 요소로 전파되는 것을 막습니다. 이 둘은 독립적입니다. 폼 제출에서 preventDefault()를 사용하면 페이지 새로고침 없이 자바스크립트로 데이터를 처리할 수 있는데, 이는 현대 웹 앱의 핵심 패턴입니다. stopPropagation()에 대해서는 21강에서 이벤트 버블링과 위임을 배울 때 자세히 다룹니다.
🖱️ 주요 이벤트 종류
이 섹션에서는 실무에서 자주 사용하는 이벤트 종류와 각각의 특성을 살펴보겠습니다.
자바스크립트 이벤트는 수십 가지가 있지만, 실무에서 자주 쓰는 것은 한정되어 있습니다. 카테고리별로 핵심 이벤트를 정리해 보겠습니다.
const el = document.querySelector(".box");
const input = document.querySelector("input");
// 마우스 이벤트
el.addEventListener("click", (e) => console.log("클릭"));
el.addEventListener("dblclick", (e) => console.log("더블클릭"));
el.addEventListener("mousedown", (e) => console.log("마우스 버튼 눌림"));
el.addEventListener("mouseup", (e) => console.log("마우스 버튼 뗌"));
el.addEventListener("mouseenter", (e) => console.log("마우스 진입 (버블링 없음)"));
el.addEventListener("mouseleave", (e) => console.log("마우스 이탈 (버블링 없음)"));
el.addEventListener("mouseover", (e) => console.log("마우스 오버 (버블링 있음)"));
el.addEventListener("mousemove", (e) => console.log(`좌표: ${e.clientX}, ${e.clientY}`));
// 키보드 이벤트
document.addEventListener("keydown", (e) => console.log(`키 눌림: ${e.key}`));
document.addEventListener("keyup", (e) => console.log(`키 뗌: ${e.key}`));
// keypress는 deprecated — keydown 사용 권장
// 폼/입력 이벤트
input.addEventListener("input", (e) => console.log(`입력값: ${e.target.value}`));
input.addEventListener("change", (e) => console.log(`확정값: ${e.target.value}`));
input.addEventListener("focus", (e) => console.log("포커스 받음"));
input.addEventListener("blur", (e) => console.log("포커스 잃음"));
// 문서/창 이벤트
window.addEventListener("load", () => console.log("모든 리소스 로드 완료"));
window.addEventListener("resize", () => console.log(`창 크기: ${window.innerWidth}`));
window.addEventListener("scroll", () => console.log(`스크롤: ${window.scrollY}`));
document.addEventListener("DOMContentLoaded", () => console.log("HTML 파싱 완료"));
mouseenter/mouseleave와 mouseover/mouseout의 차이를 알아두세요. mouseenter는 요소 자체에 마우스가 들어올 때만 발생하고 버블링이 없습니다. mouseover는 하위 요소에서도 버블링이 일어나 자식에 마우스가 들어와도 발생합니다. 대부분의 경우 mouseenter/mouseleave가 의도한 동작에 맞습니다. input 이벤트는 사용자가 타이핑할 때마다 발생하고, change는 포커스를 잃을 때(또는 체크박스/셀렉트 변경 시) 발생합니다. 실시간 검색 자동완성은 input을 사용합니다.
⚠️ 자주 하는 실수
이 섹션에서는 이벤트 처리에서 초보자가 가장 자주 겪는 실수를 살펴보겠습니다.
// 실수 1: 클로저 문제 — 루프에서 이벤트 등록
const buttons = document.querySelectorAll(".btn");
// ❌ 잘못된 방법 — var는 함수 스코프라 클로저 문제 발생
for (var i = 0; i < buttons.length; i++) {
buttons[i].addEventListener("click", () => {
console.log(i); // 항상 buttons.length 출력 (3 등)
});
}
// ✅ let으로 해결 — 블록 스코프
for (let i = 0; i < buttons.length; i++) {
buttons[i].addEventListener("click", () => {
console.log(i); // 정확한 인덱스 출력
});
}
// ✅ forEach로 해결
buttons.forEach((btn, index) => {
btn.addEventListener("click", () => {
console.log(index); // 정확한 인덱스
});
});
// 실수 2: 이벤트 리스너 중복 등록
function setup() {
// setup()이 여러 번 호출되면 리스너도 중복 등록!
document.querySelector("#btn").addEventListener("click", handleClick);
}
// 해결: 리스너 해제 후 재등록, 또는 once 옵션 사용
// 실수 3: this 컨텍스트 혼동
const obj = {
name: "홍길동",
// 화살표 함수는 this가 obj가 아님!
handleClick: (event) => {
console.log(this.name); // undefined
},
// 일반 함수는 this가 이벤트 타겟
handleClick2(event) {
console.log(this === event.currentTarget); // true
// obj.name에 접근하려면 화살표 함수나 bind 필요
}
};
📝 핵심 요약
18강에서 배운 내용을 정리해 보겠습니다.
| 개념 | 사용법 | 핵심 특징 |
|---|---|---|
| 이벤트 등록 | el.addEventListener(type, fn) | 같은 이벤트에 여러 리스너 가능 |
| 이벤트 해제 | el.removeEventListener(type, fn) | 동일 함수 참조 필수 |
| 한 번만 실행 | { once: true } 옵션 | 실행 후 자동 해제 |
| event.target | 실제 이벤트 발생 요소 | 자식 요소 클릭 시 자식 |
| event.currentTarget | 리스너가 등록된 요소 | 항상 addEventListener 대상 |
| preventDefault() | 브라우저 기본 동작 막기 | 링크 이동, 폼 새로고침 등 |
| stopPropagation() | 이벤트 버블링 중단 | 부모로 전파 차단 |
다음 강인 19강 — 폼과 입력값에서는 이벤트 처리를 폼과 연결합니다. 텍스트 입력, 체크박스, 라디오 버튼, 셀렉트 박스 등 다양한 폼 요소에서 값을 읽는 방법, 유효성 검사, 폼 데이터 처리까지 배웁니다. 18강의 이벤트 처리 + 19강의 폼 처리가 합쳐지면 실용적인 입력 기능을 만들 수 있게 됩니다!
관련 주제
- addEventListener
- 이벤트 객체 target
- preventDefault
- stopPropagation
- 클릭 키보드 이벤트
- 개발·프로그래밍
- 개발·프로그래밍 강의
- 자바스크립트 기초 30강 — 웹을 움직이는 언어
- 무료강의
- 무료 온라인 강의
- NUGUNA
- 누구나
📚 시리즈 전체 공유
자바스크립트 기초 30강 — 웹을 움직이는 언어
이 강의가 속한 시리즈는 총 22강, 모두 무료입니다. 처음부터 배우려는 동료에게 시리즈 전체를 알려 주세요.
댓글
불러오는 중...
