카카오모빌리티 디벨로퍼스 문서 기술 블로그 모두의 이동을 위한 카카오 T - 기차/시외버스 접근성 개선기

들어가며 링크 복사

안녕하세요. 저는 카카오모빌리티 웹FE개발팀에서 프런트엔드 개발을 담당하는 로렌입니다. 웹FE개발팀은 주로 카카오 T와 카카오내비 서비스의 웹 기반 서비스를 개발하고 있습니다. 우리 팀의 핵심 목표 중 하나는 나이, 성별, 장애 여부와 관계없이 모든 사용자가 카카오 T와 카카오내비를 쉽고 편리하게 이용할 수 있도록 '접근성' 높은 서비스를 제공하는 것입니다.

최근 카카오 T에서는 장애인들이 기차와 시외버스를 불편 없이 이용할 수 있도록 접근성을 강화하는 프로젝트를 진행했습니다. 이 글에서는 장애인 사용자들이 카카오 T를 통해 기차와 시외버스를 예매하는 과정에서 접근성을 높이기 위해 우리 팀이 어떤 노력과 고민을 했는지, 그리고 어떻게 서비스가 개선되었는지에 대해 이야기하고자 합니다.

접근성이란 무엇일까요? 링크 복사

2008년에 시행된 장애인 차별 금지법이 2013년에 의무화되면서 접근성의 중요성이 부각되었습니다. 이 법은 장애인과 비장애인이 웹 플랫폼상의 정보에 동등하게 접근하고 이용할 수 있도록 편의를 제공해야 한다는 내용을 담고 있습니다.

웹 접근성에 관해서는 국제 표준인 웹 콘텐츠 접근성 지침(WCAG)과 국내 웹 환경을 고려하여 만든 한국형 웹 콘텐츠 접근성 지침(KWCAG)이 있습니다. 이 지침들은 크게 4가지 원칙으로 구성되어 있습니다.


  • 인식의 용이성: 모든 콘텐츠는 사용자가 인식할 수 있어야 합니다.
  • 운용의 용이성: 사용자 인터페이스 구성요소는 조작 가능하고 내비게이션 할 수 있어야 합니다.
  • 이해의 용이성: 콘텐츠는 이해할 수 있어야 합니다.
  • 견고성: 웹 콘텐츠는 미래의 기술로도 접근할 수 있도록 견고하게 만들어야 합니다.

이러한 접근성 원칙은 웹뿐만 아니라 모바일 앱에도 적용됩니다. 따라서 기획, 디자인, 개발 등 모든 직군이 이 지침을 기준으로 접근성을 고려하여 서비스를 개발해야 합니다.

그래서 접근성은 잘 지켜지고 있을까요? 링크 복사

기차와 시외버스 같은 필수적인 교통수단은 누구나 손쉽게 모바일 앱을 통해 예매할 수 있어야 합니다. 하지만 지난해 한국디지털 접근성 진흥원의 조사 결과, 장애인들이 이러한 교통수단 앱을 이용하는 데 여전히 어려움을 겪고 있는 것으로 나타났습니다.

이에 따라 우리는 장애인 사용자들이 카카오 T를 통해 기차와 시외버스를 불편 없이 예매하고 취소할 수 있도록 개선 작업을 진행했습니다. 이제 제가 담당한 기차/시외버스 서비스의 개선 사례를 위에서 소개한 접근성 지침을 기반으로 공유해 드리고자 합니다.

이 글에서는 기차 서비스를 예시로 들어 설명합니다. 시외버스에도 동일한 로직이 적용되었습니다.

기차/시외버스 접근성 대응 사례 링크 복사

인식의 용이성 - 적절한 대체 텍스트 제공 링크 복사

시각 장애인들은 스마트폰 앱을 어떻게 사용할까요? 아이폰의 경우 보이스오버(VoiceOver), 안드로이드의 경우 톡백(TalkBack) 기능이 기본으로 탑재되어 화면의 내용을 모두 음성으로 읽어줍니다. 이러한 기술 덕분에 시각장애인도 터치스크린 스마트폰을 활용할 수 있게 되었습니다. 이를 효과적으로 지원하기 위해서는 적절한 대체 텍스트를 제공하는 것이 중요합니다.

1. 경로 조회 화면 개선 작업 링크 복사

기차/시외버스의 출발지와 도착지를 입력한 다음 나오는 화면을 경로 조회 화면이라고 합니다. 경로 조회 화면은 크게 1) 경로 입력 부분 2) 경로 검색 결과 부분으로 나뉩니다. 우선 경로 입력 부분을 살펴보겠습니다.

경로 입력 부분 링크 복사

아래는 출발지와 도착지를 입력하는 화면입니다. 크게 출발지와 도착지 입력 칸과 두 지점을 전환하는 버튼으로 구성되어 있습니다.


디자인 요소들을 통해 대부분의 사용자는 출발지와 도착지 정보, 그리고 버튼 아이콘의 역할을 한눈에 파악할 수 있습니다. 하지만 시각장애인은 이러한 시각적 정보를 인지하기 어렵습니다.

이를 개선하기 위해 다음과 같은 조치를 취했습니다.

  1. 승하차 장소 정보: 출발지와 도착지에 숨겨진 텍스트를 추가하여 스크린리더가 "출발지 알파돔타워, 도착지 부산역"이라고 읽을 수 있도록 했습니다.

  2. 출도착 전환 버튼: 기존 버튼에도 접근성을 개선했습니다. 버튼 내의 SVG 이미지 태그에 aria-label 속성을 추가하여 "출도착 전환, 버튼"이라고 읽어주도록 설정했습니다.

    <a href='#url'>알파돔타워</a>
    <a href='#url'>부산역</a>
    <button>
     <svg />
    </button>
    
    <a href='#url'>알파돔타워</a>
    <a href='#url'>부산역</a>
    <button>
     <svg />
    </button>
    
    <a href='#url'>
     <span class=’hidden’>출발지</span>
     알파돔타워
    </a>
    <a href='#url'>
     <span class=’hidden’>도착지</span>
     부산역
    </a>
    <button>
     <svg aria-label='출도착 전환' />
    </button>
    
    <a href='#url'>
     <span class=’hidden’>출발지</span>
     알파돔타워
    </a>
    <a href='#url'>
     <span class=’hidden’>도착지</span>
     부산역
    </a>
    <button>
     <svg aria-label='출도착 전환' />
    </button>
    
    코드가 숨겨졌습니다.

    이러한 변경을 통해 시각장애인 사용자도 화면의 정보를 정확히 이해하고 버튼의 기능을 파악할 수 있게 되었습니다.

    경로 검색 결과 부분 링크 복사

    경로 검색 결과 화면은 소요 시간 정보, 승하차 장소 정보, 지도 아이콘으로 구성되어 있습니다.


    경로 검색 결과 부분에도 같은 방식의 개선이 필요했습니다.

    1. 소요 시간 정보: "총 소요 시간 2시간 35분"이라는 숨겨진 텍스트를 추가하여 시각장애인 사용자가 소요 시간을 명확히 인지할 수 있도록 했습니다.
    2. 승하차 장소 정보: "승차 장소 동탄역, 하차 장소 부산역"이라는 숨겨진 텍스트를 추가하여 출발지와 도착지를 정확히 파악할 수 있게 했습니다.
    3. 지도 아이콘: 지도 아이콘의 SVG 이미지 태그에 aria-label 속성을 추가하여 "해당 경로 지도 화면 보기, 버튼"이라고 읽어주도록 설정했습니다.
      <button>
       <span>
        <span>2시간 35분</span>
        <span>기차 2시간 5분</span>
       </span>
      
       <span>
        <span>SRT</span>
        <span>동탄역</span>
        <span>부산역</span>
       </span>
      </button>
      
      <button>
       <svg />
      </button>
      
      <button>
       <span>
        <span>2시간 35분</span>
        <span>기차 2시간 5분</span>
       </span>
      
       <span>
        <span>SRT</span>
        <span>동탄역</span>
        <span>부산역</span>
       </span>
      </button>
      
      <button>
       <svg />
      </button>
      
      <button>
       <span>
        <span class='hidden'>총 소요시간</span>
        <span>2시간 35분</span>
        <span>기차 2시간 5분</span>
       </span>
      
       <span>
        <span>SRT</span>
        <span class='hidden'>승차장소</span>동탄역
        <span class='hidden'>하차장소</span>부산역
       </span>
      </button>
      
      <button>
       <svg aria-label='해당 경로 지도 화면 보기' />
      </button>
      
      <button>
       <span>
        <span class='hidden'>총 소요시간</span>
        <span>2시간 35분</span>
        <span>기차 2시간 5분</span>
       </span>
      
       <span>
        <span>SRT</span>
        <span class='hidden'>승차장소</span>동탄역
        <span class='hidden'>하차장소</span>부산역
       </span>
      </button>
      
      <button>
       <svg aria-label='해당 경로 지도 화면 보기' />
      </button>
      
      코드가 숨겨졌습니다.

      2. 기차 노선 이미지 개선 작업 링크 복사

      텍스트가 아닌 콘텐츠, 특히 기차 노선 이미지의 경우, 그 의미나 용도를 모든 사용자가 인식할 수 있도록 이미지와 동등한 수준의 대체 텍스트를 제공해야 합니다.

      아래 예시는 정선 아리랑 열차 노선 이미지입니다.

      기존의 대체 텍스트는 '정선아리랑열차(A_TRAIN) 노선'이라는 제한적인 정보만 제공해 시각장애인들이 열차가 정차하는 역들을 파악하기 어려웠습니다.

        <img src=’이미지url’ alt='정선아리랑열차(A_TRAIN) 노선' />
        
        <img src=’이미지url’ alt='정선아리랑열차(A_TRAIN) 노선' />
        
        <img src=’이미지url’ alt='정선아리랑열차(A_TRAIN) 노선, 청량리, 양평, 원주, 제천, 영월, 예미, 민둥산, 별어곡, 선평, 정선, 나전, 아우라지' />
        
        <img src=’이미지url’ alt='정선아리랑열차(A_TRAIN) 노선, 청량리, 양평, 원주, 제천, 영월, 예미, 민둥산, 별어곡, 선평, 정선, 나전, 아우라지' />
        
        코드가 숨겨졌습니다.

        이를 개선하기 위해 노선도 이미지에 더 자세한 alt 속성을 추가하여, 스크린 리더가 노선도에 표시된 모든 주요 역의 이름을 순서대로 읽어주도록 했습니다. 이 작업 이후, 사용자들은 열차의 전체 노선 정보를 모두 확인할 수 있게 되었습니다.

        인식의 용이성 - 명도 대비 링크 복사

        텍스트 콘텐츠와 배경 간의 명도 대비는 4.5:1 이상이어야 합니다. 이는 시각적 어려움을 겪는 사용자들을 포함한 모든 사용자가 콘텐츠를 쉽게 읽고 인식할 수 있도록 하기 위함입니다.

        명도 대비 기준을 충족하고 접근성 기준에 맞는 색상 팔레트를 개발하기 위해 디자인팀과 협력했습니다.

        [디자인팀]

        • 명도 대비 분석: 기존 디자인의 명도 대비를 분석하여 기준에 미달하는 부분을 식별합니다.
        • 색상 조정: 분석 결과에 따라 텍스트와 배경색을 조정하여 4.5:1 이상의 명도 대비를 확보합니다. 단, 글꼴의 크기 또는 굵기에 따라 3:1까지 예외적으로 허용합니다.

        [웹FE개발팀]

        • 적용 및 검증: 변경된 색상을 적용한 후, 다시 한번 명도 대비를 검증하여 모든 콘텐츠가 기준을 충족하는지 확인합니다.

        개선 후, 텍스트와 배경이 명확히 구분되어 보이며, 정보를 더욱 쉽게 인식하고 이해할 수 있음을 확인할 수 있습니다.


        운용의 접근성 - 초점 링크 복사

        1. 스케줄 목록 링크 복사

        스케줄 목록에서 버튼 역할을 하는 요소들이 div 태그로 구현되어 있어 초점이 이동하지 않는 문제가 있었습니다.

        이를 해결하기 위해 해당 div 태그들을 button 태그로 변경하여 초점이 적절히 이동할 수 있도록 개선했습니다.

          <ul>
           <li><div onClick={스케줄클릭이벤트}>스케줄</div></li> 
           <li><div onClick={스케줄클릭이벤트}>스케줄</div></li>
           ...
          </ul>
          
          <ul>
           <li><div onClick={스케줄클릭이벤트}>스케줄</div></li> 
           <li><div onClick={스케줄클릭이벤트}>스케줄</div></li>
           ...
          </ul>
          
          <ul>
           <li><button onClick={스케줄클릭이벤트}>스케줄</button></li> 
           <li><button onClick={스케줄클릭이벤트}>스케줄</button></li>
           ...
          </ul>
          
          <ul>
           <li><button onClick={스케줄클릭이벤트}>스케줄</button></li> 
           <li><button onClick={스케줄클릭이벤트}>스케줄</button></li>
           ...
          </ul>
          
          코드가 숨겨졌습니다.

          이와 같이 HTML 요소의 의미와 기능에 맞는 적절한 태그를 사용하는 것만으로도 웹 접근성을 크게 향상시킬 수 있습니다.


          2. 모달(Modal) 링크 복사

          모달은 사용자가 특정 버튼이나 링크를 클릭했을 때 기존 화면 내에 나타나는 레이어 창입니다. 주로 기존 화면이 어두워지면서(일명 '딤드' 처리) 화면에 노출됩니다.

          모달은 접근성과 관련하여 여러 가지 사항을 고려해야 합니다. 기본적으로 포커스 탐색은 모달 영역 내에서만 이동해야 하며, 포커스가 외부로 벗어나지 않도록 해야 합니다. 별다른 처리가 없으면, 스크린 리더는 모달이 노출된 상태에서도 뒤에 가려진 콘텐츠를 읽게 되는 문제가 발생할 수 있습니다.

          이 문제점을 아래 세 가지 단계로 해소했습니다.

          1. role='dialog'aria-modal='true' 속성을 사용하여 해당 요소가 모달임을 명확히 나타냅니다.
          2. 모달이 열릴 때 aria-hidden='true' 속성을 추가하여 스크린 리더가 배경 콘텐츠를 읽지 않도록 합니다.
          3. tabIndex와 js focus()를 활용해 포커스를 모달로 이동시킵니다.
            <div class='contents'>...</div>
            <div class=’modal’>
             <div class=’dim’></div>
             <div class=’modal-content’></div>
            </div>
            
            <div class='contents'>...</div>
            <div class=’modal’>
             <div class=’dim’></div>
             <div class=’modal-content’></div>
            </div>
            
            <div class='contents' aria-hidden=’true’>...</div>
            <div class='modal' role=’dialog’ aria-modal=’true’>
             <div class='dim'></div>
             <div class='modal-content' tabIndex=’0’></div>
            </div>
            
            // script
            modalContent.focus()
            
            <div class='contents' aria-hidden=’true’>...</div>
            <div class='modal' role=’dialog’ aria-modal=’true’>
             <div class='dim'></div>
             <div class='modal-content' tabIndex=’0’></div>
            </div>
            
            // script
            modalContent.focus()
            
            코드가 숨겨졌습니다.

            이러한 속성들을 통해 기본적인 포커스 접근성을 향상시킬 수 있지만, 보조 기술의 지원 여부에 따라 동작하지 않을 수 있습니다. 충분한 테스트를 통해 추가적인 대응이 필요합니다.

            이해의 용이성 - 콘텐츠의 논리성 링크 복사

            좌석 선택 화면 링크 복사

            기차 예매를 위한 좌석 선택 화면이 있습니다. 시각장애인은 이 화면에서 어떻게 좌석을 선택할 수 있을까요?

            기존 마크업에서는 '창 측, 내측, 내측, 창 측', '15A, 15B, 15C, 15D'와 같이 읽어주어 해당 좌석 번호의 위치나 창 측/내측 여부를 쉽게 파악하기 어려웠습니다.

            이것을, 기존 태그를 수정하지 않고, role='grid'와 관련된 rowgroup, row, columnheader 속성을 활용하여 스크린 리더 사용자들이 그리드 형태의 좌석 행과 열을 인식할 수 있도록 했고 히든 텍스트를 추가하였습니다. 개선된 마크업에서는 스크린 리더가 '창 측, 역방향 15A, 버튼'이라고 읽어주며, 시각장애인도 좌석 번호의 위치를 예측하고 선택할 수 있도록 개선하였습니다.

              <div>
                <span role="columnheader">창측</span>
                <span role="columnheader">내측</span>  
                <span role="columnheader">내측</span>
                <span role="columnheader">창측</span>
              </div>
              <ul>
                <li><button>15A</button></li>
                <li><button>15B</button></li>
                <li><button></button></li>
                <li><button>15C</button></li>
                <li><button>15D</button></li>
                ...
              </ul>
              
              <div>
                <span role="columnheader">창측</span>
                <span role="columnheader">내측</span>  
                <span role="columnheader">내측</span>
                <span role="columnheader">창측</span>
              </div>
              <ul>
                <li><button>15A</button></li>
                <li><button>15B</button></li>
                <li><button></button></li>
                <li><button>15C</button></li>
                <li><button>15D</button></li>
                ...
              </ul>
              
              <div role=’grid’>
               <div role="rowgroup">
                <div role="row">
                 <span role="columnheader">창측</span>
                 <span role="columnheader">내측</span>  
                 <span role="columnheader">통로</span>
                 <span role="columnheader">내측</span>
                 <span role="columnheader">창측</span>
                </div>
               </div>
              
               <ul role="rowgroup">
                <li role="row">
                 <button><span class=’hidden’>창측, 역방향</span>15A</button>
                </li>
                <li role="row">
                 <button><span class=’hidden’>내측, 역방향</span>15B</button>
                </li>
                <li role="row">
                 <button></button>
                </li>
                <li role="row">
                 <button><span class=’hidden’>내측, 역방향</span>15C</button>
                </li>
                <li role="row">
                 <button><span class=’hidden’>창측, 역방향</span>15D</button>
                </li>
                ...
               </ul>
              </div>
              
              <div role=’grid’>
               <div role="rowgroup">
                <div role="row">
                 <span role="columnheader">창측</span>
                 <span role="columnheader">내측</span>  
                 <span role="columnheader">통로</span>
                 <span role="columnheader">내측</span>
                 <span role="columnheader">창측</span>
                </div>
               </div>
              
               <ul role="rowgroup">
                <li role="row">
                 <button><span class=’hidden’>창측, 역방향</span>15A</button>
                </li>
                <li role="row">
                 <button><span class=’hidden’>내측, 역방향</span>15B</button>
                </li>
                <li role="row">
                 <button></button>
                </li>
                <li role="row">
                 <button><span class=’hidden’>내측, 역방향</span>15C</button>
                </li>
                <li role="row">
                 <button><span class=’hidden’>창측, 역방향</span>15D</button>
                </li>
                ...
               </ul>
              </div>
              
              코드가 숨겨졌습니다.

              마치며 링크 복사

              기획, 개발, 디자이너가 웹 접근성 지침을 준수한다고 해도 '모든 사용자가 실제로 이용하기에 편한가?'라는 사용성 문제는 끊임없는 고민거리이기 때문에 접근성 대응이 어려운 것 같습니다. 현실적으로 일정 및 인력의 한계로 접근성 준수에 대응하기 어렵고 저 역시 다양한 사용자를 위한 추가 개발을 부담스럽게 여긴 적이 있습니다. 하지만 우리 모두가 경험할 수 있는 다양한 상황을 편리하게 바꾸는 일이라고 생각하니 이해가 되었습니다. 누구나 노안이나 시력 저하를 경험할 수 있고, 일시적으로 어두운 빗길 운전이나 강렬한 햇빛 아래에서 앱 사용 시 화면을 인식하기 어려울 수 있습니다. 이번 접근성 개선 작업 경험을 기반으로, 앞으로도 모두가 편하게 이용할 수 있는 서비스를 만들기 위해 계속 고민하고 논의하려고 합니다.

              지난 4월 카카오는 장애인 30여 명이 직접 참여하는 '접근성 서포터즈' 발대식을 열고 본격적인 활동을 시작한다고 합니다. 카카오모빌리티 역시 참여하고 있으며, 이를 통해 다양한 사용자와 소통하며 서비스를 계속 개선할 예정입니다.