Kally

실시간 콘텐츠 영역(Live Region)을 알아보자

작성자: Joseph

작성 날짜: 2025. 2. 25. 오전 5:33:32

수정 날짜: 2025. 6. 12. 오전 6:12:48

안녕하세요. Joseph입니다. 이번 주제는 실시간 콘텐츠 영역, 영어 표현으로는 라이브 리전(Live Region)이라고 부르는 WAI-ARIA 기술에 관해 다뤄보고자 합니다.

라이브 리전이 뭐에요?

일반적으로 눈으로 시각적인 UI를 보면 언제, 어떻게 콘텐츠가 변하는지 바로 파악할 수 있습니다. 예를 들어, 텍스트가 "사과"였다가 "Apple"로 갑자기 바뀐다면, 변한 것을 직관적으로 바로 알 수 있습니다. 조금 더 와닿는 예를 들어보자면 웹 채팅창에서 상대방이 답장을 보냈을 때, 목록에 업데이트된 상대방의 답장을 바로 알 수 있지요.

그런데, 스크린 리더 사용자는 화면을 2차원, 선형으로 탐색하기 때문에 탐색 중인 요소 하나 외에 정보는 원레대로라면 들을 수 없습니다.

스크린리더 사용자가 채팅을 할 때는 보통 채팅 입력란에서 초점을 두게 됩니다. 이 상황에서 스크린 리더 사용자는 입력중인 텍스트와 입력한 텍스트 외에 바뀐 정보는 들을 수 없다는 얘기지요.

웹에서 이런 이슈를 해결할 수 있도록 만들어진 것이 바로 라이브 리전입니다. 라이브 리전 요소는 스크린 리더 사용자에게 실시간 정보를 전달하는 요소로, 라이브 리전으로 지정된 컨테이너 안에 변경 내용이 있으면 해당 변경사항을 스크린 리더 사용자에게 전달합니다.

직접 사용해 봅시다.

ARIA 기술을 사용하기 전에

WAI-ARIA 기술을 사용할 때는 특히 다른 웹표준 지침보다도 명세를 엄격하게 적용해야 합니다. 사용 할 때는 반드시 WAI-ARIA의 명세를 확인하여야 합니다.

WAi-ARIA를 통해 얻을 수 있는 이점만큼, WAI-ARIA를 잘못 사용했을 때의 위험성 리스크 또한 매우 높으므로,

WAI-ARIA 기술의 접근성 자유도에는 그만한 리스크가 있음을 항상 유의해야 합니다.

import React, {useState} from 'react';
function Counter() {
const [count,updateCount] = useState<number>(0);

function increment() { updateCount(count+1); }
function decrement() { updateCount(count-1); }

const buttonStyle:CSSProperties = {
  padding : "0.5rem",
  margin  : "0 0.25rem",
  fontSize: "1.3rem"
}
return (<div className="counter-wrapper" style={{
  display:"flex",
  alignItems:"center",
  justifyContent:"center"
}}>
  <button style={buttonStyle} aria-label="감소" onClick={decrement}>-</button>
  <span className="count" aria-live="assertive">
    {count}
  </span>
  <button style={buttonStyle} aria-label="증가" onClick={increment}>+</button>
</div>)
}

위 코드는 React에서 카운터를 구현한 것입니다. 다양한 플랫폼을 공부해보신 분이라면 익숙한 에제겠네요.

감소와 증가, 두 버튼을 누르면 count 변수 값이 업데이트됩니다. 감소를 누르면 현재 count값에서 1을 뺀 값을, 증가를 누르면 현재 count 값에 1을 더한 값으로 업데이트합니다. 업데이트된 값은, 두 버튼 사이에 표시되며, 바뀔 때 마다 업데이트됩니다.

여기서 눈여겨봐야할 코드는 aria-live="assertive"입니다. 이 속성이 평범한 요소를 실시간 콘텐츠 영역으로 만들어줍니다.

이 속성이 있기 떄문에 버튼을 누를 때 마다, 시각적으로 숫자가 변경되며, 동시에 바뀐 정보를 스크린 리더로 들을 수 있습니다.

더 자세히 알아보기

라이브 리전은 assertive와 polite 두 종류로 나뉩니다. 이 둘의 차이점은 음성이 업데이트된 정보를 알리는 우선순위입니다. assertive는 기존에 읽던 내용을 끊고 끼어들어 바뀐 내용을 알립니다.

기본적으로 라이브 리전을 지정하기 위해서는 aria-live="assertive|polite|off"를 사용합니다.

반면에 polite는 기존에 읽던 내용이 있다면, 스크린 리더가 해당 내용을 모두 말할 때 까지 기다린 다음에 바뀐 내용을 전달합니다.

사전적 의미로는 assertive는 '단정적인', '독단적인', '자기 주장이 강한' 등의 뜻을 가지고 있습니다. 좀 농담을 섞어서 말하자면 싸가지가 없는 위 아래 없이 대드는 녀석입니다. 말은 부정적으로 했지만, 급하게 내용을 전달해야 하거나, 다른 정보보다 바뀐 정보를 최우선으로 전달해야 할 때, 이 assertive를 사용합니다.

polite는 '공손한'이라는 뜻을 가지고 있습니다. assertive는 날나리 불량 학생이고 polite는 우등생 같은 친구입니다. 먼저 누군가 말하고 있다면, 말을 충분히 듣고 말합니다.

polite는 주로 다른 무언가를 사용자가 조작하는 와중에 변경된 내용을 전달해야 하는 상황에 활용됩니다. 예를들어, 회원 가입 도중에 아이디 입력란에 아이디를 입력했는데, 형식에 맞지 않다는 메시지를 출력하고 있을 때, 이 polite를 사용해야 합니다. assertive는 자기 주장만 펼치는 독불장군 같은 친구이기 때문에, 사용자가 입력하던 정보를 자르고 끼어듭니다. 즉, 입력값이 올바른지, 잘못됐는지 확인하는 데 방해가 됩니다.

다른 무언가를 사용자가 읽고 있을 가능성이 있고, 바뀐 내용을 반드시 전달해야 한다면 공손한 우등생인 polite를 찾읍시다.

둘 다 꼴 보기 싫고 내 앞에서 사라져 줬으면 좋겠다면 aria-live를 off 값으로 설정해주면 됩니다.

앵무새같은 aria-live가 성가실 때는?

라이브 리전은 기본적으로 특정 정보가 변경되면 변경된 정보를 포함한 전체 내용을 읽습니다. 앵무새가 같은 말을 반복하는 것 처럼요. 이를 방지하고 바뀐 부분만을 읽었으면 좋겠다면 aria-atomic이라는 똘마니를 부릴 수 있습니다.

aria-atomic을 true로 설정하면 내용이 바뀌었을 때, 변경된 부분만을 알리게 됩니다.

aria-live의 알림 범위를 지정하고 싶을 때는?

라이브 리전에는 aria-relevant라는 꼬봉이 있습니다. 이 친구는 라이브 리전 내 변경 사항이 있을 때 알려줄 알림의 범위를 지정하는 데 사용합니다.

  • additions text: 라이브 리전 요소 안에서 요소가 추가되거나, 요소 내용이 변경되면 알리는 것을 기본값으로 합니다.
  • additions: 요소가 추가됐을 때만 알립니다.
  • removals: 요소가 제거됐을 때만 알립니다.
  • text: 라이브 리전 내 변경된 텍스트만 알립니다.
  • all: 요소의 추가, 삭제 및 요소 텍스트의 변경을 모두 알립니다.

라이브 리전의 메커니즘

aria-relevant에서 설명한 것 처럼, aria-live를 설정한 요소는 기본적으로 텍스트 변경과 요소 추가를 감시합니다. 텍스트 변경은 구체적으로 Node 인스턴스 중, Text 객체의 textContent 속성 값의 변경을 의미합니다.

기본적으로 DOM을 다룰 때 사용하는 innerHTML은 하위에 있는 노드를 제거하고 교체하는 일련의 작업을 수행합니다. 즉, 텍스트의 변경과는 무관합니다.

라이브 리전에서의 추가된 요소와 삭제된 요소를 감지하는 메커니즘은 접근성 트리를 기준으로 합니다.

이 말은, 진짜로 요소가 사라지거나, 추가되는 경우에도 발생하지만, aria-hidden="true"였던 요소가 false로 바뀌거나 CSS 스타일에서 display가 none이였던 요소가 눈에 보이는 display 유형 값으로 변경됐을 때도 동작합니다. visibility:hidden이 visible로 바뀌었을 때도 마찬가지이며, inert 속성이 적용되어 있던 요소에서 inert 속성을 제거했을 때도 라이브 리전에서는 요소가 추가된 것으로 인식합니다.

라이브 리전이 기본값인 요소

  • alert:

    role="alert"aria-live="assertive"가 기본으로 적용되어 있습니다.

    경보, 긴급 알림 등의 의미를 가지고 있으니 조금은 예의없지만 신속하게 정보를 전달해야 하므로 기존 내용을 기다리지 않고 끼어듭니다.

  • alertdialog:

    위에서 설명한 role="alert"role="dialog"가 합쳐진 형태입니다.

    대화상자가 열리면 자동으로 대화상자 내에 있는 내용을 읽습니다.

  • status:

    role="alert"과는 반대로 aria-live="polite"가 기본인 요소입니다.

    주 용도는 웹에서 실시간으로 변경되는 로그 화면이나 터미널 애플리케이션에서 사용하기 위해 만들어졌습니다.

주의사항

  1. 라이브 리전을 너무 남발하지 마세요.

뭐든지 실시간으로 전달되는 것이 이상적이고 좋아보일 수 있지만, 스크린 리더의 사용 패턴과 과도한 실시간 정보 제공은 맞지 않습니다. 탐색을 방해할 수 있습니다.

  1. 스크린 리더 설정 중 라이브 리전 읽기 여부 설정이 있어도 live region 사용은 신중해야 합니다.

1번과 겹치는 주의사항으로, "과도한 실시간 정보가 불편하면 사용자가 끄면 되잖아요?"라는 생각을 하실 수 있어서 적어 놓습니다. 스크린 리더에서 라이브 리전 읽기 여부를 설정하는 것은, 웹사이트 내 모든 라이브 리전에 적용됩니다.

즉 어떤 실시간 콘텐츠를 읽게 할 지 선택할 수 없습니다. 때문에, 진짜 중요한 알림을 aria-live로 제공했을 때, 해당 알림도 제공받지 못하는 위험이 있습니다.

알려진 이슈

  • Sense Reader는 노드의 추가를 인지할 수 있으나, 텍스트 노드만 업데이트 되는 경우 인식하지 못합니다. innerHTML로 업데이트할 경우 노드 자체가 새로 그려지므로 센스리더에서 이를 인식하지만, 직접 Text 객체에 접근하여 내용을 변경하면 인식하지 못합니다. 이는 React와 같은 선언형 Javascript UI 프레임워크에서 두드러지는 문제입니다.
  • Sense Reader는 aria-live="polite" 지원하지 않습니다.
  • Sense Reader는 aria-atomic을 지원하지 않습니다.