접근성 작업, 그동안 어렵게만 생각하셨나요?🧐

많은 개발자들이 접근성(Accessibility)을 어렵고 복잡한 작업으로 여기며, 이를 위해 많은 추가 노력이나 전문 지식이 필요하다고 생각합니다.
하지만 사실 몇 가지 기본 원칙만 적용해도 사용자 경험(UX)을 크게 개선할 수 있습니다.
이 글에서는 프런트엔드 개발자가 컴포넌트를 개발할 때 반드시 고려해야 할 주요 접근성 원칙들을 자세히 다룹니다.
예제 코드와 그림 설명을 곁들여 실습에 바로 적용할 수 있도록 돕겠습니다.

🦾 접근성이 중요한 이유

접근성 개선은 단순히 보조 기술을 사용하는 사용자만을 위한 것이 아닙니다.
올바른 접근성 구현은 전체 사용자 경험(UX) 향상과 SEO 개선에도 긍정적인 영향을 미칩니다.

🎯 인터랙티브 요소의 올바른 사용

<div>에 onClick 핸들러를 사용하는 것은 피하고, <button>과 <a> 같은 네이티브 요소를 활용해야 합니다.

<select>, <input>, <textarea> 등 기본 HTML 요소는 접근성이 이미 고려된 상태로 제공됩니다.

ARIA 속성은 마지막 수단으로 신중하게 사용해야 하며, 잘못 적용하면 오히려 접근성을 해칠 수 있습니다.
aria-label과 aria-hidden 같은 속성은 올바르게 사용하면 큰 도움이 됩니다.

🚀 개발 과정에서 접근성을 고려해야 하는 이유

접근성은 나중에 해결하는 것이 아니라, 개발 초기부터 포함되어야 합니다.
기본적인 접근성 원칙을 지키는 것만으로도 사용자 경험과 웹사이트의 전반적인 품질을 향상시킬 수 있습니다.

1. 시맨틱 HTML: 접근성의 시작점

접근성은 시맨틱 HTML에서 시작됩니다.
HTML5 요소를 의도된 용도에 맞게 사용하면 브라우저와 보조 기술(예: 스크린 리더)이 페이지 구조를 쉽게 이해할 수 있습니다.
게다가 SEO(검색 엔진 최적화)에도 긍정적인 영향을 미칩니다.

인터랙티브 요소

<button>과 <a>는 가장 중요한 인터랙티브 요소입니다.
이들은 기본적으로 키보드 지원과 스크린 리더에 시맨틱한 의미를 제공합니다.

안티 패턴: <div>에 onClick 사용
웹 애플리케이션에서 흔히 보이는 실수는 <div>에 onClick 핸들러를 붙여 버튼처럼 사용하는 경우입니다.
이는 절대 피해야 합니다.
<div>는 접근성 기능이 없어 키보드 탐색이나 스크린 리더에서 제대로 작동하지 않습니다.

<!-- 잘못된 예 -->
<div onClick={handleClick}>클릭하세요</div>

<!-- 올바른 예 -->
<button onClick={handleClick}>클릭하세요</button>
  • <a>: 링크는 우클릭 컨텍스트 메뉴, 새 탭 열기(Ctrl+클릭 또는 중간 클릭) 등 다양한 기능을 제공합니다.
  • <button>: 키보드 탐색(예: Tab 및 Enter 키)을 지원하며, 보조 기술 사용자에게 필수적입니다.

스타일링 팁

  • <button>과 <a>는 CSS로 자유롭게 스타일링할 수 있습니다.
    접근성을 희생하지 않으면서 원하는 디자인을 구현하세요.
.custom-button {
  background-color: #007bff;
  color: white;
  padding: 10px 20px;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}

네이티브 요소 활용

<select>, <input>, <textarea> 같은 네이티브 요소는 기본 접근성을 갖추고 있습니다.
예를 들어, <select> 드롭다운은 키보드와 스크린 리더에서 추가 작업 없이도 잘 작동합니다.

커스텀 컴포넌트 대신 라이브러리 사용

커스텀 드롭다운을 만들고 싶을 때 접근성을 완벽히 구현하는 것은 어렵습니다.
이런 경우 react-select 같은 성숙한 라이브러리를 사용하는 것이 효율적입니다.

import Select from 'react-select';

const options = [
  { value: 'react', label: 'React' },
  { value: 'vue', label: 'Vue' },
];

function MyComponent() {
  return <Select options={options} />;
}

2. 폼: 모든 사용자를 위한 간단한 구조

폼은 사용자와의 상호작용에서 핵심적인 역할을 합니다.
접근성을 높이기 위해 몇 가지 간단한 규칙을 따르세요.

<form> 태그 사용

모든 폼 필드는 <form> 안에 포함되어야 하며, onSubmit 핸들러와 제출 버튼을 갖추는 것이 좋습니다.
이는 Enter 키로 제출하거나 모바일 화면 키보드에서 필드 간 이동을 가능하게 합니다.

<form onSubmit={handleSubmit}>
  <label htmlFor="name">이름:</label>
  <input type="text" id="name" name="name" />
  <button type="submit">제출</button>
</form>

라벨: 필수 요소

모든 입력 필드에는 <label>이 있어야 하며, htmlFor 속성으로 입력과 연결하세요.
암시적 연결(라벨 안에 입력을 감싸는 방식)은 일부 스크린 리더에서 제대로 지원되지 않을 수 있으니 주의하세요.

// 추천
<label htmlFor="email">이메일:</label>
<input type="email" id="email" />

// 비추천 (일부 스크린 리더에서 문제 발생 가능)
<label>이메일: <input type="email" /></label>

React에서 고유 ID 생성

React에서는 useId 훅을 사용해 ID 충돌을 방지할 수 있습니다.

import { useId } from 'react';

function FormField() {
  const id = useId();
  return (
    <>
      <label htmlFor={id}>이름:</label>
      <input type="text" id={id} />
    </>
  );
}

플레이스홀더는 보조 역할

플레이스홀더는 라벨을 대체할 수 없습니다.
입력이 시작되면 사라지고, 대비가 낮아 읽기 어려울 수 있습니다.

<!-- 잘못된 예 -->
<input type="text" placeholder="이름" />

<!-- 올바른 예 -->
<label for="name">이름:</label>
<input type="text" id="name" placeholder="홍길동" />

3. 키보드 내비게이션: 마우스 없이도 완벽하게

키보드 탐색은 접근성의 핵심입니다.
사용자가 Tab 키로 논리적 순서대로 이동하고, Enter 키로 동작을 실행할 수 있어야 합니다.

포커스 표시기

포커스 표시기를 절대 비활성화하지 마세요.
:focus-visible 선택자를 사용하면 마우스 사용자에게는 표시기를 숨기고, 키보드 사용자에게만 보이게 할 수 있습니다.

button:focus-visible {
  outline: 2px solid #007bff;
  outline-offset: 2px;
}

4. 모달: 접근성을 고려한 설계

모달은 흔히 사용되지만 접근성 문제가 많습니다.
<dialog> 요소를 활용하면 쉽게 해결할 수 있습니다.

<dialog> 사용

function Modal({ isOpen, onClose }) {
  const dialogRef = useRef(null);

  useEffect(() => {
    if (isOpen) dialogRef.current.showModal();
    else dialogRef.current.close();
  }, [isOpen]);

  return (
    <dialog ref={dialogRef}>
      <p>모달 내용</p>
      <button onClick={onClose}>닫기</button>
    </dialog>
  );
}

커스텀 모달

<dialog>를 사용하지 않는 경우

  • 포커스 관리: 모달 열릴 때 포커스를 모달로 이동, 닫히면 원래 요소로 반환.
  • 포커스 트랩: react-focus-lock 같은 라이브러리 추천.
  • Escape 키: 모달 닫기 지원.
import FocusLock from 'react-focus-lock';

function CustomModal({ isOpen, onClose }) {
  if (!isOpen) return null;
  return (
    <FocusLock>
      <div role="dialog" aria-label="커스텀 모달">
        <p>모달 내용</p>
        <button onClick={onClose}>닫기</button>
      </div>
    </FocusLock>
  );
}

5. 이미지 대체 텍스트: 모두를 위한 설명

>img< 태그에는 반드시 alt 속성을 추가하세요.
장식용 이미지는 alt=""로 설정합니다.

<img src="cat.jpg" alt="검은 고양이가 나무 위에 앉아 있음" />
<img src="deco-border.png" alt="" />

좋은 대체 텍스트 작성법

  • 텍스트가 포함된 이미지는 반드시 해당 텍스트를 포함.
  • 주변 문맥과 중복되지 않는 설명 제공.

6. 스타일링: 접근성을 높이는 디자인

클릭 가능한 영역

버튼과 링크는 충분한 크기와 패딩을 가져야 합니다.

.button {
  padding: 12px 24px;
}

애니메이션 최소화

prefers-reduced-motion을 존중하세요.

@media (prefers-reduced-motion: reduce) {
  .slide-in {
    animation: none;
  }
}

반응형 디자인

em과 rem 단위를 사용해 사용자 설정을 반영하세요.

.container {
  font-size: 1rem;
  padding: 1em;
  max-width: 50em;
}

7. ARIA 속성: 최후의 수단

ARIA는 시맨틱 HTML로 해결되지 않을 때 사용하세요.

<button aria-label="검색">
  <SearchIcon />
</button>

<div>React <ReactLogo aria-hidden="true" /></div>

결론

접근성은 개발 과정의 일부로 처음부터 고려해야 합니다.
위 원칙들은 보조 기술 사용자뿐 아니라 모든 사용자의 경험을 개선하며, SEO에도 긍정적입니다.

profile
꾸준히, 의미있는 사이드 프로젝트 경험과 문제해결 과정을 기록하기 위한 공간입니다.

0개의 댓글