Cursor로 컴포넌트를 뽑고, v0.dev로 UI를 스케치하고, Claude로 인터랙션 로직을 붙이는 시대다. 그런데 AI가 만들어준 코드를 들여다보면 심심찮게 마주치는 장면이 있다. 페이지를 이동시키는 <button onclick="window.location.href='/'">, 모달을 여는 <a href="javascript:void(0)">. 동작은 한다. 하지만 이 코드는 틀렸다.
접근성 전문가 Mica Avigliano가 dev.to에 정리한 가이드는 이 문제를 명쾌하게 짚는다. 링크(<a>)와 버튼(<button>)은 단순히 생김새의 문제가 아니다. 링크는 사용자를 새로운 위치로 데려가고, 버튼은 현재 페이지 안에서 액션을 실행한다. 이 경계가 무너지면 키보드 사용자는 Space 키로 '버튼인 줄 알았던 링크'를 눌렀을 때 페이지가 스크롤되는 황당함을 경험한다. 스크린 리더는 "링크, 메뉴 열기"를 읽어주며 사용자에게 잘못된 기대를 심는다. 브라우저 히스토리는 예상치 못한 방식으로 쌓인다. 사용자가 느끼는 혼란은 코드 한 줄짜리 실수에서 비롯된다.
판단 기준은 생각보다 단순하다. 아코디언 섹션을 열고 닫는가? aria-expanded를 가진 <button>이다. 같은 페이지의 특정 섹션으로 점프하는가? <a href="#section">이다. 더 보기로 목록 아이템을 추가 로드하는가? <button>이고, 포커스는 새로 로드된 첫 번째 아이템으로 이동해야 한다. 카드 전체를 클릭 가능하게 만들어 상세 페이지로 보내는가? <a>에 stretched::after 패턴을 쓴다. 그리고 어떤 요소를 쓰든, outline: none만 지우고 끝내는 건 키보드 사용자를 화면에서 지워버리는 것과 같다. focus-visible 스타일은 선택이 아니라 의무다.
그렇다면 AI는 왜 이걸 틀리는가. 답은 dev.to의 또 다른 글에 있다. 엔지니어의 정체성 위기를 다룬 이 아티클은 AI 도구가 가져온 변화의 본질을 이렇게 정리한다. "AI는 타이핑을 자동화했고, 그 자리를 판단으로 채웠다." 빈 파일 문제, 보일러플레이트, 첫 번째 러프 패스—이 모든 건 이제 AI가 빠르게 처리한다. 하지만 무엇을 만들지 아는 것, 왜 깨지는지 아는 것, AI가 자신 있게 제안한 세 가지 중 어느 게 새벽 2시에 데이터를 오염시킬지 아는 것—그게 엔지니어의 진짜 역할이었고, 앞으로도 그렇다.
시맨틱 HTML은 바로 그 '판단'의 영역이다. AI는 훈련 데이터 안에서 가장 흔한 패턴을 따른다. 그리고 웹에는 href="#"에 클릭 핸들러를 붙인 링크가, onclick으로 페이지를 이동시키는 버튼이 넘쳐난다. AI는 그 패턴을 학습했고, 통계적으로 그럴듯한 코드를 출력한다. 그게 접근성을 망가뜨리는 코드라도. 이를 걸러내는 건 모델이 아니라 리뷰하는 사람이다. AI가 생성한 모든 코드를 주니어 PR 보듯 읽어야 한다는 조언이 여기서도 정확히 맞아떨어진다.
역설적이게도, AI 도구가 더 많이 쓰일수록 시맨틱과 접근성에 대한 판단력은 더 희소한 역량이 된다. 코드 생성이 범용재가 되면, 남는 건 방향을 잡는 힘이다. 어떤 요소를 써야 하는지, 왜 그 선택이 사용자 경험을 망치는지, 스크린 리더 사용자의 여정이 어떻게 흘러가는지를 아는 것—이건 프롬프트로 위임할 수 없는 영역이다. AI 코딩 도구를 매일 쓰면서도 손으로 디버깅하고, 기초 근육을 유지하라는 조언이 단순한 보수주의가 아닌 이유가 여기에 있다.
앞으로 AI 에이전트가 더 많은 UI 코드를 만들어낼수록, 프론트엔드 개발자의 역할은 두 방향으로 좁혀질 것이다. 하나는 AI 출력의 빠른 소비자가 되는 것, 다른 하나는 그 출력이 사용자에게 안전하고 올바른지를 판단하는 사람이 되는 것. 링크와 버튼을 구분하는 능력은 사소해 보이지만, 그 판단이 쌓이는 곳에 신뢰할 수 있는 인터페이스가 만들어진다. AI가 짜는 코드의 양이 늘어날수록, 그 코드를 제대로 읽는 사람의 가치는 함께 올라간다.