버튼을 사용하세요
(gomakethings.com)- 웹 인터페이스에서
<div>대신<button>을 사용하는 것이 접근성과 기능 면에서 올바른 선택임 -
<div>는 스크린 리더에 인터랙티브 요소로 인식되지 않으며, 키보드 포커스나Enter,Spacebar입력에도 반응하지 않음 -
[role="button"]이나[tabindex="0"]속성을 추가해도 포커스 순서 오류와 키보드 이벤트 처리 문제가 남음 - 이러한 문제를 해결하기 위해 여러 이벤트 리스너와 조건문을 추가하는 것은 불필요한 복잡성을 초래함
-
<button>은 접근성, 포커스, 키보드 입력 처리 기능을 기본 제공하므로, 단순하고 표준적인 해결책임
잘못된 접근: <div>로 버튼 만들기
- React나 HTMX 사용자들 사이에서
<div onclick="...">형태로 모달 열기 등 인터랙션을 구현하는 사례가 많음- 예시 코드:
<div onclick="showSignIn()">Open Modal</div>
- 예시 코드:
- 이 방식의 문제점
- 스크린 리더가 해당 요소를 인터랙티브 요소로 인식하지 않음
- 키보드로 포커스 이동 불가
-
click이벤트만 작동하며,Enter나Spacebar입력에는 반응하지 않음
접근성 “수정” 시도의 한계
-
[role="button"]속성을 추가하면 스크린 리더 인식 문제는 해결되지만,
포커스 가능성이나 키보드 입력 처리 문제는 여전히 남음 -
[tabindex="0"]을 추가해 포커스를 가능하게 할 수 있으나,
포커스 순서가 꼬이거나 예기치 않은 이동이 발생할 위험이 있음 - 키보드 입력을 처리하려면
keydown이벤트를 전역(document)에 등록하고,
Enter또는' '(space) 키를 감지해 포커스된 요소를 찾아 실행해야 함document.addEventListener('keydown', (event) => { if (event.key !== 'Enter' && event.key !== ' ') return; const notRealBtn = document.activeElement.closest('[onclick]'); if (!notRealBtn) return; // 실행 코드 }); - 결과적으로,
<button>이 기본적으로 제공하는 기능을 복잡하게 재구현하는 셈임
<button>이 제공하는 기본 기능
-
<button>요소는 다음을 자동으로 지원- 암묵적 역할(
[role="button"]) - 자동 포커스 가능성
- 포커스 상태에서
Enter와Spacebar입력 시click이벤트 발생
- 암묵적 역할(
- 동일한 동작을
<div>로 구현하려면 여러 속성과 스크립트가 필요하지만,
<button>은 단 한 줄로 해결 가능<button onclick="showSignIn()">Open Modal</button>
결론: 단순함이 최선
-
<button>은 접근성 표준을 충족하면서도 코드량을 줄이는 가장 단순한 방법 - 불필요한 이벤트 처리나 속성 추가 없이 표준 HTML 요소를 사용하는 것이 유지보수성과 효율성 측면에서 유리
- “게으른 개발자일수록 올바른 요소를 사용하라”는 메시지로,
불필요한 복잡성을 피하고 기본 기능을 활용하는 개발 습관의 중요성을 강조함
background, border, outline, appearance, -webkit-appearance, cursor
덮어씌워야하는 기본 스타일시트가 너무 많아요ㅠㅠ
Hacker News 의견
-
내 불만 중 하나는 웹사이트가
onclick핸들러로 네비게이션을 구현하는 것임
그냥<a>태그를 쓰면 새 탭 열기, 접근성 장치 통합, 오른쪽 클릭 메뉴 등 모든 게 자동으로 잘 작동함
네비게이션이라면 자바스크립트 수프 대신 링크를 써야 함- 최근 몇 년간 이런 식으로 구현하는 사례가 늘었음
아마 프레임워크 영향이나 무관심 때문일 것임
그래도 전통적인 방식이 UX 면에서는 거의 항상 더 나음
<a>태그를 대체하려는 사람들에게는 약간의 불편함이 있기를 바람 - React로 시작한 개발자들이 기초 HTML 개념을 배우지 않고 바로 “재미있는 것”으로 뛰어든 게 문제라고 생각함
이런 사람들이 잘못된 패턴을 만들어서 후속 개발자들이 그대로 따라감
<div>를 버튼처럼 꾸며야 했던 경우는 극히 드물었음 - JS 기반 스크롤도 없애야 함
나는 마우스 가운데 버튼으로 스크롤을 자주 하는데, 많은 사이트가 이걸 망가뜨림 - 이 얘기를 들으니 Microsoft Office 365의 링크 체커가 떠오름
왼쪽 클릭하면 안전성 검사 페이지가 뜨는데, 가운데 클릭하면 그냥 바로 이동함 - 최근 참여한 React 프로젝트에서도 모든 네비게이션이
onClick으로 되어 있음
사실상 링크인 요소조차 전부 클릭 핸들러로 처리되어 있어서 이해가 안 됨
- 최근 몇 년간 이런 식으로 구현하는 사례가 늘었음
-
대부분의 버튼에는
type="button"을 명시해야 함
기본값은submit이라 폼 안에 있으면 자동으로 제출됨
아마 일부 개발자들이 이걸 몰라서<div>를 쓰는 듯함- OP의 긴 글에서 이 핵심 정보가 빠졌다고 생각함
기본 타입 버튼은 이상하게 동작해서 JS 핸들러를 건너뛰기도 함 - 기본값은
<input type="submit">에 해당하고<button>은 다름 - 나도 이걸 직접 겪으며 배움
-
<div>를 쓰면type="submit"문제를 피할 수 있음
<div>는 처음부터 비어 있어서 필요한 기능만 추가할 수 있고, 나중에 수정도 쉬움
반면<button>은 기본 동작을 이해하려면 문서를 봐야 함
결국 명시적 제어 vs 내장 기능의 선택 문제임
- OP의 긴 글에서 이 핵심 정보가 빠졌다고 생각함
-
“그 목적에 맞게 만들어진 HTML 요소를 그대로 쓰자”는 방향으로 글을 확장했으면 좋겠음
많은 SPA 개발자들이 HTML 요소의 의미를 잘 몰라서 매번 바퀴를 다시 만듦- 요소들이 좀 더 스타일링 가능했으면 좋겠음
예를 들어 기본 date picker는 너무 못생겨서 JS 기반으로 대체하게 됨 - “플랫폼을 그대로 사용하라”는 말이 HTML5 이후 프론트엔드에서 자주 나오지만, 아직 모든 곳에 퍼지진 않음
- 실제로는 대부분의 개발자가 HTML 요소를 거의 모르고, DIV 하나로 모든 걸 해결하려 함
- 2010년쯤에는 브라우저마다 버튼 스타일이 달라서 직접 만들어야 했음
그래서 커스텀 버튼이 생겨난 배경이 있음
- 요소들이 좀 더 스타일링 가능했으면 좋겠음
-
요즘은 클릭 가능한 영역을 찾느라 화면을 이리저리 누르는 세대가 생겼음
10년 전 누군가가 링크 드래그를 텍스트 선택보다 중요하게 만들어서 이제는 텍스트 선택이 거의 불가능함
이걸 고치려면 브라우저를 포크해야 할지도 모름- 나는 링크를 드래그해서 백그라운드 탭으로 여는 습관이 있음
Alt(또는 Option) 키를 누르면 링크 안의 텍스트를 선택할 수 있음 - iOS에서 전화번호를 복사하려다 자동으로 전화 걸리는 것도 비슷하게 짜증남
정말 원치 않는 동작임 - 선택 불가능한 텍스트는 미치게 함
macOS의 TextSniper 앱을 쓰면 영역을 선택해 OCR로 텍스트를 복사할 수 있음
덕분에 Google Analytics도 조금 쓸 만해짐 - 나도 링크 안의 일부 텍스트를 선택하려다 실패하는 일이 많음
이런 문제는 더 자주 언급되어야 함 - 링크 텍스트 선택용 브라우저 확장도 있음
예전엔 Select Like A Boss, 지금은 Drag-Select Link Text라고 함
- 나는 링크를 드래그해서 백그라운드 탭으로 여는 습관이 있음
-
Chrome의 기본 스타일시트에
button {align-items: flex-start}가 있어서 flexbox 크기 오류로 한참 헤맸음
그래도 가능한 한 올바른 HTML 요소를 쓰려 하지만, 작은 사이드 프로젝트에서는<div>가 더 편할 때가 있음-
appearance: none속성이 버튼 스타일 초기화에 유용함
나는.unbuttonify클래스를 만들어서 버튼처럼 동작하지만 다른 모양을 내게 함 - 프론트엔드 개발자라면 CSS 기본기를 알아야 한다는 점을 강조하고 싶음
-
-
가능한 한 요소를 본래 의도에 맞게 사용해야 함
-
버튼에 대한 두 가지 불만이 있음
하나는 어차피 스타일을 다시 입혀야 한다는 점,
또 하나는 버튼을 중첩할 수 없다는 경고임
이건 현실적으로 꽤 자주 마주침 -
LLM이 이런 잘못된 패턴을 자주 생성함
브라우저의 기본 기능을 무시하고 복잡하게 구현하는 경우가 많음
나는 Claude에게 이런 코드를 단순화하라고 자주 지시함
TypeScript에서도 에러 처리 방식을 이상하게 만드는 경향이 있음- LLM은 코드를 쓰는 능력은 뛰어나지만, 소프트웨어 엔지니어링 감각은 부족함
- 토큰 예측 특성상, LLM은 복잡한 패턴을 더 자주 선택함
-
나는 가능한 한 버튼을 기본으로 사용함
단, 실제로는 링크처럼 동작해야 하는 경우엔<a>태그를 씀- URL이 바뀌면 링크, 안 바뀌면 버튼으로 구분하는 편임
- “웹앱 내에서 이동하는 하이퍼링크”라면 그건
<a>태그임
-
왜
<div>를 쓰자는 의견이 나오는지 궁금했음- 아마
<div>가 이상한 외형 커스터마이징에 더 유리해서일 것임
그래서 버튼처럼 보이지도, 동작하지도 않게 됨 - 예를 들어 TV Tropes 같은 사이트는 긴 목록을 “폴더” 형태로 접었다 펼치는데, 이걸
<div onclick>으로 구현했었음 - 가장 흔한 이유는 기본 버튼 스타일을 덮어쓰기 귀찮아서임
- 아마