티스토리 뷰

WEB/JavaScript

[JavaScript] 탭 메뉴

silverline79 2026. 1. 3. 00:19

탭 메뉴 구현을 위한 설계도

탭 메뉴를 직접 짤 때 우리는 크게 세 가지 단계를 거칩니다.

  1. 선택 (Selection): 클릭할 '탭 버튼들'과 보여줄 '컨텐츠들'을 모두 불러옵니다. 🖱️
  2. 반복 (Loop): 여러 개의 버튼 중 어떤 것이 클릭될지 모르니, 모든 버튼에 "클릭하면 실행해!"라는 이벤트를 미리 걸어둡니다. 🔄
  3. 제어 (Control): 버튼이 클릭되면 모든 active 클래스를 일단 다 지우고, **방금 클릭한 그 버튼(과 연결된 컨텐츠)**에만 active 클래스를 붙입니다. ✨

첫 번째 단계: 요소 선택하기

퍼블리셔분들은 HTML 구조를 먼저 보시죠? 보통 아래와 같은 구조일 거예요.

 

<ul class="tab-group">
  <li class="tab-btn active">메뉴 1</li>
  <li class="tab-btn">메뉴 2</li>
</ul>
<div class="content-group">
  <div class="tab-content active">내용 1</div>
  <div class="tab-content">내용 2</div>
</div>

자바스크립트에서 tab-btn이라는 클래스를 가진 요소를 모두 가져와서 btns라는 변수에 담으려고 합니다. 이때 querySelector 대신 여러 개를 한꺼번에 가져오는 메서드를 써야 하는데요.

여기서 첫 번째 질문입니다! 여러 개의 요소를 배열 형태(NodeList)로 한 번에 담을 때 사용하는 자바스크립트 명령어가 무엇인지 혹시 알고 계신가요? (힌트: document.querySelector____)

 

document.querySelector가 하나만 가져온다면, 모두(All) 가져오는 메서드는 이름 뒤에 그 단어가 붙습니다.

바로 document.querySelectorAll('.tab-btn') 입니다. 🔍

이렇게 가져온 결과물은 배열과 비슷한 형태(NodeList)로 담기게 되는데, 퍼블리셔 입장에서 아주 유용한 특징이 있습니다. 바로 forEach라는 반복문을 사용해서 버튼 하나하나에 이벤트를 걸어줄 수 있다는 점이죠.


2단계: 반복문으로 이벤트 걸기 (Loop)

이제 가져온 버튼들(btns)을 하나씩 꺼내서 "클릭" 이벤트를 심어줄 차례입니다.

const btns = document.querySelectorAll('.tab-btn');

btns.forEach((btn, index) => {
  btn.addEventListener('click', () => {
    // 여기에 클릭했을 때 일어날 일을 적습니다.
    console.log(index + "번째 버튼이 클릭됨!");
  });
});

여기서 index라는 매개변수가 아주 중요한 역할을 합니다. 0번 버튼을 누르면 0번 컨텐츠를 보여줘야 하니까요.


직접 설계해보기

탭 메뉴의 핵심은 **"나머지는 다 끄고, 내가 누른 것만 켜기"**입니다. 버튼을 클릭했을 때, 가장 먼저 모든 버튼과 컨텐츠에서 active 클래스를 제거하는 과정이 필요한데요.

 

왜 하나하나 지우지 않고 **"전체를 다 지우는 과정"**을 먼저 거치는 것이 코드를 짤 때 더 유리할까요?

탭이 3개일 때, 5개일 때마다 코드를 일일이 수정하는 것은 비효율적이죠. 그래서 말씀하신 대로 **"일단 모두 지우고(초기화), 선택한 것만 켠다"**는 방식이 가장 깔끔하고 확장성이 좋습니다.

탭 메뉴 완성해보기

이제 우리가 설계한 로직을 코드로 합쳐보겠습니다.

const btns = document.querySelectorAll('.tab-btn');
const contents = document.querySelectorAll('.tab-content');

btns.forEach((btn, index) => {
  btn.addEventListener('click', () => {
    // 1. 모든 버튼과 컨텐츠에서 'active' 클래스를 제거 (초기화)
    btns.forEach(item => item.classList.remove('active'));
    contents.forEach(item => item.classList.remove('active'));

    // 2. 클릭된 버튼과 그에 맞는 순서(index)의 컨텐츠에만 클래스 추가
    btn.classList.add('active');
    contents[index].classList.add('active');
  });
});

 


함수로 정의하고 호출하기

/* 탭 메뉴: 모든 클래스 제거 후 나만 추가 */
function handleTab(btns, contents, index) {
  btns.forEach(btn => btn.classList.remove('active'));
  contents.forEach(con => con.classList.remove('active'));
  
  btns[index].classList.add('active');
  contents[index].classList.add('active');
}

 

탭 메뉴 호출 (인덱스 활용)

탭 메뉴는 "몇 번째 버튼을 눌렀느냐"가 중요합니다. forEach의 index를 함수에 전달하는 것이 핵심이에요.

const tabBtns = document.querySelectorAll('.tab-btn');
const tabContents = document.querySelectorAll('.tab-content');

tabBtns.forEach((btn, index) => {
  btn.addEventListener('click', () => {
    // 미리 만들어둔 함수를 호출하면서 (버튼묶음, 컨텐츠묶음, 누른순서)를 전달!
    handleTab(tabBtns, tabContents, index);
  });
});

 

💡 실무 팁: this를 쓸 때 주의할 점

함수 안에서 this를 쓰고 싶다면, addEventListener의 콜백 함수를 화살표 함수(() => {})가 아닌 **일반 함수(function() {})**로 써야 합니다.

  • 화살표 함수: this가 내가 클릭한 버튼이 아닐 수 있음.
  • 일반 함수: this가 정확히 클릭된 그 요소를 가리킴.

※ 해당 내용은 Google Gmini3.0과 함께 공부한 내용입니다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG more
«   2026/02   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
글 보관함