6P by GN⁺ 1일전 | ★ favorite | 댓글 4개
  • 웹 인터페이스에서 <div> 대신 <button>을 사용하는 것이 접근성과 기능 면에서 올바른 선택
  • <div>스크린 리더에 인터랙티브 요소로 인식되지 않으며, 키보드 포커스나 Enter, Spacebar 입력에도 반응하지 않음
  • [role="button"]이나 [tabindex="0"] 속성을 추가해도 포커스 순서 오류와 키보드 이벤트 처리 문제가 남음
  • 이러한 문제를 해결하기 위해 여러 이벤트 리스너와 조건문을 추가하는 것은 불필요한 복잡성을 초래함
  • <button>접근성, 포커스, 키보드 입력 처리 기능을 기본 제공하므로, 단순하고 표준적인 해결책임

잘못된 접근: <div>로 버튼 만들기

  • React나 HTMX 사용자들 사이에서 <div onclick="..."> 형태로 모달 열기 등 인터랙션을 구현하는 사례가 많음
    • 예시 코드:
      <div onclick="showSignIn()">Open Modal</div>
      
  • 이 방식의 문제점
    • 스크린 리더가 해당 요소를 인터랙티브 요소로 인식하지 않음
    • 키보드로 포커스 이동 불가
    • click 이벤트만 작동하며, EnterSpacebar 입력에는 반응하지 않음

접근성 “수정” 시도의 한계

  • [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"])
    • 자동 포커스 가능성
    • 포커스 상태에서 EnterSpacebar 입력 시 click 이벤트 발생
  • 동일한 동작을 <div>로 구현하려면 여러 속성과 스크립트가 필요하지만,
    <button>은 단 한 줄로 해결 가능
    <button onclick="showSignIn()">Open Modal</button>
    

결론: 단순함이 최선

  • <button>접근성 표준을 충족하면서도 코드량을 줄이는 가장 단순한 방법
  • 불필요한 이벤트 처리나 속성 추가 없이 표준 HTML 요소를 사용하는 것이 유지보수성과 효율성 측면에서 유리
  • “게으른 개발자일수록 올바른 요소를 사용하라”는 메시지로,
    불필요한 복잡성을 피하고 기본 기능을 활용하는 개발 습관의 중요성을 강조함

background, border, outline, appearance, -webkit-appearance, cursor
덮어씌워야하는 기본 스타일시트가 너무 많아요ㅠㅠ

그래서 CSS Reset이 있는거죠.

우리나라 관공서 사이트는 <a> 많이 사용하던데...

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 내장 기능의 선택 문제임
  • “그 목적에 맞게 만들어진 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>으로 구현했었음
    • 가장 흔한 이유는 기본 버튼 스타일을 덮어쓰기 귀찮아서