SOYOYU

<button>

클릭으로 동작을 트리거하는 인터랙티브 컨트롤. URL 이동은 a, 동작은 button.

SEO·GEO·AEO·A11y 4축에서의 의미

SEO

●●●○○중간

라우팅 vs 액션 구분의 신호. button을 라우팅에 쓰면 검색엔진 링크 인식 손해.

GEO

●●●●○높음

AI 에이전트가 클릭 가능 액션을 식별. button vs a 구분이 자동화 성공률 결정.

AEO

●●○○○낮음

답변 인용에 직접 영향 적음.

A11y

●●●●●결정적

키보드·스크린리더·AI 에이전트 모두 button 시맨틱 의존. div onClick은 모두에게 깨짐.

올바른 사용

  • type 명시 (button|submit|reset)
  • 텍스트가 없으면 aria-label
  • 라우팅에는 button 안 씀 (a/Link가 정답)
  • disabled로 비활성
표준 button
<button type="button" onClick={handleClick}>
  결제하기
</button>

type 명시 + onClick. 가장 기본.

아이콘만 있는 button
<button type="button" aria-label="설정 열기">
  <SettingsIcon aria-hidden="true" />
</button>

시각 텍스트 없으면 aria-label. 아이콘은 aria-hidden.

form submit
<form onSubmit={handleSubmit}>
  ...
  <button type="submit">전송</button>
</form>

form 안 submit 버튼은 type="submit". Enter 키로도 동작.

자주 보는 안티패턴

  • type 누락 — form 안에서 submit으로 오인
  • div + onClick으로 button 흉내
  • 라우팅에 button onClick={router.push}
  • 아이콘 button에 aria-label 누락
div + onClick
<div onClick={handleClick} className="btn">
  결제하기
</div>

Tab 포커스 안 됨, Enter/Space 안 먹음, 스크린리더 "버튼" 발화 안 함.

라우팅에 button
<button onClick={() => router.push('/terms')}>
  약관 보기
</button>

우클릭 새 탭, Ctrl+클릭 모두 불가. 검색엔진 링크 인식 안 됨.

type 누락
<form>
  <button onClick={save}>저장</button>
  <input ... />
</form>

type 기본값이 submit. Enter 시 의도치 않은 form 제출.

해설

클릭으로 동작을 일으키는 인터랙티브 컨트롤. 시리즈 #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 처리가 더 견고.