React에서 접근성을 살려 이모지 사용하기💡 (with. role, aria-label, aria-hidden✨)

김방울·2023년 1월 17일
2

HTML / CSS

목록 보기
3/6
post-thumbnail

이 글은 https://medium.com/@seanmcp/%EF%B8%8F-how-to-use-emojis-in-react-d23bbf608bf7 의 포스트를 토대로 작성했습니다!

프로젝트 개발을 하다 보면 이모티콘(이모지)를 사용하는 경우가 종종 있습니다.
이전에는 일반적으로 게시물을 작성하거나 문자를 보낼 때 처럼 인라인으로 이모지를 삽입해 왔습니다.

이번에 과제 수행을 하며 이모티콘을 활용할 일이 생겼고 문득 '이렇게 사용해도 되나?' 라는 의문이 들어 검색해 보니 이 방법은 접근성에 좋지 않다고 합니다...🙅 (스크린리더가 이모티콘에 대한 정보를 파악할 수 없기 때문에 이상한 문자로 읽는다고 합니다)

<h1 className='section__title'>맹수의 왕, 김방울😺</h1>
<p className='section__desc'>5살 코숏 고양이 김방울을 소개합니다.🙋</p>

html의 role 속성과aria-label 속성을 이용하면 접근성을 보완해줄 수 있습니다.

role 속성이란

ARIA role은 콘텐츠에 의미론적 의미를 제공하여 화면 판독기 및 기타 도구가 해당 유형의 개체에 대한 사용자 기대치와 일치하는 방식으로 개체와의 상호 작용을 표시하고 지원할 수 있도록 합니다. ARIA role은 HTML에 원래 존재하지 않거나 존재하지만 아직 완전한 브라우저 지원이 없는 요소를 설명하는 데 사용할 수 있습니다. -mdn web docs의 WAI-ARIA 설명

role='img' 속성은 단일 이미지로 간주되어야 하는 페이지 콘텐츠 내의 여러 요소를 식별하는 데 사용할 수 있습니다. 이러한 요소는 이미지, 코드 스니펫, 텍스트, 이모티콘 또는 시각적 방식으로 정보를 전달하기 위해 결합할 수 있는 기타 콘텐츠가 될 수 있습니다. -mdn web docs의 img role 설명

<div role="img" aria-label="Description of the overall image">
  <img src="graphic1.png" alt="" />
  <img src="graphic2.png" />
</div>

한마디로 element 요소가 담당하는 역할을 정의해주는 것입니다.

보통 탭 메뉴를 구성할 경우<ul><li> 태그 또는 <div><button> 태그를 이용해 탭 메뉴를 구성할 텐데요, <div><button> 태그로 탭을 구성할 경우 시각적 정보를 통해 웹 컨텐츠를 받아들일 수 있는 사람은 탭 메뉴라는 것을 인식할 수 있지만 브라우저나 스크린 리더기는 이 요소가 탭메뉴라는 것을 알 수가 없습니다.😭
role='tab', role='tablist' 등을 사용하면 이 요소가 탭 메뉴의 역할을 담당한다고 알릴 수 있습니다.

styled-componentas 는 스타일 속성을 유지하면서 태그를 바꿔주고 싶을 때 사용하는 속성이죠. 이와 비슷한 역할을 해 주는 html 상의 속성...! 이라고 생각이 들었습니다.😎

물론, 굳이 role을 사용하지 않고 시맨틱하게 코드를 짠 뒤, 꼭 필요한 곳에만 role 속성을 써 주는 것이 가장 바람직하다고 합니다.

aria-label 속성이란

aria-label은 레이블로 참조되어질 수 있는 DOM 안에 표시가능한 적당한 텍스트가 없을 때, 상호작용을 하는 요소들 또는 다른 ARIA 선언들을 통해 상호작용하도록 만들어진 요소에 사용하기 위한 것입니다. -mdn web docs의 aria-label 설명

설명을 읽어도 감이 안 와서 더 찾아보니🤔
화면에 현재 요소가 무엇인지 설명할 텍스트가 없을 경우imgalt, input 태그와 함께하는 label 속성처럼 요소가 무엇인지 설명해 주는 속성이라고 합니다.

예시를 들기 위해 현재 사이드 프로젝트로 제작중인 블로그를 가져와봤습니다.. 👽
이 화면에서도 붉은 사각형 박스로 표시한 부분들은, 단순히 장식용! 이 아닌 클릭 등의 접근이 가능한 요소들입니다.

<Button className='Header__button-darkmode' onClick={onChangeDarkMode}>
  <SUN_24 /> // svg 파일입니다!
</Button>

상단 우측에 위치한 다크모드 전환 버튼은 다음과 같은 구조를 갖고 있었는데, 스크린 리더 이용자들을 위해 접근성을 고려하려면 aria-label 을 사용해 다음과 같이 바꿔주어야겠죠.

<Button 
  className='Header__button-darkmode' 
  onClick={onChangeDarkMode} 
  aria-label='다크모드 전환'
>
  <SUN_24 />
</Button>

이 같이 이모티콘도 태그로 감싼 뒤, aria-label 속성으로 설명을 해 주면 스크린리더가 태그 내의 진짜 컨텐츠(이모티콘)는 읽지 않고 aria-label 에 적힌 속성을 읽습니다.

<div role="img" aria-label="Table flip"> // aria-label의 내용을 스크린 리더기에서 읽습니다.
  <p>(╯°□°)╯︵ ┻━┻</p> // 이 이모티콘은 스크린 리더기에서 읽지 않습니다!
</div>

aria-hidden 속성이란

저는🙋 이렇게 글의 장식용📄으로만 이모티콘을 사용할 예정❗이어서 스크린 리더기에 읽히는 것을 원하지 않아요. 🙅

하지만 단순히 렌더링 목적, 즉 장식용으로만 이모티콘을 사용하고 싶은데 스크린 리더기에 읽힌다면 오히려 접근성이 떨어지고 콘텐츠 본래 내용도 파악하기 매우 어려워질 것입니다. 위의 예시만 해도, 이모티콘을 사용할 예정느낌표이어서 라고 읽어버린다면 내용이 매우 부자연스러워집니다...💦

요소에 장식용으로 간주될 수 있는 콘텐츠가 포함되어 있거나 페이지에 렌더링 된 동등하게 액세스 가능한 콘텐츠에 중복되는 요소를 보조기술 사용자에게 노출되지 않도록 설정할 수 있습니다. 이를테면, 시각적으로 렌더링 된 텍스트와 함께 SVG 또는 글꼴 아이콘을 포함하는 링크입니다. 이 상황에서 아이콘은 링크 텍스트와 중복될 수 있으므로 보조기술 사용자에게 아이콘 정보를 숨김으로써 콘텐츠 탐색을 용이하게 할 수 있습니다.

aria-hidden='true' 속성으로 스크린 리더기에서 읽히지 않을 요소를 설정할 수 있다는 내용입니다!

Emoji 컴포넌트 만들기🌟

<span role="img" aria-label="sheep">🐑</span>

위의 내용을 종합하면, 이모지를 사용할 때는 다음과 같은 형식으로 사용이 권장됩니다. 스크린 리더기는 '양'이라는 단어로 레이블을 지정할 수 있는 이미지로 위 요소를 이해합니다.

<p>
  방울이는 <span role="img" aria-label="cat">😺</span><span role="img" aria-hidden="true">💗</span>
</p>

그런데 이 마크업... 간단한 문장을 표현하는데도 길이가 훨씬 길어져서 보기 불편합니다. 게다가 이모티콘을 이런 형식으로 적재적소에 사용하려면 정말 엄청나게 귀찮습니다.

개인적으로 리액트의 꽃🌸은 재사용이 가능한 컴포넌트 라고 생각합니다.

const Emoji = (props) => {
  return (
    <span
      className='Emoji'
      role='img'
      aria-label={props.label ? props.label : ''}
      aria-hidden={props.label ? 'false' : 'true '}
    >
      {props.symbol}
    </span>
  );
};

export default Emoji;
import Emoji from 'components/common/Emoji';
...

<Emoji symbol='🎨' />
...
<p>
  방울이는 <Emoji label='cat' symbol='😺' /><Emoji symbol='💗' />
</p>

다음과 같이 간단하게 컴포넌트화해주면 매우 편하게 이모티콘을 사용할 수 있습니다!
타입스크립트 사용 시에는 labelsymbol 속성 모두 string 타입이 되겠네요🌞

import Link from 'next/link';
import Emoji from './Emoji';

const LinkEmoji = (props) => {
  return (
    <Link
      className='LinkEmoji'
      href={props.href}
      target='_blank'
      style={{ display: 'inline-block' }}
    >
      <Emoji symbol='🔗' />
    </Link>
  );
};

export default LinkEmoji;

링크 갖다 붙이는 것도 귀찮았던 저는 한술 더 떠서 링크용 이모지 컴포넌트도 만들었습니다..
😅

마치며

간단하게 공부하고자 글을 적으려 했는데 문서를 읽다 보니 생각보다 많이 길어졌습니다.🤔 항상 느끼는 거지만 html은 입문할 때만 쉬운 것 같아요. 그래도 이번 포스트를 적으면서 알고 있던 지식들도 복기하고, 모호하게만 알고 있던 개념들도 꽤나 이해하게 되었습니다😄

참고자료

profile
코딩하는 고양이🐱 / UI Developer, Front-end Developer

0개의 댓글