HTML에서 체크박스 만드는 법
체크박스는 <input type="checkbox">예요. 텍스트와 함께 <label> 안에 넣으면 텍스트를 클릭해도 체크가 바뀌어요. 상자를 하나 또는 둘 다 체크하고 Save를 눌러 폼이 무엇을 보내는지 보세요.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Checkbox</title>
</head>
<body>
<form>
<p><label><input type="checkbox" name="newsletter" value="yes"> Send me the newsletter</label></p>
<p><label><input type="checkbox" name="updates" checked> Product updates</label></p>
<button type="submit">Save</button>
</form>
</body>
</html>Master HTML the hands-on way
Interactive lessons, real projects, and instant feedback.
START NOW
기본 상태에서 폼은 updates=on만 보내요. 규칙 두 가지로 설명돼요.
체크된 상자는 name=value를 보내요. 두 번째 상자에는 value가 없어서 기본값 on을 보내요.
체크하지 않은 상자는 아무것도 보내지 않아요. newsletter=no도 아니고 빈 값도 아니에요. 데이터에 그 이름 자체가 없어요.
HTML의 checked는 시작 상태를 정해요. 다른 불리언 속성처럼 속성이 있느냐가 중요해서, checked="false"도 상자를 체크해요.
체크박스 속성
속성 하는 일 name체크됐을 때 보내는 키 value체크됐을 때 보내는 값(기본값 on) checked페이지가 로드될 때 체크된 상태 required이 상자를 체크해야 폼이 제출됨 disabled체크를 바꿀 수 없고 전송되지 않음
체크박스 묶음
"이 중에서 원하는 만큼 고르세요"에는 모든 상자에 같은 name과 서로 다른 value를 주세요. 묶음에 이름이 생기도록 <legend>와 함께 <fieldset>으로 감싸세요.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Checkbox group</title>
<style>
body { font-family: system-ui, sans-serif; }
fieldset { border: 1px solid #9ca3af; border-radius: 8px; }
label { display: block; margin: 4px 0; }
</style>
</head>
<body>
<form>
<fieldset>
<legend>Topics you like</legend>
<label><input type="checkbox" name="topic" value="html" checked> HTML</label>
<label><input type="checkbox" name="topic" value="css" checked> CSS</label>
<label><input type="checkbox" name="topic" value="js"> JavaScript</label>
</fieldset>
<p><button type="submit">Save</button></p>
</form>
</body>
</html>
폼은 topic=html과 topic=css를 보내요. 같은 이름이 두 번이에요. 대부분의 서버 프레임워크는 이걸 목록으로 읽어요. PHP는 예외인데, 이름이 대괄호로 끝나지 않으면(name="topic[]") 마지막 값만 남겨요. JavaScript에서는 new FormData(form).getAll('topic')이 ['html', 'css']를 돌려줘요.
체크하지 않았을 때도 값 보내기
서버에 명시적인 "아니요"가 필요하면 체크박스 앞에 같은 이름의 숨겨진 input을 두세요. 체크하지 않으면 숨겨진 값만 전송되고, 체크하면 둘 다 전송되며 서버는 마지막 값을 가져가요.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Unchecked value</title>
</head>
<body>
<form>
<input type="hidden" name="marketing" value="no">
<label><input type="checkbox" name="marketing" value="yes"> Marketing emails</label>
<p>
<label><input type="checkbox" name="terms" value="accepted" required> I accept the terms</label>
</p>
<button type="submit">Continue</button>
</form>
</body>
</html>
Continue를 눌러 보세요. 약관 상자가 required라서 체크하기 전에는 폼이 거부해요. 그다음에는 marketing=no와 terms=accepted를 보내고, 첫 번째 상자를 체크했다면 marketing=no, marketing=yes, terms=accepted를 보내요. Rails 같은 프레임워크가 정확히 이런 숨겨진 input 쌍을 만들어요. 이 방법에 기대기 전에 서버가 반복되는 이름을 어떻게 읽는지 확인하세요.
전체 선택과 indeterminate 상태
체크박스에는 "일부만 선택됨"을 뜻하는 세 번째 모양인 대시가 있어요. JavaScript에서만 쓸 수 있고, box.indeterminate = true로 설정해요. "전체 선택" 상자는 자기가 제어하는 상자 중 일부만 체크됐을 때 이 상태를 써요.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Select all</title>
<style>
body { font-family: system-ui, sans-serif; }
ul { list-style: none; padding-inline-start: 24px; margin: 4px 0; }
</style>
</head>
<body>
<label><input type="checkbox" id="all"> Select all</label>
<ul>
<li><label><input type="checkbox" class="item" checked> Email</label></li>
<li><label><input type="checkbox" class="item"> SMS</label></li>
<li><label><input type="checkbox" class="item"> Push</label></li>
</ul>
<script>
const all = document.querySelector('#all');
const items = [...document.querySelectorAll('.item')];
const sync = () => {
const count = items.filter((box) => box.checked).length;
all.checked = count === items.length;
all.indeterminate = count > 0 && count < items.length;
};
all.addEventListener('change', () => {
items.forEach((box) => { box.checked = all.checked; });
});
items.forEach((box) => box.addEventListener('change', sync));
sync();
</script>
</body>
</html>
indeterminate는 모양(그리고 스크린 리더가 알려 주는 "혼합" 또는 "부분 선택")만 바꿔요. 속으로는 여전히 체크됐거나 안 됐거나 둘 중 하나이고, 폼이 보내는 것도 그 상태예요. indeterminate 상자를 클릭하면 대시가 사라지고 체크가 바뀌어요.
체크박스에 반응하려면 change 이벤트를 쓰고, 상태는 box.checked로 읽으세요. box.value는 체크가 바뀌어도 그대로예요.
체크박스 꾸미기
가장 빠른 방법은 accent-color예요. 기본 체크박스의 색만 바꾸고 동작은 모두 유지해요. 완전히 다른 모양을 원하면 appearance: none으로 기본 그림을 없애고 input을 다른 상자처럼 꾸미세요.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Checkbox styles</title>
<style>
body { font-family: system-ui, sans-serif; }
label { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.accent { accent-color: #16a34a; width: 20px; height: 20px; }
.custom {
appearance: none;
width: 20px;
height: 20px;
margin: 0;
border: 2px solid #6b7280;
border-radius: 5px;
display: grid;
place-content: center;
}
.custom::before {
content: "";
width: 10px;
height: 6px;
border: 3px solid #fff;
border-top: 0;
border-right: 0;
transform: rotate(-45deg) translate(1px, -1px);
visibility: hidden;
}
.custom:checked { background: #2563eb; border-color: #2563eb; }
.custom:checked::before { visibility: visible; }
.custom:focus-visible { outline: 3px solid #f59e0b; outline-offset: 2px; }
</style>
</head>
<body>
<label><input type="checkbox" class="accent" checked> accent-color</label>
<label><input type="checkbox" class="custom" checked> appearance: none</label>
<label><input type="checkbox" class="custom"> appearance: none, unchecked</label>
</body>
</html>
직접 꾸민 상자도 진짜 체크박스예요. 클릭과 Space로 체크가 바뀌고, 폼과 함께 전송되고, 체크박스라고 알려 줘요. :checked로 그리고 :focus-visible 스타일도 유지하세요. 기본 그림이 없으면 직접 추가하지 않은 포커스 링은 없거든요.
토글 스위치
스위치는 "이걸 포함"이 아니라 "지금 켜짐/꺼짐"을 뜻하는 체크박스예요. appearance: none으로 그리고 role="switch"를 추가하면, 스크린 리더가 체크된 체크박스 대신 켜지거나 꺼진 스위치라고 알려 줘요.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Toggle switch</title>
<style>
body { font-family: system-ui, sans-serif; }
label { display: inline-flex; align-items: center; gap: 10px; }
.switch {
appearance: none;
margin: 0;
width: 44px;
height: 24px;
border-radius: 12px;
background: #9ca3af;
position: relative;
cursor: pointer;
transition: background 0.2s;
}
.switch::before {
content: "";
position: absolute;
top: 3px;
inset-inline-start: 3px;
width: 18px;
height: 18px;
border-radius: 50%;
background: #fff;
transition: inset-inline-start 0.2s;
}
.switch:checked { background: #16a34a; }
.switch:checked::before { inset-inline-start: 23px; }
.switch:focus-visible { outline: 3px solid #f59e0b; outline-offset: 2px; }
</style>
</head>
<body>
<label><input type="checkbox" class="switch" role="switch" name="dark" checked> Dark mode</label>
</body>
</html>
inset-inline-start는 줄의 시작 쪽에서부터 손잡이 위치를 정해서, 오른쪽에서 왼쪽으로 쓰는 페이지에서는 스위치가 반대 방향으로 움직여요. 그래야 맞아요. 하나만 고를 수 있는 선택지 묶음은 HTML 라디오 버튼 을 보세요.
자주 하는 실수
체크하지 않은 상자에서 false를 기대함. 아무것도 전송되지 않아요. 서버에서 이름이 없으면 "꺼짐"으로 처리하거나 숨겨진 input을 추가하세요.
checked="false". 여전히 체크돼요. 속성을 지우세요.
라벨 없는 체크박스 , 또는 옆에 텍스트는 있지만 <label>이 아닌 경우. 작은 상자만 클릭 대상이 되고, 스크린 리더는 이름 없이 "체크박스"라고만 알려 줘요. HTML label 을 보세요.
value를 빼먹음. 묶음의 모든 상자가 on을 보내서 서버가 구별할 수 없어요.
체크 여부를 보려고 box.value를 읽음. box.checked를 쓰세요.
"하나만 고르기"에 체크박스를 씀. 그건 라디오 버튼이 하는 일이에요.