data-* 속성은 **HTML 표준에서 정식으로 제공하는 “사용자 정의 데이터 저장 공간”**이에요.
한 줄로 요약하면 👉 HTML 요소에 JS용 데이터를 몰래(?) 붙여두는 방법입니다.
✅ data-* 속성이란?
- data-로 시작하는 모든 속성은 유효한 HTML
- 브라우저는 의미 해석 안 함
- JavaScript가 읽어서 사용하는 용도
👉 HTML ↔ JavaScript 사이의 데이터 전달 통로
🧠 왜 필요할까?
❌ 나쁜 옛날 방식
<div
class="admin active"
id="user123">
</div>
- 의미 없는 id/class 남발
- 스타일용과 로직용이 섞임
✅ data-* 사용
<div
class="user" // 스타일 데이터
data-user-id="123" // 로직용 데이터
data-role="admin">
</div>
- 스타일은 class
- 로직 데이터는 data-*
- 역할 분리 👍
📌 JavaScript에서 어떻게 쓰나?
1️⃣ dataset으로 접근 (가장 많이 씀)
<button
data-user-id="10"
data-is-admin="true">
버튼
</button>
const btn = document.querySelector("button");
btn.dataset.userId // "10"
btn.dataset.isAdmin // "true"
📌 규칙
- data-user-id → dataset.userId
- data-is-admin → dataset.isAdmin
- 항상 문자열로 들어옴
Q. data-user-id -> dataset.uesrId 로 접근
이건 HTML 규칙 + JavaScript 문법 + 브라우저 자동 변환 규칙이 겹쳐서 생기는 차이입니다.
❓ 왜 btn.dataset.userId만 되고, btn.dataset.user-id 는 안 될까?
🔴 btn.dataset.user-id ❌
JS 문법상 이렇게 해석됩니다:
btn.dataset.user - id
👉 - 는 하이픈이 아니라 뺄셈 연산자
그래서 문법 에러 또는 엉뚱한 계산이 됨
🔴 btn.dataset."user-id" ❌
btn.dataset."user-id"
👉 JS에서는 속성 이름에 문자열 직접 접근 불가
문자열로 접근하려면 대괄호 표기법을 써야 함
btn.dataset["user-id"] // ❌ 이것도 안 됨
왜냐하면 dataset 안에는 "user-id"라는 키가 존재하지 않기 때문
🔄 그럼 왜 userId로 접근되는데 같은 거야?
🔹 HTML
<button data-user-id="10">
HTML 속성 이름 규칙:
- 케밥 케이스 (kebab-case) 사용
- data-user-id ← 이게 표준
🔹 JavaScript (dataset)
브라우저가 자동 변환함:
data-user-id → dataset.userId
📌 변환 규칙 (중요)
- data- 제거
- - 뒤 문자를 대문자로 변환
- 하이픈(-) 제거
user-id → userId
user-name → userName
long-user-id-value → longUserIdValue
👉 그래서 camelCase가 됨
🧠 그럼 user-id 같은 표기법 이름은 뭐라고 불러?
정확한 용어는 👇
✅ kebab-case (케밥 케이스)
user-id
data-user-name
long-user-id-value
- HTML 속성 이름 표준
- CSS 클래스 이름
- URL 경로
🔄 다른 케이스들 정리
| 표기법 | 예시 |
| camelCase | userId |
| PascalCase | UserId |
| kebab-case | user-id |
| snake_case | user_id |
| SCREAMING_SNAKE | USER_ID |
❗ 그럼 접근할 수 있는 방법은 오직 하나?
✅ 표준 & 권장
btn.dataset.userId
⚠️ 예외적으로 가능한 방법
btn.getAttribute("data-user-id") // "10"
하지만
- 자동 변환 ❌
- 읽기 전용 느낌
- dataset 조작과 섞어 쓰기 불편
🧩 왜 이런 귀찮은 규칙을 만들었을까?
이유 3가지
- HTML은 kebab-case가 읽기 쉬움
- JS 객체는 camelCase가 표준
- 하이픈은 JS 식별자에 ❌
👉 그래서 브라우저가 중간에서 번역기 역할을 함
한 줄 요약
- data-user-id → kebab-case
- dataset.userId → camelCase
- 브라우저가 자동 매핑
- dataset.user-id는 JS 문법상 불가능
이거 이해했으면
👉 data-user-id="true"가 왜 boolean이 아닌지
👉 dataset과 aria-* 차이
👉 React에서 data-*가 그대로 안 쓰이는 이유
이런 것도 바로 이어서 이해될 거예요 😄
2️⃣ getAttribute 방식
btn.getAttribute("data-user-id"); // "10"
Q. getAttribute("")
getAttribute()는 DOM 요소에 붙어 있는 “HTML 속성(attribute)” 값을 그대로 꺼내오는 함수예요.
🔹 getAttribute() 한 줄 정의
HTML에 실제로 적혀 있는 속성 값을 문자열로 가져오는 함수
📌 기본 형태
element.getAttribute("속성이름")
예시
<button data-user-id="10" disabled></button>
const btn = document.querySelector("button");
btn.getAttribute("data-user-id"); // "10"
btn.getAttribute("disabled"); // "" (있으면 빈 문자열)
btn.getAttribute("class"); // null (없으면 null)
🧠 attribute vs property (핵심 차이)
이게 제일 중요해요.
1️⃣ attribute (HTML에 적힌 것)
<input type="checkbox" checked>
checkbox.getAttribute("type"); // "checkbox"
checkbox.getAttribute("checked"); // "" (존재 여부만)
2️⃣ property (JS 객체 상태)
checkbox.checked; // true
📌 getAttribute는 항상 attribute만 봄
📌 .checked, .value, .id 이런 건 property
🔄 dataset이랑 비교하면?
<div data-count="5"></div>
getAttribute
div.getAttribute("data-count"); // "5"
dataset
div.dataset.count; // "5"
차이 요약
| 접근 대상 | HTML 속성 | JS 변환 객체 |
| 케이스 | 그대로 | camelCase |
| 타입 | 항상 문자열 | 항상 문자열 |
| 존재 안 하면 | null | undefined |
| 값 변경 | setAttribute | dataset.xxx = |
🔧 setAttribute도 같이 알아두자
btn.setAttribute("data-user-id", "20");
btn.removeAttribute("disabled");
📌 존재만 중요하면 값은 보통 ""로 둠
⚠️ 언제 getAttribute를 쓰는 게 좋을까?
✅ 써야 하는 경우
- 원본 HTML 값이 필요한 경우
- 속성 이름이 JS 식별자로 부적합할 때
el.getAttribute("aria-label")
- 속성 존재 여부만 확인할 때
el.hasAttribute("disabled")
❌ 피하는 게 좋은 경우
input.getAttribute("value") // ❌
input.value // ⭕
이유: value는 사용자 입력에 따라 property가 변함
🧩 왜 이렇게 헷갈리게 만들어졌냐면…
- HTML: 문서 포맷
- DOM: JS 객체
- 둘이 1:1이 아님
👉 그래서
**“보여지는 상태” vs “HTML 원본”**이 분리됨
한 줄 요약
- getAttribute() = HTML에 적힌 속성 그대로 읽기
- DOM property랑 다름
- dataset은 getAttribute의 JS 친화 버전
이해됐으면 다음 단계로
👉 getAttribute()와 .value 차이
👉 boolean attribute (checked, disabled) 왜 값이 없는지
👉 removeAttribute vs false 차이
이것들도 자연스럽게 이어져요 😄
🔄 값 변경도 가능
btn.dataset.userId = "20";
→ HTML에 즉시 반영됨
<button data-user-id="20"></button>
⚠️ 주의사항 (중요)
❗ 1. 보안용 ❌
<div data-password="1234"></div> ❌
- 개발자도구에서 누구나 볼 수 있음
- 인증, 권한, 토큰 저장용 ❌
❗ 2. 데이터 타입 없음
btn.dataset.count // "5"
- 숫자 ❌ 문자열 ⭕
- 필요하면 변환
Number(btn.dataset.count)
❗ 3. 너무 큰 데이터 ❌
- JSON 덩어리, 긴 텍스트 ❌
- DOM 비대해짐 → 성능 저하
💡 언제 쓰는 게 좋을까?
👍 적합한 경우
- element에 연결된 ID
- 상태값 (data-state="open")
- 서버에서 내려준 초기 값
- 클릭 시 필요한 파라미터
<li data-post-id="345" data-category="news"></li>
👎 안 좋은 경우
- 민감 정보
- 대용량 데이터
- 자주 변경되는 상태 (→ JS 변수/상태관리)
🤔 class랑 뭐가 달라?
| 구분 | class | data-* |
| 목적 | 스타일/선택자 | 데이터 저장 |
| 의미 | CSS 중심 | JS 중심 |
| 여러 개 | 가능 | 가능 |
| 접근 | classList | dataset |
👉 class는 디자인, *data-는 데이터
한 줄 정리
- data-* = HTML 요소에 붙이는 JS용 데이터
- 브라우저는 무시, 개발자는 적극 활용
- 보안·대용량·타입은 조심
원하면
👉 실무 예제 (이벤트 처리 / 서버 데이터 바인딩)
👉 React·Vue에서 data-* 쓰는 경우
👉 복사 방지 사이트에서 data-*로 숨겨둔 텍스트 분석
이런 것도 이어서 설명해줄게요.
'개발자도구' 카테고리의 다른 글
| 요청 헤더 Accept-Encoding & 응답 헤더 Vary: Accept-Encoding 헤더 (0) | 2025.12.15 |
|---|---|
| Sec-Fetch-Dest / Mode / Site (0) | 2025.12.09 |
| melonweb_cmtpgn_tunnel2.html & melonlib.min.js (0) | 2025.12.02 |
| document.domain mutation is ignored because the surrounding agent cluster is origin-keyed (0) | 2025.12.01 |
| 403 vs 401 에러 코드 (0) | 2025.12.01 |
| URL path가 폴더명처럼 보일 때, 폴더 전체를 불러오는 것일까? & md(markdown) 파일 렌더링 방식 (0) | 2025.12.01 |
| https://global.apis.naver.com 은 어떤 도메인? 엔드포인트 & 게이트웨이 & CORS (0) | 2025.11.24 |
| https://pagead2.googlesyndication.com 는 무슨 도메인? (0) | 2025.11.23 |