[μ›Ή ν‘œμ€€ μ‹œλ¦¬μ¦ˆ #4] "μ›€μ§μ΄λŠ” 웹도 ν‘œμ€€μ΄ μžˆλ‚˜μš”?" : Vanilla JS와 WAI-ARIA μ‹€μ „νŽΈ

2025. 12. 29. 10:18Β·After. 2025

μš°λ¦¬λŠ” 2뢀와 3λΆ€λ₯Ό 톡해 νƒ„νƒ„ν•œ HTML ꡬ쑰가 μ–Όλ§ˆλ‚˜ μ€‘μš”ν•œμ§€ λ°°μ› μŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ ν˜„λŒ€μ˜ 웹은 ν΄λ¦­ν•˜κ³ , νŽΌμ³μ§€κ³ , νŒμ—…μ΄ λœ¨λŠ” λ“± 쉴 μƒˆ 없이 μ›€μ§μž…λ‹ˆλ‹€.

μ—¬κΈ°μ„œ λ¬Έμ œκ°€ λ°œμƒν•©λ‹ˆλ‹€. λˆˆμ— λ³΄μ΄λŠ” λ””μžμΈμ€ λ°”λ€Œμ§€λ§Œ, 보쑰 곡학 κΈ°κΈ°(슀크린 리더 λ“±)λŠ” 이 λ³€ν™”λ₯Ό λˆˆμΉ˜μ±„μ§€ λͺ»ν•  λ•Œκ°€ 많기 λ•Œλ¬Έμž…λ‹ˆλ‹€. μ˜€λŠ˜μ€ μžλ°”μŠ€ν¬λ¦½νŠΈλ‘œ 동적인 κΈ°λŠ₯을 κ΅¬ν˜„ν•  λ•Œ μ›Ή ν‘œμ€€μ„ μ§€ν‚€λŠ” ν•„μ‚΄κΈ°, WAI-ARIAλ₯Ό μ•Œμ•„λ³΄κ² μŠ΅λ‹ˆλ‹€.


 1. WAI-ARIA, μ™œ ν•„μš”ν•œκ°€μš”?

μžλ°”μŠ€ν¬λ¦½νŠΈλ‘œ νŠΉμ • μš”μ†Œλ₯Ό display: none;μ—μ„œ block;으둜 λ°”κΏ¨λ‹€κ³  κ°€μ •ν•΄ λ΄…μ‹œλ‹€. λˆˆμœΌλ‘œλŠ” λ³΄μ΄μ§€λ§Œ, 슀크린 λ¦¬λ”λŠ” 이 메뉴가 μ—΄λ ΈλŠ”μ§€, λ‹«ν˜”λŠ”μ§€, ν˜Ήμ€ μ§€κΈˆ λ‚΄κ°€ λˆ„λ₯΄λŠ” 것이 νƒ­ λ²„νŠΌμΈμ§€ 일반 λ²„νŠΌμΈμ§€ μ•Œ 수 μ—†μŠ΅λ‹ˆλ‹€.

WAI-ARIA(Web Accessibility Initiative - Accessible Rich Internet Applications)λŠ” λ°”λ‘œ 이럴 λ•Œ μ‚¬μš©ν•©λ‹ˆλ‹€. HTML νƒœκ·Έλ§ŒμœΌλ‘œλŠ” μ„€λͺ…ν•˜κΈ° νž˜λ“  'μ—­ν• (Role)', 'μƒνƒœ(State)', '속성(Property)'을 μΆ”κ°€λ‘œ λͺ…μ‹œν•΄μ£ΌλŠ” μΌμ’…μ˜ '보좩 μ„€λͺ…μ„œ'μž…λ‹ˆλ‹€.


 2. μ‹€μ „! μ ‘κ·Ό κ°€λŠ₯ν•œ νƒ­(Tab) 메뉴 λ§Œλ“€κΈ°

κ°€μž₯ ν”ν•˜κ²Œ μ“°μ΄λŠ” νƒ­ 메뉴λ₯Ό BEM 방법둠과 Vanilla JS, 그리고 WAI-ARIAλ₯Ό κ²°ν•©ν•΄ κ΅¬ν˜„ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€.

πŸ“„ HTML ꡬ쑰 (BEM + WAI-ARIA)

<div class="c-tabs" data-component="tabs">
  <div class="c-tabs__list" role="tablist">
    <button 
      type="button" 
      class="c-tabs__item c-tabs__item--active" 
      role="tab" 
      aria-selected="true" 
      aria-controls="panel-1" 
      id="tab-1"
    >
      곡지사항
    </button>
    <button 
      type="button" 
      class="c-tabs__item" 
      role="tab" 
      aria-selected="false" 
      aria-controls="panel-2" 
      id="tab-2" 
      tabindex="-1"
    >
      이벀트
    </button>
  </div>

  <div 
    class="c-tabs__panel c-tabs__panel--active" 
    id="panel-1" 
    role="tabpanel" 
    aria-labelledby="tab-1"
  >
    <p>μ΅œμ‹  곡지사항 λ‚΄μš©μž…λ‹ˆλ‹€.</p>
  </div>
  <div 
    class="c-tabs__panel" 
    id="panel-2" 
    role="tabpanel" 
    aria-labelledby="tab-2" 
    hidden
  >
    <p>μ§„ν–‰ 쀑인 μ΄λ²€νŠΈκ°€ μ—†μŠ΅λ‹ˆλ‹€.</p>
  </div>
</div>

βš™οΈ Vanilla JS 둜직 (Airbnb Style Guide μ€€μˆ˜)

const initTabs = () => {
  const tabContainers = document.querySelectorAll('[data-component="tabs"]');

  tabContainers.forEach((container) => {
    const tabs = container.querySelectorAll('[role="tab"]');
    const panels = container.querySelectorAll('[role="tabpanel"]');

    const switchTab = (event) => {
      const targetTab = event.currentTarget;
      const targetPanelId = targetTab.getAttribute('aria-controls');

      tabs.forEach((tab) => {
        tab.classList.remove('c-tabs__item--active');
        tab.setAttribute('aria-selected', 'false');
        tab.setAttribute('tabindex', '-1');
      });

      panels.forEach((panel) => {
        panel.classList.remove('c-tabs__panel--active');
        panel.setAttribute('hidden', '');
      });

      targetTab.classList.add('c-tabs__item--active');
      targetTab.setAttribute('aria-selected', 'true');
      targetTab.setAttribute('tabindex', '0');

      const targetPanel = container.querySelector(`#${targetPanelId}`);
      if (targetPanel) {
        targetPanel.classList.add('c-tabs__panel--active');
        targetPanel.removeAttribute('hidden');
      }
    };

    tabs.forEach((tab) => {
      tab.addEventListener('click', switchTab);
    });
  });
};

document.addEventListener('DOMContentLoaded', initTabs);

 μ½”λ“œ λ°–μ—μ„œ λ“œλ¦¬λŠ” 상세 μ„€λͺ…

μœ„ μ½”λ“œλŠ” μ‚¬μš©μžκ°€ 탭을 ν΄λ¦­ν–ˆμ„ λ•Œ μ‹œκ°μ μΈ λ³€ν™”λΏλ§Œ μ•„λ‹ˆλΌ 보쑰 곡학 기기에도 μƒνƒœ λ³€ν™”λ₯Ό μ™„λ²½ν•˜κ²Œ μ „λ‹¬ν•˜λ„λ‘ μ„€κ³„λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

  1. Role λͺ…μ‹œ: role="tablist", role="tab", role="tabpanel"을 톡해 이 ꡬ쑰가 νƒ­ μ‹œμŠ€ν…œμž„μ„ λΈŒλΌμš°μ €μ— λͺ…ν™•νžˆ μ•Œλ¦½λ‹ˆλ‹€.
  2. Aria 속성 μ œμ–΄: μžλ°”μŠ€ν¬λ¦½νŠΈλ‘œ λ‹¨μˆœνžˆ 클래슀만 λ°”κΎΈλŠ” 것이 μ•„λ‹ˆλΌ aria-selected 값을 true/false둜 ν† κΈ€ν•©λ‹ˆλ‹€. 슀크린 λ¦¬λ”λŠ” 이 값을 읽어 μ‚¬μš©μžμ—κ²Œ "선택됨" μ—¬λΆ€λ₯Ό μ•Œλ €μ€λ‹ˆλ‹€.
  3. 포컀슀 관리: μ„ νƒλ˜μ§€ μ•Šμ€ νƒ­μ—λŠ” tabindex="-1"을 μ£Όμ–΄ λΆˆν•„μš”ν•œ ν‚€λ³΄λ“œ 접근을 막고, ν™œμ„±ν™”λœ νƒ­μ—λ§Œ ν¬μ»€μŠ€κ°€ 가도둝 κ΄€λ¦¬ν–ˆμŠ΅λ‹ˆλ‹€.
  4. Hidden 속성 ν™œμš©: display: none λŒ€μ‹  HTML5 ν‘œμ€€μΈ hidden 속성을 ν† κΈ€ν•˜μ—¬ μ‹œκ°μ μœΌλ‘œλ‚˜ κ΅¬μ‘°μ μœΌλ‘œλ‚˜ νŒ¨λ„μ„ μˆ¨κΉλ‹ˆλ‹€.
  5. Vanilla JS & Airbnb Style: μ™ΈλΆ€ 라이브러리 μ˜μ‘΄μ„±μ„ μ œκ±°ν•˜κ³  ν™”μ‚΄ν‘œ ν•¨μˆ˜, const, forEach λ“± ν˜„λŒ€μ μΈ μžλ°”μŠ€ν¬λ¦½νŠΈ 문법을 μ‚¬μš©ν•˜μ—¬ κΉ”λ”ν•˜κ³  λΉ λ₯Έ λ‘œμ§μ„ κ΅¬μ„±ν–ˆμŠ΅λ‹ˆλ‹€.

 λ§ˆμΉ˜λ©°: κΈ°μˆ λ³΄λ‹€ μ€‘μš”ν•œ 것은 'μ—°κ²°'μž…λ‹ˆλ‹€

μžλ°”μŠ€ν¬λ¦½νŠΈλŠ” 웹을 즐겁게 λ§Œλ“€μ§€λ§Œ, 잘λͺ» μ“°λ©΄ λˆ„κ΅°κ°€μ—κ²ŒλŠ” κ±°λŒ€ν•œ μž₯벽이 λ©λ‹ˆλ‹€. WAI-ARIAλŠ” κ·Έ μž₯벽을 ν—ˆλ¬Όκ³  기술과 μ‚¬μš©μžλ₯Ό μ—°κ²°ν•΄μ£ΌλŠ” λ”°λœ»ν•œ κ°€μ΄λ“œλΌμΈμž…λ‹ˆλ‹€.

ν™”λ €ν•œ μ• λ‹ˆλ©”μ΄μ…˜μ„ κ³ λ―Όν•˜κΈ° 전에, "λ‚΄ μ½”λ“œκ°€ 보이지 μ•ŠλŠ” κ³³μ—μ„œλ„ μΉœμ ˆν•œκ°€?"λ₯Ό λ¨Όμ € κ³ λ―Όν•˜λŠ” κ°œλ°œμžκ°€ μ§„μ§œ μ‹€λ ₯ μžˆλŠ” 개발자 μ•„λ‹κΉŒμš”?


 4λΆ€ 핡심 μš”μ•½

  • 동적인 UI λ³€ν™”λŠ” λ°˜λ“œμ‹œ aria-selected, aria-expanded λ“±μœΌλ‘œ μƒνƒœλ₯Ό μ•Œλ €μ•Ό ν•œλ‹€.
  • role 속성을 톡해 νƒœκ·Έμ˜ κΈ°λ³Έ 의미λ₯Ό λ„˜μ–΄μ„  μ‹€μ œ 역할을 λΆ€μ—¬ν•œλ‹€.
  • 바닐라 μžλ°”μŠ€ν¬λ¦½νŠΈλ‘œ ν΄λž˜μŠ€μ™€ ARIA 속성을 λ™μ‹œμ— μ œμ–΄ν•˜λŠ” 것이 ν‘œμ€€ μ½”λ”©μ˜ 핡심이닀.
μ €μž‘μžν‘œμ‹œ λΉ„μ˜λ¦¬ 동일쑰건 (μƒˆμ°½μ—΄λ¦Ό)

'After. 2025' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€

2026λ…„ ν•„μˆ˜ 뢁마크: ꡬ글이 쑰용히 μΆœμ‹œν•œ 무료 AI μ„œλΉ„μŠ€ 리슀트  (0) 2026.03.11
[μ›Ή ν‘œμ€€ μ‹œλ¦¬μ¦ˆ #5] AI μ—μ΄μ „νŠΈμ˜ μ‹œλŒ€: μ›Ή ν‘œμ€€μ€ 이제 AIλ₯Ό μœ„ν•œ 'API'λ‹€  (1) 2025.12.30
[μ›Ή ν‘œμ€€ μ‹œλ¦¬μ¦ˆ #3] ꡬ글이 μ‚¬λž‘ν•˜λŠ” μ›Ήμ‚¬μ΄νŠΈμ˜ λΉ„λ°€: μ½”λ“œ 속에 μˆ¨κ²¨μ§„ SEO μ „λž΅  (0) 2025.12.28
[μ›Ή ν‘œμ€€ μ‹œλ¦¬μ¦ˆ #2] λˆˆμ— 보이지 μ•ŠλŠ” ꡬ쑰의 힘: BEMκ³Ό μ‹œλ§¨ν‹± λ§ˆν¬μ—…μ˜ μ‹œλ„ˆμ§€  (0) 2025.12.27
[μ›Ή ν‘œμ€€ μ‹œλ¦¬μ¦ˆ #1] 당신도 였늘 'μž₯μ• 'λ₯Ό κ²ͺμ—ˆμŠ΅λ‹ˆλ‹€: 접근성이 보편적 ꢌ리인 이유  (0) 2025.12.26
'After. 2025' μΉ΄ν…Œκ³ λ¦¬μ˜ λ‹€λ₯Έ κΈ€
  • 2026λ…„ ν•„μˆ˜ 뢁마크: ꡬ글이 쑰용히 μΆœμ‹œν•œ 무료 AI μ„œλΉ„μŠ€ 리슀트
  • [μ›Ή ν‘œμ€€ μ‹œλ¦¬μ¦ˆ #5] AI μ—μ΄μ „νŠΈμ˜ μ‹œλŒ€: μ›Ή ν‘œμ€€μ€ 이제 AIλ₯Ό μœ„ν•œ 'API'λ‹€
  • [μ›Ή ν‘œμ€€ μ‹œλ¦¬μ¦ˆ #3] ꡬ글이 μ‚¬λž‘ν•˜λŠ” μ›Ήμ‚¬μ΄νŠΈμ˜ λΉ„λ°€: μ½”λ“œ 속에 μˆ¨κ²¨μ§„ SEO μ „λž΅
  • [μ›Ή ν‘œμ€€ μ‹œλ¦¬μ¦ˆ #2] λˆˆμ— 보이지 μ•ŠλŠ” ꡬ쑰의 힘: BEMκ³Ό μ‹œλ§¨ν‹± λ§ˆν¬μ—…μ˜ μ‹œλ„ˆμ§€
빙고ꡬ맛탕
빙고ꡬ맛탕
  • 빙고ꡬ맛탕
    π•Žπ”Όπ”Ή 𝔸𝕃𝕃 𝕃𝕆𝔾
    빙고ꡬ맛탕
    • 전체보기
      • w. Claude
      • w. GPTs
      • w. Grok
      • w. Gemini
      • After. 2025
      • Before. 2021
  • λΈ”λ‘œκ·Έ 메뉴

    • λ°©λͺ…둝
    • κΈ€μ“°κΈ°
    • κ΄€λ¦¬μž
  • 인기 κΈ€

  • νƒœκ·Έ

    border-collapse 였λ₯˜
    크둬 μˆ¨κ²¨μ§„ κΈ°λŠ₯
    크둬 읽기 λͺ¨λ“œ
    크둬 λΆ„ν•  보기
    크둬 ꡬ글 렌즈
    크둬 κΏ€νŒ
    box-shadow
    ie collapse 버그
    μ œλ―Έλ‚˜μ΄
    input fileνƒœκ·Έ λ°”κΎΈκΈ°
    크둬 μ œλ―Έλ‚˜μ΄
    νƒ­ μ „ν™˜ 가속
    ie9 μ„  버그
    footer κ³ μ •
    크둬
    min-height:100%
    footer ν•˜λ‹¨μ— κ³ μ •
    css3
  • hELLOΒ· Designed Byμ •μƒμš°.v4.10.6
빙고ꡬ맛탕
[μ›Ή ν‘œμ€€ μ‹œλ¦¬μ¦ˆ #4] "μ›€μ§μ΄λŠ” 웹도 ν‘œμ€€μ΄ μžˆλ‚˜μš”?" : Vanilla JS와 WAI-ARIA μ‹€μ „νŽΈ
μƒλ‹¨μœΌλ‘œ

ν‹°μŠ€ν† λ¦¬νˆ΄λ°”