클릭으로 동작을 일으키는 인터랙티브 컨트롤. 시리즈 #1에서 깊이 다룬 button vs a 결정의 핵심. URL이 바뀌면 a, 바뀌지 않으면 button.
SEO 관점에서 button을 라우팅에 잘못 사용하면 — 검색엔진이 링크로 인식 못 함. 사이트의 내부 링크 구조에서 그 페이지가 사라진다. URL 이동은 반드시 a(또는 Next.js Link)로.
GEO 관점에서 AI 에이전트가 클릭 가능한 액션을 식별하는 1순위 신호. accessibility tree에서 button role로 명확. div onClick은 generic으로 보여 — 에이전트가 클릭 가능한지 추측.
A11y 관점에서 critical. button은 키보드 Tab 자동 포커스, Enter/Space 자동 발화, 스크린리더 "버튼" 라벨, disabled 상태 자동, focus-visible 스타일을 모두 무료로 제공. div onClick으로 흉내 내려면 수십 줄의 코드 — 그래도 미세한 동작이 깨진다.
자주 보는 안티패턴: type 누락(form 안에서 submit으로 오인 — Enter 시 의도치 않은 제출. type="button"을 반드시 명시), div + onClick으로 button 흉내(시리즈 #1의 핵심 안티패턴), 라우팅에 button onClick={router.push}(우클릭 새 탭, Ctrl+클릭, 검색엔진 인식 모두 깨짐 — a 또는 Link가 정답), 아이콘 버튼에 aria-label 누락(시각 사용자가 모르는 텍스트가 시각장애 사용자에게 더더욱 모름).
3가지 type — submit(form 제출, 기본값), button(일반 동작), reset(form 초기화). form 외부의 button은 기본값이 submit이지만 동작 없음. form 내부에서는 반드시 명시가 안전.
disabled 속성 — 비활성 상태. native가 자동으로 포커스 제외, 클릭 막기, 스크린리더 "비활성" 발화. CSS 시각만 회색으로 처리하지 말고 native 속성으로.
aria-pressed — toggle 버튼(좋아요, 즐겨찾기 등)의 ON/OFF 상태. <button aria-pressed="true">좋아요</button>. 스크린리더가 "눌림" 상태 발화.
aria-expanded — 토글 가능한 요소(아코디언, 메뉴) 트리거. details(시리즈 #4)나 Radix Accordion에서 자동 처리.
UI 라이브러리 — Radix Button, Headless UI Button, shadcn Button 모두 native button을 렌더하고 a11y는 무료. 직접 만들 때보다 키보드 단축키, focus 관리, disabled 처리가 더 견고.