HTML에서 버튼 만드는 법
버튼은 <button> 요소예요. 태그 사이의 내용이 사용자에게 보이고, type="button"은 폼을 보내는 대신 내 스크립트를 실행한다는 뜻이에요. 미리보기에서 클릭해 보세요.
동작하는 버튼에 필요한 건 이게 전부예요. 라벨, type, 그리고 click을 듣는 무언가요. 브라우저가 키보드 포커스를 받게 해 주고 Enter나 Space로 실행되게 해 주니, 키보드 사용자는 따로 작업하지 않아도 쓸 수 있어요. 클릭 핸들러를 단 <div>는 이 중 어느 것도 얻지 못해요.
태그에 onclick="..."를 바로 쓴 코드도 볼 수 있어요. 동작은 하지만, addEventListener를 쓰면 스크립트가 마크업 밖에 있고 버튼 하나에 리스너를 여러 개 달 수 있어요.
버튼 type 세 가지
type 속성은 버튼을 눌렀을 때 무슨 일을 할지 정해요.
type | 하는 일 | 용도 |
|---|---|---|
submit | 버튼이 속한 폼을 보냄. 기본값이에요. | 폼의 주요 동작 |
button | 그 자체로는 아무것도 안 하고, JavaScript가 정함 | 토글, 대화 상자, 카운터 등 스크립트로 하는 모든 것 |
reset | 폼의 모든 필드를 처음 값으로 되돌림 | 거의 안 씀. 사용자가 실수로 누르기 쉬워요 |
무언가 입력한 뒤 각 버튼을 눌러 보세요. 폼이 제출되어도 미리보기는 페이지를 떠나지 않고, 폼이 보냈을 데이터를 보여 줘요.
Reset은 빈 필드가 아니라 HTML에 쓰인 값(Ada)으로 되돌려요. 그리고 Uppercase에서 type="button"을 지우면 제출 버튼이 돼서, 이름을 대문자로 바꾼 뒤 폼을 제출해요.
HTML 제출 버튼: name과 value 보내기
제출 버튼은 자기 name과 value를 가질 수 있어요. 그 버튼으로 제출했을 때만 폼과 함께 전송되기 때문에, 버튼 두 개가 폼 하나를 공유하면서 어떤 동작을 골랐는지 서버에 알릴 수 있어요.
Publish를 누르면 폼이 title=My first post와 action=publish를 보내요. 제목을 지우고 아무 버튼이나 눌러 보세요. required가 제출을 막고 브라우저가 자체 메시지를 보여 줘요. 텍스트 필드에서 Enter를 눌러도 제출되는데, 브라우저는 이걸 첫 번째 제출 버튼을 누른 것으로 취급해서 action=draft를 보내요.
제출 버튼은 그 버튼 하나에 대해 폼 설정을 덮어쓰는 속성도 받아요. formaction(다른 URL), formmethod, formtarget, 그리고 유효성 검사를 건너뛰는 formnovalidate예요. 필수 필드가 아직 비어 있어도 동작해야 하는 "임시 저장" 버튼에는 보통 formnovalidate를 써요.
폼 밖에 둔 버튼도 폼의 id를 받는 form 속성으로 그 폼을 제출할 수 있어요. <button type="submit" form="signup">Sign up</button>처럼요.
비활성화된 버튼
disabled 속성은 버튼을 꺼요. 비활성화된 버튼은 클릭을 무시하고, Tab에서 건너뛰어지고, 폼과 함께 전송되지 않아요. 브라우저가 회색으로 표시하기도 해요. 조건이 충족될 때까지 제출 버튼을 비활성화해 두는 패턴이 흔해요.
JavaScript에서 button.disabled는 불리언 속성이라 true나 false로 설정해요. HTML에서는 속성이 있느냐가 중요해서, disabled="false"도 버튼을 비활성화해요.
비활성화된 버튼은 왜 비활성화됐는지 알려 주지 않고, Tab으로 이동하는 키보드 사용자와 스크린 리더 사용자는 그 버튼에 닿지도 못해요. 이유가 뻔하지 않다면 버튼 옆에 텍스트로 알려 주거나, 버튼을 활성화해 두고 눌렀을 때 오류 메시지를 보여 주세요.
CSS로 버튼 꾸미기
브라우저는 자체 글꼴, 테두리, 배경으로 버튼을 그려요. 속성 몇 개를 지정하면 그걸 모두 바꿀 수 있어요. 포커스 스타일은 보이게 유지하세요. 키보드 사용자는 그걸로 지금 위치를 알아요.
각 규칙의 역할이에요.
font: inherit는 버튼이 페이지 글꼴을 쓰게 해요. 이게 없으면 버튼은 브라우저 자체의 더 작은 폼 글꼴(Chrome과 Firefox에서 13.33px)을 써요.cursor: pointer는 손 모양 커서를 보여 줘요. 버튼은 기본적으로 일반 화살표를 보여 줘요.:hover,:active,:focus-visible은 포인터, 누름, 키보드 상태를 꾸며요.:focus-visible은 키보드 포커스일 때는 링을 보여 주지만 마우스 클릭 뒤에는 보여 주지 않아서, 그걸 피하려고outline을 없앨 필요가 없어요.:disabled는disabled속성이 있는 버튼을 대상으로 해요.
미리보기에서 Tab을 눌러 주황색 포커스 링이 앞의 두 버튼 사이를 오가는 걸 보세요. 비활성화된 버튼은 건너뛰어요. (Mac의 Safari와 Firefox는 시스템 설정에서 키보드 탐색을 켜야 Tab으로 버튼에 가요. Safari에서는 설정 없이 Option+Tab으로도 돼요.) 전체 상태 목록은 CSS hover를 보세요.
아이콘이 있는 버튼
<button>은 HTML을 담을 수 있어서 아이콘을 텍스트 옆에 넣을 수 있어요. 아이콘만 있는 버튼은 스크린 리더가 읽을 텍스트가 없으니 aria-label을 주세요.
SVG의 aria-hidden="true"는 SVG를 접근성 트리에서 빼서, 첫 번째 버튼은 "Add item, 버튼", 두 번째는 "Delete, 버튼"으로 읽혀요.
button과 input type="submit" 차이
<input type="submit">은 제출 버튼을 쓰는 옛날 방식이에요. 둘 다 같은 방식으로 폼을 보내고, 차이는 무엇을 담을 수 있느냐예요.
<input type="submit" value="Send"> <!-- label comes from value, text only -->
<button type="submit">Send</button> <!-- label is the content, HTML allowed -->
<button> | <input type="submit"> | |
|---|---|---|
| 라벨 | 태그 사이의 내용 | value 속성 |
| 아이콘이나 마크업 포함 | 가능 | 불가능 |
| 닫는 태그 | 필요 | 없음(빈 요소) |
| 기본 type | submit | 항상 제출 |
<button>을 쓰세요. <input type="submit">을 남겨 둘 이유는 그걸 선택하는 기존 CSS나 JavaScript뿐이에요. 나머지 두 type에 해당하는 <input type="button">과 <input type="reset">도 있어요.
버튼일까, 링크일까?
버튼은 현재 페이지에서 무언가를 해요. 제출하고, 열고, 토글하고, 삭제해요. 링크는 다른 URL로 가요. 클릭했을 때 주소창이 바뀐다면 <a href>여야 하고, 디자인상 필요하면 버튼처럼 꾸미면 돼요. 그 패턴과 링크 안의 버튼이 왜 올바르지 않은지는 HTML 버튼 링크에서 다뤄요.
접근성
- 진짜
<button>을 쓰세요. 포커스를 받고, Enter와 Space로 동작하고, 버튼이라고 알려 줘요.<div>로 이걸 다시 만들려면role,tabindex, 키 핸들러가 필요하고, 그래도 대개 불완전해요. - 모든 버튼에는 이름이 필요해요. 텍스트, 또는 아이콘만 있다면
aria-label이에요. - 포커스 스타일을 보이게 유지하세요. 대체 스타일 없이
outline: none만 쓰면 키보드 사용자가 길을 잃어요. - 누르기에 충분히 크게 만드세요. WCAG 2.2는 최소 24 × 24 CSS 픽셀을 요구해요. 위의 꾸민 예제 정도의 padding이면 높이가 약 40픽셀이 돼요.
- 토글 버튼에는
aria-pressed="true"나"false"를 추가하고 상태가 바뀔 때 업데이트하세요.
자주 하는 실수
- 폼 안의
type없는 버튼. 폼을 제출해요. 제출용이 아닌 모든 버튼에type="button"을 쓰세요. - 버튼을 링크로 감쌈.
<a href="/x"><button>Go</button></a>는 올바르지 않은 HTML이고 브라우저마다 다르게 동작해요. 버튼처럼 꾸민 링크, 즉 요소 하나만 쓰세요. <div onclick>을 버튼으로 씀. 키보드로 갈 수 없어요.:focus-visible스타일 없이 포커스 outline을 없앰.disabled="false"로 쓰고 활성화될 거라고 기대함. 대신 속성을 지우세요.font: inherit를 빼먹음. 버튼만 페이지와 다른 글꼴을 쓰는 이유를 궁금해하게 돼요.
자주 묻는 질문
HTML에서 버튼은 어떻게 만드나요?
<button type="button">Click me</button>처럼 쓰세요. 태그 사이의 텍스트가 라벨이에요. 무언가 동작하게 하려면 JavaScript로 click 리스너를 추가하고, 폼을 보내려면 type="submit"으로 <form> 안에 넣으세요.
HTML 버튼의 기본 type은 무엇인가요?
submit이에요. 폼 안에 있는 type 속성 없는 <button>은 클릭하면 그 폼을 제출해요. 폼 안의 평범한 버튼이 페이지를 새로고침할 수 있는 이유예요. JavaScript를 실행하는 버튼에는 type="button"을 쓰세요.
button과 input type=submit은 무엇이 다른가요?
둘 다 폼을 제출해요. <button>은 태그 사이에 HTML(아이콘, 굵은 텍스트)을 담을 수 있고, <input type="submit">은 value 속성에서 라벨을 가져와 일반 텍스트만 담을 수 있어요. 요즘 코드는 대부분 <button>을 써요.
HTML에서 버튼을 비활성화하려면 어떻게 하나요?
disabled 속성을 추가하세요. <button disabled>Save</button>처럼요. 비활성화된 버튼은 클릭도 포커스도 안 되고, 이름과 값이 폼과 함께 전송되지 않아요. 다시 활성화하려면 속성을 지우세요(JavaScript에서는 btn.disabled = false).
HTML 버튼을 누르면 다른 페이지로 가게 하려면 어떻게 하나요?
버튼처럼 꾸민 링크를 쓰세요. <a href="/pricing" class="btn">See pricing</a>처럼요. <button>은 현재 페이지에서 하는 동작용이에요. 버튼을 <a>로 감싸는 건 올바르지 않은 HTML이에요.
버튼을 누르면 왜 페이지가 새로고침되나요?
<form> 안에 있고 type이 없어서 제출 버튼이 된 거예요. 그래서 브라우저가 폼을 보내고 응답을 불러와요. 버튼에 type="button"을 추가하거나, JavaScript로 폼을 처리한다면 submit 리스너에서 event.preventDefault()를 호출하세요.