classList · attributes · dataset — 요소 속성 조작 API

classList로 CSS 클래스를 관리하고, getAttribute/setAttribute로 속성을 다루며, dataset으로 data-* 사용자 데이터를 읽고 쓰는 실무 패턴을 정리합니다.

· 5 min read · PALDYN Team

지난 글에서 DOM 요소를 생성·삽입·제거하는 방법을 살펴봤습니다. 이번에는 요소의 클래스와 속성을 읽고 쓰는 API — classList, getAttribute/setAttribute, dataset — 를 정리합니다.


classList — CSS 클래스 관리

className은 클래스 전체를 하나의 문자열로 관리해 개별 클래스 조작이 불편합니다. classListDOMTokenList 인터페이스로 각 클래스를 독립적으로 다룹니다.

const el = document.querySelector('.btn');

// className 방식 (구형, 불편)
el.className = el.className + ' active'; // 공백 처리 실수 잦음

// classList 방식 (권장)
el.classList.add('active');         // 없으면 추가
el.classList.remove('active');      // 있으면 제거
el.classList.toggle('active');      // 토글
el.classList.contains('active');    // true/false
el.classList.replace('old', 'new'); // 교체 (반환: 성공 여부)

classList · DOMTokenList API

다중 클래스 조작

// add/remove는 여러 클래스 동시 처리 가능
el.classList.add('active', 'highlighted', 'large');
el.classList.remove('disabled', 'hidden');

toggle의 force 인자

// force: true → 무조건 추가 (add와 같음)
// force: false → 무조건 제거 (remove와 같음)
el.classList.toggle('active', isLoggedIn);
el.classList.toggle('error', hasError);

// 상태 기반 클래스 설정 패턴
['loading', 'success', 'error'].forEach(s => el.classList.remove(s));
el.classList.add(currentState);

classList 이터레이션

const el = document.querySelector('.a.b.c');

[...el.classList];       // ['a', 'b', 'c']
el.classList.length;     // 3
el.classList.item(0);    // 'a'
el.classList.value;      // 'a b c'

for (const cls of el.classList) {
  console.log(cls);
}

attributes — 속성 조작

HTML 속성은 모두 문자열로 저장됩니다.

const img = document.querySelector('img');

// 읽기 — 없으면 null
img.getAttribute('src');    // '/logo.png'
img.getAttribute('missing'); // null

// 쓰기 — 항상 문자열로 변환
img.setAttribute('alt', '로고');
img.setAttribute('width', 200); // 숫자도 '200'으로 저장됨

// 존재 확인
img.hasAttribute('loading'); // true/false

// 제거 — 빈 문자열('')과 다름
img.removeAttribute('loading');

// 모든 속성 목록
img.getAttributeNames(); // ['src', 'alt', 'width', ...]

attributes · dataset — 속성 조작

속성 vs 프로퍼티

HTML 속성과 DOM 프로퍼티는 다릅니다.

const input = document.querySelector('input');

// HTML attribute: input.getAttribute('value') — 초기값 (항상 문자열)
// DOM property: input.value — 현재 입력값 (자동 타입 변환)

input.setAttribute('value', 'hello'); // HTML 마크업의 value
input.value;                          // 사용자가 수정한 현재 값

// boolean 속성 차이
const btn = document.querySelector('button');
btn.setAttribute('disabled', ''); // HTML: disabled="" (존재만으로 활성화)
btn.disabled;                     // true (DOM 프로퍼티, boolean)
btn.removeAttribute('disabled');  // 완전 제거
btn.disabled = false;             // 프로퍼티 방식 (더 간편)

ARIA 속성

el.setAttribute('aria-expanded', 'true');
el.setAttribute('aria-label', '닫기');
el.setAttribute('role', 'button');

// 또는 ariaExpanded 프로퍼티 (Chrome 81+)
el.ariaExpanded = 'true';

dataset — data-* 사용자 데이터

HTML의 data-* 속성은 JavaScript와 데이터를 교환하는 표준 방법입니다.

<div id="user-card"
     data-user-id="42"
     data-role="admin"
     data-last-login="2026-05-08">
</div>
const card = document.getElementById('user-card');

// kebab-case → camelCase 자동 변환
card.dataset.userId;    // '42'
card.dataset.role;      // 'admin'
card.dataset.lastLogin; // '2026-05-08'

// 설정 — 자동으로 data-* 속성 추가
card.dataset.theme = 'dark'; // → data-theme="dark"

// 삭제
delete card.dataset.role; // data-role 제거

// 존재 확인
'userId' in card.dataset; // true

dataset 이터레이션

// Object.entries — key는 camelCase
for (const [key, value] of Object.entries(card.dataset)) {
  console.log(key, value);
  // userId 42 / role admin / lastLogin 2026-05-08
}

// 스프레드
const data = { ...card.dataset };
// { userId: '42', role: 'admin', lastLogin: '2026-05-08' }

실무 패턴

CSS 상태 클래스 기반 토글

// 아코디언 UI
const accordions = document.querySelectorAll('.accordion');

accordions.forEach(acc => {
  acc.querySelector('.header').addEventListener('click', () => {
    const isOpen = acc.classList.contains('open');
    // 나머지 모두 닫기
    accordions.forEach(a => a.classList.remove('open'));
    // 클릭한 것만 토글
    acc.classList.toggle('open', !isOpen);
  });
});

dataset으로 이벤트 위임

// HTML: <button data-action="delete" data-id="42">삭제</button>
document.addEventListener('click', (e) => {
  const btn = e.target.closest('button[data-action]');
  if (!btn) return;

  const { action, id } = btn.dataset;
  if (action === 'delete') deleteItem(id);
  if (action === 'edit') editItem(id);
});

className vs classList 성능

// className으로 여러 클래스 설정 — 1번의 DOM 조작
el.className = 'card active highlighted large';

// classList.add 여러 번 — 역시 1번의 DOM 조작 (배치 처리)
el.classList.add('card', 'active', 'highlighted', 'large');

// 두 방법 모두 빠름 — 가독성 기준으로 선택

getComputedStyle과 함께

// CSS 변수 읽기
const style = getComputedStyle(el);
style.getPropertyValue('--primary-color'); // '#7ec8e3'
style.color; // 계산된 색상값

// inline style 직접 설정
el.style.setProperty('--gap', '16px');
el.style.backgroundColor = '#0a0a0a';
el.style.removeProperty('color');

지난 글: DOM 요소 생성·삽입·제거 — createElement부터 replaceWith까지


읽어주셔서 감사합니다. 😊