웹 접근성: 시맨틱 HTML과 ARIA로 완성하기

서예림·2025년 11월 29일

접근성?

들어가기 전에..! 요즘 프론트엔드 채용 공고를 살펴보면 "접근성"이라는 키워드를 많이 볼 수 있습니다.
하지만 제가 접근성을 생각하면서 개발한 것들을 떠올려보면 meta 태그 입력하기, 시멘틱 구조 잡기, aria-label, alt 정도였던 것 같습니다.
최근 웹앱 개발을 하면서 디자인 시스템 문서를 보다 보니 aria-* 속성이 많은 컴포넌트에 적용되어 있었고, 스크린리더와 관련된다는 것 정도는 알고 있었지만, 솔직히 실무 우선순위에서는 늘 뒤로 밀려왔던 것이 사실입니다.

웹 접근성이란?

웹 접근성(Web Accessibility)이란, 사람들의 능력이 어느 정도 제한되어 있더라도 가능한 사람이 동일한 방식으로 웹을 사용할 수 있도록 보장하는 것입니다.

  • 접근성을 지켜야 하는 이유
    해당 글을 살펴보면 접근성은 단지 특정 사용자를 위한 배려를 넘어 개발자에게도 더 견고하고 유지보수하기 쉬운 코드를 만들도록 도와주는 요소라는 점을 알 수 있습니다.

시맨틱 태그

웹 접근성의 기초는 결국 "시맨틱 HTML"입니다.
시맨틱 HTML은 태그 자체에 의미가 담겨 있는 마크업을 뜻하며 단순히 <div><span>만 쓰는 게 아니라 콘텐츠의 역할과 의미에 맞는 태그를 사용하는 것을 말합니다.

비슷하지만 다른 태그들

1. <section> vs <article> vs <div>

세 개의 태그들은 콘텐츠 영역을 구분하는데 사용됩니다.

  • div: 의미없는 컨테이너, 콘텐츠에 의미를 부여하지 않습니다. 단순히 스타일링이나 레이아웃을 위한 그룹화에 사용되며 다른 시맨틱 태그를 쓸 수 없을 때 사용합니다.
  • section: 주제별 콘텐츠 그룹입니다. 웹 페이지 내 주요 주제의 일부 영역이고 단독으로는 의미가 불완전할 수 있습니다.
  • article: 독립적인 콘텐츠입니다. 완전히 독립적으로 배포 가능한 콘텐츠 입니다. 다른 페이지에 복사해도 의미가 온전히 유지됩니다.

article 안에 section을 넣을 수 있고, section 안에 article을 넣을 수도 있습니다.

<!-- div -->
<div class="container">
  <div class="wrapper">
    <!-- 단순히 레이아웃을 위한 박스 -->
  </div>
</div>

<!-- section -->
<section>
  <h2>최신 뉴스</h2>
  <p>오늘의 뉴스 내용...</p>
  <p>이 섹션은 전체 페이지의 한 부분입니다.</p>
</section>

<!-- article -->
<article>
  <h2>React 19의 새로운 기능</h2>
  <p>작성자: 홍길동</p>
  <p>React 19에서는...</p>
</article>

2. <button> vs <a>

클릭 가능한 요소지만 역할과 동작이 다릅니다.

  • button: 액션을 수행할 때 사용합니다. Space와 Enter 키로 활성화됩니다.
  • a: 다른 페이지나 위치로 이동할 때 사용합니다. Enter 키로 활성화됩니다.
<!-- button -->
<button onClick={handleDelete}>삭제</button>

<!-- a -->
<a href="/products">상품 보기</a>

스크린 리더가 "버튼입니다" vs "링크입니다"를 다르게 안내합니다.
사용자가 예상하는 동작이 다릅니다. (링크는 우클릭 시 새탭 열기가 가능합니다.)

3. <b> vs <strong>

둘 다 화면에서 굵은 글씨로 나타나지만, 다른 의미를 가지고 있습니다.

  • b: 시각적 스타일만 나타냅니다. 단순하게 굵게 보이게 합니다.
  • strong: 중요도를 나타냅니다. 스크린 리더가 강조해서 읽습니다.
<!-- b: 시각적 스타일만 -->
<p>이 텍스트는 <b>굵게</b> 표시됩니다.</p>

<!-- strong: 의미적 강조 -->
<p>
  <strong>주의:</strong> 이 제품은 <em>반드시</em> 
  냉장 보관해야 합니다.
</p>

4. <i> vs <em>

  • i: 시각적 스타일만 나타냅니다. 단순하게 기울임체로 보이게 합니다.
  • em: 강세를 나타냅니다. 스크린리더가 억양을 달리해서 읽습니다.
<!-- i: 시각적 스타일만 -->
<p>이 텍스트는 <i>기울임</i> 표시됩니다.</p>

<!-- em: 의미적 강조 -->
<p>이것은 <em>강조하고 싶은</em> 부분입니다.</p>

5. <img> vs <figure> +<figcaption>

이미지를 표현하는 방법이지만 설명을 제공하는 방식이 다릅니다.

  • img: 단순 이미지입니다. alt 속성으로 대체 텍스트를 제공합니다.
  • figure + figcaption: 이미지와 캡션을 그룹화합니다. 스크린리더가 이미지와 설명을 연결해서 읽어줍니다.
<!-- img: 단순 이미지 -->
<img src="chart.png" alt="2024년 월별 매출 그래프" />
<!-- 스크린 리더: "2024년 월별 매출 그래프, 이미지" -->

<!-- figure: 이미지 + 캡션 -->
<figure>
  <img src="chart.png" alt="월별 매출 그래프" />
  <figcaption>2024년 매출은 전년 대비 20% 증가했습니다.</figcaption>
</figure>

스크린 리더와 ARIA 속성

ARIA는 무엇인가?

ARIA(Accessible Rich Internet Applications)는 HTML만으로는 표현하기 어려운 UI 상태나 구조를 보조기술(스크린리더)에게 전달하기 위한 속성 집합입니다.

원칙: "No ARIA is better than Bad ARIA"
잘못된 ARIA는 접근성을 오히려 해칠 수 있습니다.
HTML이 이미 제공하는 기본 의미(네이티브 시맨틱)가 항상 우선입니다.

자주 쓰는 ARIA 속성

1. 이름(Name)을 제공하는 속성

  • aria-label: 요소에 접근 가능한 이름 제공 (텍스트가 없거나 시각적으로 숨겨야 할 때)
  • aria-labelledby: 다른 요소의 텍스트를 이름으로 연결
  • aria-describedby: 상세 설명 제공, 주로 입력 폼 안내 페이지

2. 상태(State)를 나타내는 속성

  • aria-expanded: 아코디언, 드롭다운 메뉴가 열림/닫힘 상태인지 표시
  • aria-checked: 체크박스, 토글 스위치 상태 표시
  • aria-selected: 탭, 리스트박스 등 선택 상태 표시

3. 구조/역할(Role)을 부여하는 속성

  • role="dialog": 모달 대화 상자 의미
  • role="alert": 즉시 알려야 하는 경고 메세지
  • role="tablist", role="tab", role="tabpanel": 탭 UI 구조 정의

4. 관계(Relationship)를 나타내는 속성

  • aria-controls: 이 요소가 제어하는 요소가 무엇인지 연결
  • aria-owns: DOM 구조가 다르지만 논리적으로 자식 관계를 연결
  • aria-labelledby: 요소 제목(title) 연결

5. 실시간 알림(Live Region) 속성

  • aria-live="polite": 새 메세지를 조용히 알려줌
  • aria-live="assertive": 즉시 끼어들어 알려줌 (주의 필요)
  • aria-atomic: 전체 메세지를 다시 읽을지 여부
  • aria-relevant: 어떤 변경을 감지할지

ARIA를 적용할 때 알아야 할 원칙

1. 상호작용 요소는 반드시 시맨틱 요소로 만들어야 한다.

사용자가 클릭, 탭, 키보드 조작을 해야 하는 UI라면 button 또는 a 같은 시맨틱 요소를 반드시 사용해야 합니다.

❌ 나쁜 예 - div를 버튼처럼 사용
<div role="button" tabindex="0" onclick="submitForm()">
  제출
</div>

✅ 좋은 예 - 네이티브 버튼 사용
<button type="submit">제출</button>

왜 이렇게 해야 할까?

  • 키보드 조작(Enter/Space)이 자동 제공된다.
  • focus, disabled, active 상태를 직접 구현할 필요가 없다.
  • 스크린리더가 정확히 역할을 파악한다.

2. 시맨틱 요소에 불필요한 ARIA 역할을 중복 적용하지 않아야 한다.

HTML5 요소가 이미 역할(role)을 내장하고 있다면 굳이 ARIA를 덧붙이지 않습니다.

❌ 나쁜 예 - 의미 중복
<nav role="navigation">...</nav>

✅ 좋은 예 - 기본 의미 그대로
<nav>...</nav>

사용해야 하는 이유

  • 스크린리더는 <nav>를 이미 navigation landmark로 인식한다.
  • 중복 ARIA는 오히려 혼란을 줄 수 있다.

3. 아이콘만 있는 버튼에는 반드시 '의미 있는 이름'을 제공해야 한다.

텍스트 없는 버튼은 스크린리더에게 "버튼"으로만 읽힙니다.
목적(닫기/삭제/열기)을 명확히 알려야 합니다.

❌ 나쁜 예 - 이름 없는 버튼
<button>
  <svg>...</svg>
</button>

✅ 좋은 예 - aria-label 추가
<button aria-label="닫기">
  <svg aria-hidden="true">...</svg>
</button>

✅ 또는 - sr-only 텍스트 사용
<button>
  <span class="sr-only">닫기</span>
  <svg aria-hidden="true">...</svg>
</button>

4. 드롭다운/아코디언 등 상태가 변하는 요소는 '상태'와 '관계'를 ARIA로 명확히 표현해야 한다.

UI가 열렸는지/닫혔는지 상태를 aria-expanded로 나타내고, 어떤 요소를 제어하는지 aria-controls로 연결해야 한다.

❌ 나쁜 예 - 상태 정보 없음
<div class="accordion-header" onclick="toggle()">
  FAQ 제목
</div>
<div class="accordion-content">내용…</div>

✅ 좋은 예 - 상태 + 관계 표현
<button aria-expanded="false" aria-controls="answer1" id="question1">
  FAQ 제목
</button>

<div id="answer1" role="region" aria-labelledby="question1" hidden>
  내용...
</div>

5. 의미 없는 장식 요소는 보조기술에서 숨겨야 한다.

UX를 위해 디자인적 요소가 들어가더라도 스크린리더가 굳이 읽을 필요가 없는 콘텐츠는 alt="" 또는 aria-hidden="true"를 사용해 숨겨야 한다.

❌ 나쁜 예 - 불필요한 alt
<img src="pattern.png" alt="배경 패턴 이미지" />

✅ 좋은 예 - 빈 alt로 숨기기
<img src="pattern.png" alt="" aria-hidden="true" />

6. 동적으로 변하는 메세지는 Live Region을 사용해 알려야 한다.

UI가 자동으로 갱신되면 스크린리더에게 변화가 전달되지 않을 수 있으므로 aria-live로 메세지 영역을 지정해야 한다.

❌ 나쁜 예 - 변화는 있지만 알림 없음
<div id="status">저장되었습니다</div>

✅ 좋은 예 - live region 적용
<div aria-live="polite" id="status"></div>
// JS로 메세지 업데이트
document.getElementById("status").textContent = "저장되었습니다";

마무리

이번에 접근성을 공부하면서 느낀점은 접근성이 특별하거나 거창한 것이 아니라는 것입니다.

  • 시맨틱 HTML 사용하기
  • 상호작용 요소 제대로 쓰기
  • 이름(name)과 상태(state) 제대로 전달하기
  • 필요한 경우에만 ARIA로 보완하기

접근성은 특정 사용자만을 위한 기능이 아니라 모든 사용자를 고려하는 더 나은 개발 습관이며 장기적으로는 개발자에게도 유지보수성과 확장성을 가져다 주는 선택이라고 느꼈습니다!

참고 자료

4개의 댓글

comment-user-thumbnail
2025년 12월 3일

덕분에 요즘 핫한 시맨틱 태그에 대해 다시 한 번 상기시키는 내용이였습니다~! 스크린 리더 부분은 잘 몰랐는데 항상 좋은 내용 작성해주셔서 도움이 많이됩니다. 감사합니다.

답글 달기
comment-user-thumbnail
2025년 12월 4일

전 회사에서 요 부분으로 약간 애먹은 기억이 있는데 정리된 걸 보니 좋네용!! 롤에맞는 상태를 적절하게 부여하는 것을 잘 봐야하겠네용 잘 보규갑니당!!

답글 달기
comment-user-thumbnail
2025년 12월 5일

음성 안내도 정말 중요한 부분인데 급한 개발이라 and 회사가 크게 고려하지 않는 부분이라 넘겨버린 적이 많았던 내용이네요 ㅠ 유용하고 좋았습니다!

답글 달기
comment-user-thumbnail
2025년 12월 5일

단순 강조와 의미적 강조에 차이가 있다는 걸 알게 되었어요! ARIA에 대해 처음 알아보기 좋은 글인 것 같습니다, 잘 보구 갑니다 ㅎㅎ

답글 달기