frontend/javascript
🟨 2-2. 실전 이벤트 패턴 — 모달, 드롭다운, 아코디언, 탭 메뉴 한 번에 완성하기
mirabo01
2025. 11. 7. 08:47
1. 이번 편에서 배울 내용
이 글에서는 이벤트 시스템을 실무 UI에 적용하는 법을 다룬다.
하나의 공통 키워드는 바로 “클릭(click)”과 상태 전환(toggle) 이다.
배울 구성은 다음 네 가지다.
1️⃣ 모달 (Modal) — 화면 중앙에 띄워지는 팝업
2️⃣ 드롭다운 (Dropdown) — 선택 옵션 표시/숨기기
3️⃣ 아코디언 (Accordion) — 클릭 시 내용 열기/닫기
4️⃣ 탭 메뉴 (Tab Menu) — 탭 전환으로 컨텐츠 전환
2. 준비 코드 (공통 HTML & CSS)
우선 모든 예제에 공통으로 쓸 최소한의 스타일부터 추가하자.
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<title>이벤트 패턴 예제</title>
<style>
body {
font-family: sans-serif;
margin: 40px;
}
section {
margin-bottom: 60px;
border: 1px solid #ddd;
padding: 20px;
border-radius: 10px;
}
h2 {
margin-bottom: 16px;
}
/* 공통 버튼 스타일 */
button {
padding: 6px 12px;
border: none;
background: #4a90e2;
color: #fff;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background: #3b78c5;
}
</style>
</head>
<body>
<script type="module" src="main.js"></script>
</body>
</html>
이제 <body> 안에 각 섹션을 하나씩 추가하면서 실습해보자.
3. 모달 (Modal) — 클릭으로 열고 닫기
HTML
<section>
<h2>1️⃣ 모달 (Modal)</h2>
<button id="openModal">모달 열기</button>
<div id="modal" class="modal">
<div class="modal-content">
<p>이건 모달입니다 👋</p>
<button id="closeModal">닫기</button>
</div>
</div>
</section>
CSS
.modal {
display: none;
position: fixed;
top: 0; left: 0;
width: 100%; height: 100%;
background: rgba(0,0,0,0.5);
justify-content: center; align-items: center;
}
.modal.show { display: flex; }
.modal-content {
background: #fff;
padding: 30px;
border-radius: 10px;
text-align: center;
}
JS
const modal = document.getElementById("modal");
const openBtn = document.getElementById("openModal");
const closeBtn = document.getElementById("closeModal");
openBtn.addEventListener("click", () => modal.classList.add("show"));
closeBtn.addEventListener("click", () => modal.classList.remove("show"));
// 모달 바깥 영역 클릭 시 닫기
modal.addEventListener("click", (e) => {
if (e.target === modal) modal.classList.remove("show");
});
✅ 핵심 포인트
- classList.toggle() 대신 add/remove로 명확하게 제어
- 오버레이 영역(modal 자체)을 클릭했을 때 닫히도록 조건 분기
- 실제 서비스에서도 거의 동일하게 쓰인다
4. 드롭다운 (Dropdown) — 클릭으로 옵션 토글
HTML
<section>
<h2>2️⃣ 드롭다운 (Dropdown)</h2>
<div class="dropdown">
<button id="dropdownBtn">메뉴 ▼</button>
<ul id="dropdownMenu" class="dropdown-menu">
<li>HTML</li>
<li>CSS</li>
<li>JavaScript</li>
</ul>
</div>
</section>
CSS
.dropdown {
position: relative;
display: inline-block;
}
.dropdown-menu {
display: none;
position: absolute;
top: 40px;
left: 0;
background: white;
border: 1px solid #ccc;
width: 150px;
border-radius: 6px;
}
.dropdown-menu li {
padding: 10px;
cursor: pointer;
}
.dropdown-menu li:hover {
background: #eee;
}
.dropdown-menu.show {
display: block;
}
JS
const dropdownBtn = document.getElementById("dropdownBtn");
const dropdownMenu = document.getElementById("dropdownMenu");
dropdownBtn.addEventListener("click", (e) => {
dropdownMenu.classList.toggle("show");
});
// 다른 곳 클릭 시 자동 닫기 (이벤트 위임 + 버블링 활용)
document.addEventListener("click", (e) => {
if (!e.target.closest(".dropdown")) {
dropdownMenu.classList.remove("show");
}
});
✅ 핵심 포인트
- closest() 메서드로 부모 요소 판별
- 외부 클릭 시 닫히게 만들어 UX 향상
- 이벤트 위임 + 버블링 제어의 좋은 실전 예시
5. 아코디언 (Accordion) — 클릭 시 내용 열고 닫기
HTML
<section>
<h2>3️⃣ 아코디언 (Accordion)</h2>
<div class="accordion">
<div class="item">
<div class="title">HTML이란?</div>
<div class="content">HTML은 웹의 구조를 정의하는 언어입니다.</div>
</div>
<div class="item">
<div class="title">CSS란?</div>
<div class="content">CSS는 스타일을 담당하는 언어입니다.</div>
</div>
<div class="item">
<div class="title">JavaScript란?</div>
<div class="content">JS는 동작과 상호작용을 구현하는 언어입니다.</div>
</div>
</div>
</section>
CSS
.accordion .item {
border-bottom: 1px solid #ddd;
padding: 10px 0;
}
.accordion .title {
cursor: pointer;
font-weight: bold;
}
.accordion .content {
display: none;
margin-top: 6px;
color: #555;
}
.accordion .content.show {
display: block;
}
JS
const accordion = document.querySelector(".accordion");
accordion.addEventListener("click", (e) => {
if (!e.target.classList.contains("title")) return;
const content = e.target.nextElementSibling;
content.classList.toggle("show");
});
✅ 핵심 포인트
- “이벤트 위임(Event Delegation)”의 전형적인 사용 예
- nextElementSibling 으로 바로 다음 내용 블록 접근
- 각 아이템을 독립적으로 열고 닫을 수 있음
6. 탭 메뉴 (Tab Menu) — 컨텐츠 전환
HTML
<section>
<h2>4️⃣ 탭 메뉴 (Tab Menu)</h2>
<div class="tabs">
<div class="tab-buttons">
<button data-tab="html">HTML</button>
<button data-tab="css">CSS</button>
<button data-tab="js">JavaScript</button>
</div>
<div class="tab-contents">
<div class="tab-content" id="html">HTML은 웹의 구조를 구성합니다.</div>
<div class="tab-content" id="css">CSS는 스타일을 담당합니다.</div>
<div class="tab-content" id="js">JavaScript는 동작을 담당합니다.</div>
</div>
</div>
</section>
CSS
.tab-buttons {
margin-bottom: 10px;
}
.tab-buttons button {
margin-right: 8px;
background: #ddd;
color: #000;
}
.tab-buttons button.active {
background: #4a90e2;
color: #fff;
}
.tab-content {
display: none;
border: 1px solid #ccc;
padding: 10px;
border-radius: 8px;
}
.tab-content.active {
display: block;
}
JS
const tabButtons = document.querySelectorAll(".tab-buttons button");
const tabContents = document.querySelectorAll(".tab-content");
tabButtons.forEach((btn) => {
btn.addEventListener("click", () => {
const target = btn.dataset.tab;
// 모든 탭 비활성화
tabButtons.forEach((b) => b.classList.remove("active"));
tabContents.forEach((c) => c.classList.remove("active"));
// 선택된 탭만 활성화
btn.classList.add("active");
document.getElementById(target).classList.add("active");
});
});
✅ 핵심 포인트
- dataset 속성(data-tab)으로 탭 대상 연결
- 상태 변경 시 class 토글로 컨텐츠 제어
- 실제 서비스의 탭, 설정, 메뉴 구조 전부 같은 원리
7. 이 4가지 패턴의 공통점
패턴 핵심 이벤트 상태 관리 방식 주요 메서드
| 모달 | click | class add/remove | classList |
| 드롭다운 | click + document | toggle + 외부 클릭 감지 | closest() |
| 아코디언 | click (위임) | 개별 열림/닫힘 | nextElementSibling |
| 탭 | click | 단일 활성 상태 | dataset, forEach |
이 4개만 완벽히 이해하면,
React나 Vue로 넘어갔을 때 “UI 상태 전환의 본질”을 완전히 이해할 수 있다.
프레임워크는 결국 이런 로직을 더 깔끔하게 래핑한 도구일 뿐이니까.
8. 실무에서 확장 아이디어
- 모달에 ESC 키 닫기 기능 추가 → keydown 이벤트 활용
- 드롭다운에 키보드 네비게이션 추가
- 아코디언을 하나만 열리게 변경 → “활성 상태 관리 변수” 추가
- 탭에 URL hash(#tab-js) 동기화 → 새로고침 후에도 유지
이런 식으로 발전시켜 나가면
하나의 UI 패턴을 수십 가지 방식으로 확장할 수 있다.
9. 마무리
이제 당신은 단순히 이벤트를 “배웠다”가 아니라,
이벤트로 UI를 설계할 수 있는 수준에 도달했다.
이 4개의 예제만으로도
- 모달 창
- 네비게이션 메뉴
- FAQ 섹션
- 설정 페이지 탭
을 직접 구현할 수 있다.
"이벤트는 단순한 함수 호출이 아니라, 사용자 경험을 설계하는 도구다."