JavaScript 독학 가이드 8 - 이벤트 처리
버튼 클릭하면 뭔가 일어나게 하고 싶죠? 마우스 올리면 색깔 바뀌고, 입력하면 실시간으로 검색 결과 나오고. 이 모든 게 이벤트로 가능합니다. 사용자의 모든 동작에 반응할 수 있어요.
이벤트가 뭔가요?
이벤트는 웹 페이지에서 일어나는 사건이에요. 클릭, 마우스 움직임, 키보드 입력 등 모든 게 이벤트입니다.
// "버튼이 클릭되면, 경고창을 띄워라"
button.addEventListener("click", function() {
alert("버튼이 클릭됐어요!");
});
실행 결과:
버튼 클릭 시:
이 페이지에서 알림
버튼이 클릭됐어요!
이벤트 리스너 등록하기
addEventListener (추천!)
<button id="myBtn">클릭</button>
<script>
let btn = document.querySelector("#myBtn");
btn.addEventListener("click", function() {
console.log("버튼 클릭됨!");
});
</script>
실행 결과:
여러 리스너를 추가할 수 있어요.
btn.addEventListener("click", function() {
console.log("첫 번째 리스너");
});
btn.addEventListener("click", function() {
console.log("두 번째 리스너");
});
// 둘 다 실행됨!
실행 결과:
두 리스너가 모두 순서대로 실행됩니다
on속성 (옛날 방식)
btn.onclick = function() {
console.log("클릭!");
};
하나만 등록 가능하고, 나중에 덮어써집니다.
주요 이벤트 종류
마우스 이벤트
<button id="btn">마우스 테스트</button>
<script>
let btn = document.querySelector("#btn");
btn.addEventListener("click", () => console.log("클릭"));
btn.addEventListener("dblclick", () => console.log("더블클릭"));
btn.addEventListener("mouseenter", () => console.log("마우스 진입"));
btn.addEventListener("mouseleave", () => console.log("마우스 나감"));
btn.addEventListener("mousedown", () => console.log("마우스 버튼 누름"));
btn.addEventListener("mouseup", () => console.log("마우스 버튼 뺌"));
</script>
실행 결과:
키보드 이벤트
<input type="text" id="myInput">
<script>
let input = document.querySelector("#myInput");
input.addEventListener("keydown", () => console.log("키 누름"));
input.addEventListener("keyup", () => console.log("키 뗌"));
input.addEventListener("keypress", () => console.log("키 입력")); // 거의 안 씀
</script>
실행 결과:
폼 이벤트
<form id="myForm">
<input type="text" id="username">
<button type="submit">제출</button>
</form>
<script>
let form = document.querySelector("#myForm");
let username = document.querySelector("#username");
username.addEventListener("focus", () => console.log("포커스"));
username.addEventListener("blur", () => console.log("포커스 해제"));
username.addEventListener("input", () => console.log("입력 중"));
username.addEventListener("change", () => console.log("값 변경됨"));
form.addEventListener("submit", (e) => {
e.preventDefault(); // 폼 제출 막기
console.log("폼 제출!");
});
</script>
실행 결과:
preventDefault()로 페이지 새로고침을 막았습니다
기타 이벤트
// 페이지 로드 완료
window.addEventListener("load", () => {
console.log("페이지 로드됨");
});
// 스크롤
window.addEventListener("scroll", () => {
console.log("스크롤 중");
});
// 리사이즈
window.addEventListener("resize", () => {
console.log("창 크기 변경");
});
이벤트 객체
이벤트가 발생하면 이벤트 정보를 담은 객체가 전달돼요.
btn.addEventListener("click", function(event) {
console.log(event); // 이벤트 정보
console.log(event.type); // "click"
console.log(event.target); // 클릭된 요소
});
실행 결과:
마우스 위치
document.addEventListener("click", function(e) {
console.log(`X: ${e.clientX}, Y: ${e.clientY}`);
});
실행 결과:
clientX, clientY는 브라우저 창 기준 마우스 좌표입니다
눌린 키 확인
document.addEventListener("keydown", function(e) {
console.log(`눌린 키: ${e.key}`);
console.log(`키 코드: ${e.code}`);
});
실행 결과:
이벤트 버블링과 캡처링
버블링
이벤트가 자식에서 부모로 전파돼요.
<div id="outer">
<div id="inner">
<button id="btn">클릭</button>
</div>
</div>
<script>
document.querySelector("#btn").addEventListener("click", () => {
console.log("버튼 클릭");
});
document.querySelector("#inner").addEventListener("click", () => {
console.log("inner 클릭");
});
document.querySelector("#outer").addEventListener("click", () => {
console.log("outer 클릭");
});
// 버튼 클릭 시 출력:
// 버튼 클릭
// inner 클릭
// outer 클릭
</script>
실행 결과:
이벤트가 자식 → 부모 방향으로 버블링됩니다
버블링 막기
btn.addEventListener("click", function(e) {
e.stopPropagation(); // 부모로 전파 안 됨
console.log("버튼만 클릭");
});
실행 결과:
버블링이 중단되어 부모 요소의 이벤트가 실행되지 않습니다
이벤트 위임
부모에 리스너를 하나만 달고, 자식들을 처리하는 기법이에요.
<ul id="list">
<li>항목 1</li>
<li>항목 2</li>
<li>항목 3</li>
</ul>
<script>
// 나쁜 예: 각 li에 리스너 추가
// let items = document.querySelectorAll("li");
// items.forEach(item => {
// item.addEventListener("click", function() {
// console.log(this.textContent);
// });
// });
// 좋은 예: 부모(ul)에 하나만 추가
let list = document.querySelector("#list");
list.addEventListener("click", function(e) {
if (e.target.tagName === "LI") {
console.log(e.target.textContent);
}
});
</script>
실행 결과:
부모 요소 하나에만 리스너를 등록해도 모든 자식 요소의 클릭을 처리할 수 있습니다
동적으로 추가된 요소에도 작동해요!
실전 예제
1. 카운터
<!DOCTYPE html>
<html>
<body>
<h1 id="count">0</h1>
<button id="increaseBtn">+1</button>
<button id="decreaseBtn">-1</button>
<button id="resetBtn">초기화</button>
<script>
let count = 0;
let display = document.querySelector("#count");
document.querySelector("#increaseBtn").addEventListener("click", () => {
count++;
display.textContent = count;
});
document.querySelector("#decreaseBtn").addEventListener("click", () => {
count--;
display.textContent = count;
});
document.querySelector("#resetBtn").addEventListener("click", () => {
count = 0;
display.textContent = count;
});
</script>
</body>
</html>
실행 결과:
5
2. 실시간 검색
<!DOCTYPE html>
<html>
<body>
<input type="text" id="searchInput" placeholder="검색어 입력">
<p id="result"></p>
<script>
let input = document.querySelector("#searchInput");
let result = document.querySelector("#result");
input.addEventListener("input", function() {
let text = input.value;
result.textContent = `검색어: ${text}`;
});
</script>
</body>
</html>
실행 결과:
검색어: 자바스크립트
input 이벤트로 타이핑할 때마다 실시간으로 반응합니다
3. To-Do 리스트
<!DOCTYPE html>
<html>
<body>
<input type="text" id="todoInput">
<button id="addBtn">추가</button>
<ul id="todoList"></ul>
<script>
let input = document.querySelector("#todoInput");
let addBtn = document.querySelector("#addBtn");
let list = document.querySelector("#todoList");
addBtn.addEventListener("click", addTodo);
input.addEventListener("keypress", function(e) {
if (e.key === "Enter") {
addTodo();
}
});
function addTodo() {
let text = input.value.trim();
if (!text) return;
let li = document.createElement("li");
li.textContent = text;
let deleteBtn = document.createElement("button");
deleteBtn.textContent = "삭제";
deleteBtn.addEventListener("click", function() {
li.remove();
});
li.appendChild(deleteBtn);
list.appendChild(li);
input.value = "";
}
</script>
</body>
</html>
실행 결과:
- 장보기
- 운동하기
- 책 읽기
Enter 키를 누르거나 추가 버튼을 클릭하면 새 항목이 추가됩니다
4. 모달 창
<!DOCTYPE html>
<html>
<head>
<style>
.modal {
display: none;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: white;
padding: 20px;
border: 2px solid #333;
}
.modal.active {
display: block;
}
.overlay {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.5);
}
.overlay.active {
display: block;
}
</style>
</head>
<body>
<button id="openBtn">모달 열기</button>
<div class="overlay" id="overlay"></div>
<div class="modal" id="modal">
<h2>모달 창</h2>
<p>내용입니다</p>
<button id="closeBtn">닫기</button>
</div>
<script>
let openBtn = document.querySelector("#openBtn");
let closeBtn = document.querySelector("#closeBtn");
let modal = document.querySelector("#modal");
let overlay = document.querySelector("#overlay");
openBtn.addEventListener("click", () => {
modal.classList.add("active");
overlay.classList.add("active");
});
closeBtn.addEventListener("click", closeModal);
overlay.addEventListener("click", closeModal);
function closeModal() {
modal.classList.remove("active");
overlay.classList.remove("active");
}
</script>
</body>
</html>
실행 결과:
"모달 열기" 버튼 클릭 시 오버레이와 모달이 나타나고, "닫기" 버튼이나 오버레이 클릭 시 사라집니다
자주 하는 실수
1. 이벤트 리스너를 반복문에서 잘못 등록
// 틀림 (var 사용 시)
for (var i = 0; i < 3; i++) {
let btn = document.createElement("button");
btn.textContent = i;
btn.addEventListener("click", function() {
console.log(i); // 항상 3 출력됨
});
document.body.appendChild(btn);
}
// 맞음: let 사용
for (let i = 0; i < 3; i++) {
let btn = document.createElement("button");
btn.textContent = i;
btn.addEventListener("click", function() {
console.log(i); // 0, 1, 2 정상 출력
});
document.body.appendChild(btn);
}
실행 결과:
var는 함수 스코프, let은 블록 스코프이므로 반복문에서는 let을 사용해야 합니다
2. preventDefault 잘못 쓰기
// form 제출 막을 때
form.addEventListener("submit", function(e) {
e.preventDefault(); // 맨 위에 써야 함
// 폼 처리 로직
});
3. this 헷갈림
btn.addEventListener("click", function() {
console.log(this); // btn (이벤트가 발생한 요소)
});
btn.addEventListener("click", () => {
console.log(this); // window (화살표 함수는 this 바인딩 안 됨)
});
실행 결과:
이벤트 핸들러에서 this를 사용하려면 일반 함수를 사용해야 합니다
다음 단계
다음 글에서는 ES6+ 최신 문법을 배웁니다. 구조 분해, 스프레드 연산자, async/await 등 모던 JavaScript 문법을 알아볼 거예요.
이벤트 처리를 마스터하면 진짜 인터랙티브한 웹사이트를 만들 수 있어요!
JavaScript 독학 가이드 시리즈:
← 블로그 목록으로