HTML에서 라디오 버튼 만드는 법
라디오 버튼은 독자가 묶음에서 딱 하나만 고르게 해요. 각 선택지는 <input type="radio">이고, 같은 name을 주면 한 묶음이 돼요. 사이즈를 고르고 Order를 눌러 보세요.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Radio buttons</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>Size</legend>
<label><input type="radio" name="size" value="s"> Small</label>
<label><input type="radio" name="size" value="m" checked> Medium</label>
<label><input type="radio" name="size" value="l"> Large</label>
</fieldset>
<p><button type="submit">Order</button></p>
</form>
</body>
</html>Master HTML the hands-on way
Interactive lessons, real projects, and instant feedback.
START NOW
폼은 한 쌍, size=m을 보내요. 묶음의 name과 선택된 버튼의 value예요. 구성 요소는 이래요.
묶음의 모든 버튼에 같은 name . 하나를 고르면 나머지는 해제돼요.
각각 서로 다른 value . 서버가 받는 값이에요. value가 없는 라디오는 on을 보내서 서버에 아무것도 알려 주지 못해요.
기본 선택지에 checked .
텍스트를 클릭할 수 있도록 각각을 감싸는 <label> , 그리고 스크린 리더가 각 선택지와 함께 질문("Size")을 알려 주도록 <legend>가 있는 <fieldset> .
name이 묶음을 만들어요
이름이 다른 라디오는 별개의 묶음이고, 묶음마다 선택이 따로 있어요. 이 예제에는 묶음 두 개와, 실수로 라디오마다 이름을 따로 준 망가진 묶음이 있어요.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Radio groups</title>
<style>
body { font-family: system-ui, sans-serif; display: flex; flex-wrap: wrap; gap: 16px; }
fieldset { border: 1px solid #9ca3af; border-radius: 8px; }
.broken { border-color: #dc2626; }
label { display: block; margin: 4px 0; }
</style>
</head>
<body>
<fieldset>
<legend>Crust</legend>
<label><input type="radio" name="crust" value="thin" checked> Thin</label>
<label><input type="radio" name="crust" value="thick"> Thick</label>
</fieldset>
<fieldset>
<legend>Sauce</legend>
<label><input type="radio" name="sauce" value="tomato" checked> Tomato</label>
<label><input type="radio" name="sauce" value="pesto"> Pesto</label>
</fieldset>
<fieldset class="broken">
<legend>Broken: different names</legend>
<label><input type="radio" name="a" value="1"> One</label>
<label><input type="radio" name="b" value="2"> Two</label>
<label><input type="radio" name="c" value="3"> Three</label>
</fieldset>
</body>
</html>
망가진 묶음의 세 개를 모두 클릭해 보세요. 모두 선택된 채로 남고 어느 것도 다시 끌 수 없어요. 양쪽의 나쁜 점만 모은 셈이에요. 라디오는 같은 폼 안에서 name으로 묶이기 때문에, 폼 두 개가 각자 name="size" 묶음을 가질 수 있어요.
필수 묶음과 기본 선택 없음
묶음의 라디오 아무 데나 required를 주면 폼을 제출하기 전에 그중 하나를 반드시 골라야 해요. checked를 빼면 독자가 직접 고르게 되는데, 적절한 기본값이 없을 때 그게 맞아요.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Required radio 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>How did you hear about us?</legend>
<label><input type="radio" name="source" value="search" required> Search engine</label>
<label><input type="radio" name="source" value="friend"> A friend</label>
<label><input type="radio" name="source" value="video"> A video</label>
<label><input type="radio" name="source" value="other"> Other</label>
</fieldset>
<p><button type="submit">Continue</button></p>
</form>
</body>
</html>
고르지 않고 Continue를 누르면 브라우저가 메시지와 함께 제출을 막아요. 아무 선택지나 고르면 묶음 전체가 유효해져요. 필수가 아닌 묶음을 선택하지 않으면 체크하지 않은 체크박스처럼 아무것도 보내지 않아요.
클릭으로 라디오를 선택할 수는 있지만 해제할 수는 없어요. 선택한 뒤에도 "응답 없음"이 가능해야 한다면 그걸 위한 선택지를 따로 추가하세요.
JavaScript로 선택된 값 읽기
form.elements.name은 묶음 전체를 돌려주고, 그 .value는 선택된 라디오의 값이에요(아무것도 없으면 빈 문자열). change 이벤트는 선택된 라디오에서 발생해요.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Selected value</title>
<style>
body { font-family: system-ui, sans-serif; }
label { margin-inline-end: 12px; }
#price { font-size: 1.4em; font-weight: bold; color: #2563eb; }
</style>
</head>
<body>
<form id="billing">
<label><input type="radio" name="period" value="monthly" checked> Monthly</label>
<label><input type="radio" name="period" value="yearly"> Yearly</label>
</form>
<p>Price: <span id="price"></span></p>
<script>
const form = document.querySelector('#billing');
const prices = { monthly: '12 per month', yearly: '96 per year' };
const show = () => {
document.querySelector('#price').textContent = prices[form.elements.period.value];
};
form.addEventListener('change', show);
show();
</script>
</body>
</html>
리스너가 폼에 있어서 폼의 어느 필드가 바뀌든 잡아요. 폼 밖에서는 document.querySelector('input[name="period"]:checked')로 선택된 라디오를 찾으세요.
키보드 동작
라디오 묶음은 Tab 순서에서 한 번만 멈춰요. Tab을 누르면 묶음 안으로 들어가 선택된 라디오(선택된 게 없으면 첫 번째)에 닿고, 화살표 키로 선택지 사이에서 선택을 옮겨요. Tab을 한 번 더 누르면 묶음을 벗어나요. 같은 name을 가진 진짜 라디오 input이라면 따로 작업하지 않아도 이렇게 동작하고, 이것도 <div> 요소로 라디오를 다시 만들지 말아야 하는 이유예요.
라디오 버튼 꾸미기
accent-color는 기본 라디오의 색을 바꿔요. appearance: none을 주면 원을 직접 그릴 수 있어요. 그리고 숨긴 라디오도 라벨로 선택할 수 있기 때문에, 라디오 동작은 모두 유지하면서 묶음을 카드 모음처럼 보이게 할 수도 있어요.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Radio styles</title>
<style>
body { font-family: system-ui, sans-serif; }
.accent { accent-color: #16a34a; }
.dot {
appearance: none;
width: 18px;
height: 18px;
margin: 0;
border: 2px solid #6b7280;
border-radius: 50%;
vertical-align: middle;
}
.dot:checked { border: 6px solid #2563eb; }
.dot:focus-visible { outline: 3px solid #f59e0b; outline-offset: 2px; }
.cards { display: flex; flex-wrap: wrap; gap: 8px; border: 0; padding: 0; margin: 16px 0 0; }
.card { display: block; padding: 10px 14px; border: 2px solid #e5e7eb; border-radius: 10px; cursor: pointer; }
.card input { position: absolute; opacity: 0; }
.card:has(input:checked) { border-color: #2563eb; background: #eff6ff; }
.card:has(input:focus-visible) { outline: 3px solid #f59e0b; outline-offset: 2px; }
</style>
</head>
<body>
<label><input type="radio" class="accent" name="c1" checked> accent-color</label>
<label><input type="radio" class="accent" name="c1"> accent-color</label>
<p>
<label><input type="radio" class="dot" name="c2" checked> Custom</label>
<label><input type="radio" class="dot" name="c2"> Custom</label>
</p>
<fieldset class="cards">
<legend>Plan</legend>
<label class="card"><input type="radio" name="plan" value="free" checked> Free</label>
<label class="card"><input type="radio" name="plan" value="pro"> Pro</label>
<label class="card"><input type="radio" name="plan" value="team"> Team</label>
</fieldset>
</body>
</html>
직접 그린 원은 체크됐을 때 두꺼운 테두리일 뿐이에요. 카드는 input을 display: none이 아니라 opacity: 0으로 숨겨서 포커스도 받고 화살표 키도 동작하게 하고, :has(input:checked)로 선택된 카드를 꾸며요. :has()는 현재 Chrome, Firefox, Safari에서 동작해요.
라디오, 체크박스, select 중 무엇을?
쓸 것 언제 라디오 버튼 모두 보여야 하는 몇 개(2~6개 정도)의 선택지 중 하나 체크박스 개수 제한 없는 선택, 아무것도 안 고르는 것 포함 <select>국가처럼 긴 목록 중 하나(HTML select 참고)
자주 하는 실수
한 묶음 안에서 name이 다름. 모든 라디오가 선택되고 어느 것도 해제할 수 없어요.
value가 없음. 무엇을 골랐든 서버는 on을 받아요.
직접 꾸미면서 input을 display: none으로 숨김. 더 이상 포커스를 받거나 키보드로 쓸 수 없어요.
<fieldset>과 <legend>가 없음. 스크린 리더가 질문 없이 "Small, 라디오 버튼"이라고만 알려 줘요.
"동의합니다" 상자에 라디오 버튼 하나를 씀. 체크를 절대 해제할 수 없어요. 체크박스 를 쓰세요.
한 묶음의 여러 라디오에 checked를 줌. 마지막 것만 선택돼요.