JavaScript 이벤트 처리와 이벤트 위임 🚀
안녕, 친구들! 오늘은 JavaScript의 꽃이라고 할 수 있는 이벤트 처리와 이벤트 위임에 대해 재미있게 알아볼 거야. 🌸 이 주제는 웹 개발에서 정말 중요한 부분이니까, 집중해서 들어보자고!
💡 잠깐! 이 글은 재능넷(https://www.jaenung.net)의 '지식인의 숲' 메뉴에 등록될 거야. 재능넷은 다양한 재능을 거래하는 플랫폼인데, 우리가 배울 JavaScript 지식도 하나의 멋진 재능이 될 수 있지. 그럼 시작해볼까?
1. 이벤트란 뭘까? 🤔
자, 이벤트라는 게 뭔지 한번 생각해볼까? 우리 일상생활에서 '이벤트'라고 하면 보통 특별한 행사나 사건을 떠올리지? 웹에서의 이벤트도 비슷해. 사용자가 웹 페이지와 상호작용할 때 발생하는 모든 일을 '이벤트'라고 부른다고.
예를 들어볼게:
- 마우스를 클릭했을 때 ✨
- 키보드로 뭔가를 입력했을 때 ⌨️
- 페이지가 로드되었을 때 🌐
- 스크롤을 했을 때 📜
이런 것들이 모두 이벤트야. 재능넷에서 재능을 검색하거나, 글을 올리거나, 메시지를 보낼 때도 모두 이벤트가 발생하는 거지.
🎈 재미있는 비유: 이벤트는 마치 우리가 친구에게 "야, 나 여기 있어!"라고 소리치는 것과 같아. 웹 페이지의 요소들이 JavaScript에게 "hey, 나한테 뭔 일이 생겼어!"라고 알려주는 거지.
이벤트의 종류
이벤트의 종류는 정말 다양해. 몇 가지 대표적인 이벤트를 살펴볼까?
- click: 요소를 클릭했을 때
- mouseover: 마우스가 요소 위로 올라갔을 때
- keydown: 키보드의 키를 눌렀을 때
- submit: 폼을 제출했을 때
- load: 페이지나 이미지 등의 리소스가 로드되었을 때
이 외에도 정말 많은 이벤트가 있어. 마치 재능넷에 다양한 재능이 있는 것처럼 말이야! 😉
2. 이벤트 핸들러: 이벤트의 관리자 👨💼
자, 이제 이벤트가 뭔지 알았으니, 이걸 어떻게 다루는지 알아볼 차례야. 여기서 등장하는 게 바로 '이벤트 핸들러'야.
이벤트 핸들러는 특정 이벤트가 발생했을 때 실행되는 함수야. 쉽게 말해, 이벤트가 일어났을 때 "이렇게 해라~"라고 지시하는 역할을 한다고 보면 돼.
🎭 비유 시간: 이벤트 핸들러는 마치 연극의 배우와 같아. 감독(프로그래머)이 "이 장면에서 이렇게 연기해"라고 지시를 내리면, 배우(이벤트 핸들러)는 그 지시에 따라 연기(코드 실행)를 하는 거지.
이벤트 핸들러 등록하기
이벤트 핸들러를 등록하는 방법은 크게 세 가지가 있어. 하나씩 살펴볼까?
1. HTML 속성으로 등록하기
가장 간단한 방법이지만, 요즘엔 잘 사용하지 않아. HTML과 JavaScript를 분리하는 게 좋은 관행이거든.
<button onclick="alert('안녕하세요!')">클릭해보세요</button>
2. DOM 프로퍼티로 등록하기
이 방법은 HTML과 JavaScript를 분리할 수 있어서 좋아.
let btn = document.querySelector('button');
btn.onclick = function() {
alert('안녕하세요!');
}
3. addEventListener 메서드 사용하기
이 방법이 가장 현대적이고 유연한 방법이야. 여러 개의 이벤트 핸들러를 등록할 수 있지.
let btn = document.querySelector('button');
btn.addEventListener('click', function() {
alert('안녕하세요!');
});
이 방법을 사용하면 재능넷에서 여러 가지 기능을 한 요소에 추가하기 쉬워져. 예를 들어, 버튼을 클릭했을 때 알림도 띄우고 동시에 다른 작업도 할 수 있는 거지.
3. 이벤트 객체: 이벤트의 모든 정보를 담은 보물상자 🎁
이벤트가 발생하면, 브라우저는 이벤트에 대한 모든 정보를 담은 '이벤트 객체'를 만들어. 이 객체는 정말 많은 정보를 갖고 있어서, 우리가 이벤트를 더 세밀하게 다룰 수 있게 해줘.
🕵️♂️ 상상해보기: 이벤트 객체는 마치 탐정이 범죄 현장에서 수집한 모든 증거를 담은 서류 가방 같은 거야. 누가, 어디서, 어떻게 이벤트를 발생시켰는지에 대한 모든 정보가 들어있지!
이벤트 객체를 사용하려면, 이벤트 핸들러 함수의 첫 번째 매개변수로 받아오면 돼. 보통 'e' 또는 'event'라는 이름을 사용해.
btn.addEventListener('click', function(e) {
console.log('클릭된 요소:', e.target);
console.log('클릭된 좌표:', e.clientX, e.clientY);
});
이벤트 객체의 주요 프로퍼티들을 살펴볼까?
- e.type: 이벤트의 타입 (예: 'click', 'mouseover')
- e.target: 이벤트가 발생한 요소
- e.currentTarget: 이벤트 핸들러가 붙은 요소
- e.clientX, e.clientY: 마우스 이벤트에서 마우스 커서의 좌표
- e.key: 키보드 이벤트에서 눌린 키
이런 정보들을 활용하면 정말 다양한 기능을 만들 수 있어. 예를 들어, 재능넷에서 사용자가 어떤 재능 카드를 클릭했는지, 어떤 키워드를 검색했는지 등을 정확히 알 수 있겠지?
4. 이벤트 버블링과 캡처링: 이벤트의 여행 🧳
자, 이제 좀 더 심화된 내용으로 들어가볼게. 이벤트 버블링과 캡처링이라는 개념이 있어. 이게 뭔지 알면 이벤트의 동작 방식을 더 깊이 이해할 수 있을 거야.
이벤트 버블링 (Event Bubbling) 🛁
이벤트 버블링은 특정 요소에서 이벤트가 발생했을 때, 그 이벤트가 상위 요소로 전파되는 현상을 말해. 마치 물속의 거품이 위로 올라가는 것처럼 말이야.
🎈 재미있는 비유: 이벤트 버블링은 마치 가족 모임에서 막내가 "엄마!"라고 소리치면, 엄마뿐만 아니라 할머니도 "왜?"라고 대답하는 것과 비슷해. 소리(이벤트)가 위로 위로 전달되는 거지.
예를 들어볼게:
<div id="grandparent">
<div id="parent">
<button id="child">클릭해보세요</button>
</div>
</div>
<script>
document.getElementById('grandparent').addEventListener('click', function() {
console.log('할아버지 요소 클릭됨');
});
document.getElementById('parent').addEventListener('click', function() {
console.log('부모 요소 클릭됨');
});
document.getElementById('child').addEventListener('click', function() {
console.log('자식 요소 클릭됨');
});
</script>
이 코드에서 버튼을 클릭하면 콘솔에는 다음과 같이 출력돼:
자식 요소 클릭됨
부모 요소 클릭됨
할아버지 요소 클릭됨
버튼을 클릭했을 뿐인데, 그 위의 모든 요소의 클릭 이벤트가 발생한 거야. 이게 바로 버블링이야!
이벤트 캡처링 (Event Capturing) 🕸️
이벤트 캡처링은 버블링의 반대야. 이벤트가 최상위 요소에서 시작해서 이벤트가 발생한 요소까지 내려가는 현상을 말해.
🕷️ 상상해보기: 이벤트 캡처링은 마치 거미가 거미줄을 타고 내려가는 것과 같아. 맨 위에서 시작해서 목표물(이벤트가 발생한 요소)까지 내려가는 거지.
캡처링을 사용하려면 addEventListener의 세 번째 인자로 {capture: true}를 전달하면 돼:
document.getElementById('grandparent').addEventListener('click', function() {
console.log('할아버지 요소 캡처 단계');
}, {capture: true});
document.getElementById('parent').addEventListener('click', function() {
console.log('부모 요소 캡처 단계');
}, {capture: true});
document.getElementById('child').addEventListener('click', function() {
console.log('자식 요소 캡처 단계');
}, {capture: true});
이렇게 하면 버튼 클릭 시 콘솔 출력은 이렇게 돼:
할아버지 요소 캡처 단계
부모 요소 캡처 단계
자식 요소 캡처 단계
재능넷 같은 복잡한 웹 애플리케이션에서는 이런 이벤트의 전파 방식을 이해하고 활용하는 게 중요해. 예를 들어, 모달 창 바깥을 클릭했을 때 창을 닫는 기능을 구현할 때 이벤트 버블링을 활용할 수 있지.
5. 이벤트 위임: 효율적인 이벤트 관리의 비결 🎩
자, 이제 우리의 주인공인 '이벤트 위임'에 대해 알아볼 차례야. 이벤트 위임은 앞서 배운 이벤트 버블링을 활용한 테크닉이야.
이벤트 위임은 여러 요소에 각각 이벤트 리스너를 추가하는 대신, 그 요소들의 공통 조상에 이벤트 리스너를 추가하는 방법이야. 이렇게 하면 코드도 간결해지고, 메모리 사용량도 줄일 수 있어.
🎭 비유 시간: 이벤트 위임은 마치 학급 반장이 모든 학생의 의견을 직접 듣는 대신, 학급 회의를 열어 한 번에 의견을 수렴하는 것과 같아. 효율적이지?
예를 들어, 재능넷에서 재능 목록을 표시하는 페이지가 있다고 생각해보자. 각 재능 카드마다 클릭 이벤트를 추가하는 대신, 목록 전체에 이벤트 리스너를 추가할 수 있어.
<ul id="talent-list">
<li><a href="/talent/1">웹 개발</a></li>
<li><a href="/talent/2">그래픽 디자인</a></li>
<li><a href="/talent/3">번역</a></li>
<li><a href="/talent/4">음악 제작</a></li>
</ul>
<script>
document.getElementById('talent-list').addEventListener('click', function(e) {
if (e.target.tagName === 'A') {
e.preventDefault(); // 기본 동작 막기
console.log('선택된 재능:', e.target.textContent);
// 여기에 원하는 동작 추가
}
});
</script>
이 코드에서는 ul 요소에만 이벤트 리스너를 추가했어. 그리고 클릭된 요소(e.target)가 A 태그인 경우에만 동작하도록 했지. 이렇게 하면 목록에 새로운 항목이 추가되어도 별도의 이벤트 리스너를 추가할 필요가 없어.
이벤트 위임의 장점
- 메모리 사용량 감소: 각 요소마다 리스너를 추가하지 않아도 돼.
- 동적 요소 처리 용이: 나중에 추가되는 요소들도 자동으로 이벤트 처리가 가능해.
- 코드 간결화: 반복적인 이벤트 바인딩 코드를 줄일 수 있어.
이벤트 위임 사용 시 주의할 점
- 모든 이벤트가 버블링되는 것은 아니야. (예: focus)
- 이벤트 위임을 사용할 때는 항상 타겟 요소를 확인해야 해.
- 너무 상위 요소에 위임하면 불필요한 이벤트 체크가 많아질 수 있어.
재능넷 같은 대규모 웹 애플리케이션에서는 이벤트 위임을 적절히 활용하면 성능을 크게 개선할 수 있어. 예를 들어, 재능 검색 결과 페이지에서 수많은 재능 카드에 각각 이벤트 리스너를 추가하는 대신, 결과 컨테이너에 하나의 리스너만 추가하면 되니까.
6. 이벤트의 기본 동작 제어하기 🎮
때로는 이벤트의 기본 동작을 막아야 할 때가 있어. 예를 들어, 폼 제출 시 유효성 검사를 먼저 하고 싶다거나, 링크 클릭 시 페이지 이동을 막고 싶을 때 말이야.
preventDefault() 메서드
preventDefault() 메서드는 이벤트의 기본 동작을 취소해. 아주 유용한 녀석이지!
<form id="signup-form">
<input type="text" id="username" required>
<input type="password" id="password" required>
<button type="submit">가입하기</button>
</form>
<script>
document.getElementById('signup-form').addEventListener('submit', function(e) {
let username = document.getElementById('username').value;
let password = document.getElementById('password').value;
if (username.length < 5 || password.length < 8) {
e.preventDefault(); // 폼 제출 막기
alert('사용자 이름은 5자 이상, 비밀번호는 8자 이상이어야 합니다.');
}
});
</script>
이 예제에서는 폼 제출 시 사용자 이름과 비밀번호의 길이를 체크해. 조건에 맞지 않으면 preventDefault()를 호출해서 폼 제출을 막고 경고 메시지를 띄우지.
stopPropagation() 메서드
이벤트 버블링을 막고 싶을 때는 stopPropagation() 메서드를 사용해. 이 메서드는 현재 이벤트가 상위로 전파되는 것을 막아줘.
<div id="outer">
<div id="inner">
<button id="btn">클릭!</button>
</div>
</div>
<script>
document.getElementById('btn').addEventListener('click', function(e) {
alert('버튼 클릭됨!');
e.stopPropagation();
});
document.getElementById('inner').addEventListener('click', function() {
alert('inner div 클릭됨!');
});
document.getElementById('outer').addEventListener('click', function() {
alert('outer div 클릭됨!');
});
</script>
이 예제에서 버튼을 클릭하면 "버튼 클릭됨!" 알림만 뜨고, inner와 outer div의 클릭 이벤트는 발생하지 않아. stopPropagation() 때문이지!
⚠️ 주의: stopPropagation()을 과도하게 사용하면 예상치 못한 문제가 발생할 수 있어. 꼭 필요한 경우에만 사용하는 게 좋아.
7. 커스텀 이벤트: 나만의 이벤트 만들기 🎨
JavaScript에서는 기본 제공되는 이벤트 외에도 우리가 직접 커스텀 이벤트를 만들 수 있어. 이걸 활용하면 정말 다양한 기능을 구현할 수 있지.
커스텀 이벤트 생성하기
커스텀 이벤트는 CustomEvent 생성자를 사용해서 만들 수 있어.
let myEvent = new CustomEvent('awesome', {
detail: { message: '이것은 멋진 이벤트입니다!' }
});
// 이벤트 발생시키기
element.dispatchEvent(myEvent);
이렇게 만든 커스텀 이벤트는 addEventListener로 리스닝할 수 있어:
element.addEventListener('awesome', function(e) {
console.log(e.detail.message);
});
실제 사용 예시
재능넷에서 새로운 재능이 등록됐을 때 알림을 주는 기능을 만들어볼까?
<div id="talent-notification"></div>
<script>
// 커스텀 이벤트 정의
let newTalentEvent = new CustomEvent('newTalent', {
detail: { talentName: '웹 개발', talentOwner: '김코딩' }
});
// 이벤트 리스너 등록
document.getElementById('talent-notification').addEventListener('newTalent', function(e) {
this.innerHTML = `새로운 재능이 등록되었습니다! ${e.detail.talentOwner}님의 ${e.detail.talentName}`;
});
// 이벤트 발생시키기 (실제로는 서버에서 새 재능이 등록됐을 때 호출될 거야)
setTimeout(function() {
document.getElementById('talent-notification').dispatchEvent(newTalentEvent);
}, 3000);
</script>
이 예제에서는 3초 후에 새 재능이 등록된 것처럼 이벤트를 발생시켜. 실제 애플리케이션에서는 서버에서 새 재능이 등록됐다는 알림을 받았을 때 이 이벤트를 발생시키면 돼.
💡 팁: 커스텀 이벤트를 사용하면 코드의 모듈성을 높일 수 있어. 예를 들어, 재능 등록, 사용자 로그인, 메시지 수신 등 다양한 상황에 대해 커스텀 이벤트를 만들어 사용할 수 있지.
8. 이벤트와 성능 최적화 🚀
이벤트를 많이 사용하다 면 성능 문제가 발생할 수 있어. 특히 재능넷처럼 복잡한 웹 애플리케이션에서는 이벤트 처리를 최적화하는 게 중요해. 몇 가지 팁을 알아볼까?
1. 디바운싱(Debouncing)과 쓰로틀링(Throttling) 🕰️
디바운싱과 쓰로틀링은 이벤트 핸들러가 너무 자주 호출되는 것을 방지하는 기술이야.
디바운싱은 연이어 호출되는 함수들 중 마지막 함수(또는 제일 처음)만 호출하도록 하는 것이고, 쓰로틀링은 일정 시간 간격으로 함수가 호출되도록 하는 거야.