버튼에 클릭 코드를 붙였는데 아무 반응이 없는 경험은 거의 모두가 합니다. 원인은 문법 오류가 아니라 대부분 등록 시점과 대상의 문제입니다. 이 글에서는 막히는 순서 그대로 이벤트 등록에서 시작해 위임과 해제까지 따라갑니다.
붙이는 방법은 on 하나로 통일합니다
이벤트란 클릭, 입력, 전송처럼 사용자가 일으키는 사건을 말합니다. jQuery에는 click(), change(), keyup() 같은 짧은 메서드도 있지만, 실무에서는 on() 하나로 통일하는 편이 낫습니다. 뒤에 나올 위임까지 같은 문법으로 처리되기 때문입니다.
$(function () {
$('#saveBtn').on('click', function () {
console.log('저장 버튼이 눌렸습니다');
});
$('#categorySelect').on('change', function () {
console.log('분류 선택이 바뀌었습니다');
});
});
전체를 $(function () { ... })로 감싼 이유가 있습니다. 문서를 다 읽기 전에 이 코드가 돌면 #saveBtn은 아직 존재하지 않고, jQuery는 오류 없이 조용히 넘어갑니다. 클릭이 먹지 않는 첫 번째 원인입니다.
this와 $(this)를 혼동하는 순간
같은 클래스의 버튼이 여러 개라면 “눌린 것 하나”를 집어야 합니다. 그 자리를 가리키는 것이 this입니다.
$('.card-btn').on('click', function () {
console.log(this.id); // 순수 DOM 요소의 속성 — 됩니다
console.log($(this).attr('id')); // jQuery 방식 — 역시 됩니다
$(this).toggleClass('active'); // 눌린 버튼 하나만
$(this).closest('.note-card').find('.tag').show();
// console.log(this.attr('id')); // 오류입니다
});
핵심은 this가 jQuery 객체가 아니라 순수 DOM 요소라는 사실입니다. 그래서 attr, css 같은 jQuery 메서드를 쓰려면 $()로 한 번 감싸야 합니다. 반대로 $(this).id는 undefined가 나옵니다.
이벤트 객체로 기본 동작 가로채기
이벤트 함수는 인자를 하나 받을 수 있습니다. 사건에 대한 정보가 담긴 이벤트 객체입니다. 흔히 e라는 이름을 붙입니다.
$('#noteForm').on('submit', function (e) {
e.preventDefault(); // 페이지 새로고침을 막습니다
var title = $('#noteTitle').val().trim();
if (title === '') {
$('#formMsg').text('제목을 입력해 주세요.');
return;
}
$('#noteList').append('<li>' + title + '</li>');
$('#noteTitle').val('').focus();
});
$('#searchInput').on('keyup', function (e) {
if (e.key === 'Enter') {
console.log('엔터로 검색: ' + $(this).val());
}
});
preventDefault()는 브라우저가 원래 하려던 일을 막는 명령입니다. 폼은 제출되면 페이지를 다시 불러오는 것이 기본 동작이라, 이 한 줄이 없으면 화면이 깜빡이며 작성한 내용이 사라집니다.
e.target에는 사건이 실제로 일어난 요소가 담깁니다. 값 검증에 쓰인 val()은 jQuery DOM 조작에서 다룬 메서드입니다.
새로 만든 버튼만 클릭이 안 되는 이유
목록을 코드로 추가했더니 처음부터 있던 항목은 클릭이 되는데, 새로 만든 항목만 반응하지 않는 상황입니다.
// 잘못된 코드
$('.deleteBtn').on('click', function () {
$(this).closest('li').remove();
});
$('#noteList').append('<li>새 노트 <button class="deleteBtn">삭제</button></li>');
// 이 버튼은 눌러도 아무 일이 없습니다
이유는 단순합니다. $('.deleteBtn').on(...)은 그 순간 존재하던 요소들에만 이벤트를 붙입니다. 나중에 만들어진 요소는 명단에 없었으니 등록되지 않습니다. 해결책은 사라지지 않는 부모에게 이벤트를 맡기는 것이고, 이를 이벤트 위임이라고 부릅니다.
// 올바른 코드 — 부모에게 맡기고 조건을 두 번째 인자로
$('#noteList').on('click', '.deleteBtn', function () {
$(this).closest('li').remove();
});
이제 클릭은 항상 #noteList가 받고, 눌린 자리가 .deleteBtn일 때만 함수가 돌아갑니다. 함수 안의 $(this)도 부모가 아니라 실제로 눌린 버튼을 가리키니 안심하고 쓰면 됩니다. 순수 자바스크립트에서는 부모에 addEventListener를 걸고 e.target.closest()로 직접 판별해야 하는 부분이며, 그 방식은 자바스크립트 이벤트 다루기에서 설명합니다.
off로 떼어내기와 중복 등록 막기
이벤트는 붙인 만큼 쌓입니다. 목록을 다시 그릴 때마다 등록 코드를 부르는 구조라면, 클릭 한 번에 같은 함수가 세 번 실행되는 일이 생깁니다.
$('#saveBtn').off('click'); // 클릭 이벤트 전부 해제
$('#saveBtn').off('click.autosave'); // 이름표를 붙여 그것만 해제
$('#saveBtn').on('click.autosave', function () {
console.log('자동 저장');
});
click.autosave처럼 점 뒤에 붙인 이름을 네임스페이스라고 하며, 여러 기능이 같은 요소를 공유할 때 내 것만 골라 뗄 수 있게 해 줍니다. 안전한 방법은 off를 먼저 부르고 on을 거는 것입니다. 이벤트가 걸린 요소를 움직이게 만드는 방법은 jQuery 효과에서 이어집니다.
핵심 정리
- on 통일 — 클릭·변경·입력·제출을
on()으로 통일하면 위임까지 같은 문법입니다. - this — 순수 DOM 요소이므로 jQuery 메서드를 쓰려면
$(this)로 감싸야 합니다. - preventDefault — 폼 제출과 링크 이동 같은 기본 동작을 막을 때 필요합니다.
- 이벤트 위임 — 나중에 만들어질 요소는 사라지지 않는 부모에 조건을 걸어 처리합니다.
- 중복 등록 — 같은 코드를 두 번 실행하면 이벤트도 두 번 걸리므로
off로 먼저 떼어 냅니다.