HTML-CSS

HTML label 태그 사용하기

GoJay 2024. 11. 17. 23:24

<label> 태그

label 태그는 form의 다양한 요소들에 대한 부가적인 정보를 제공해주는 태그이다. 본래의 목적은 웹 접근성 향상으로, 예를 들어 <input id="email-input">이라는 인풋 요소가 하나 있을때 해당 요소를 해당 인풋 요소를 설명해주는 텍스트와 함께 label 태그로 감싸주면 스크린 리더기가 해당 input 창이 어떤 역할을 해주는 창인지를 좀 더 잘 인식하고 설명해줄 수 있다.

<label>
  <input id="email-input" type="text" placeholder="이메일을 입력해 주세요..." />
  이메일을 입력해 주세요!
</label>

시각 장애가 있는 분들이나, 화면을 눈으로 보고 사용하기 어려운 분들은 웹 사용을 위해 스크린 리더기와 같은 도구들에 의존하게 된다. 그런데, 스크린 리더기 입장에서는 input창의 위치만으로 해당 인터랙션 요소가 어떤 의미를 갖는지 파악하는 데 한계가 있을 것이다. 그렇기 때문에, 웹 접근성 측면에서 사용자와의 상호작용을 주로 해줘야하는 form 태그 요소가 각각 어떤 의미를 갖는지를 잘 표현해주는 것은 매우 중요하다.

label 태그의 하위에 form 태그들을 감싸는 식으로 사용하는 것을 '암묵적 연결(Implicit Association)'이라고 한다. 태그의 위계를 만들지 않고 label 태그와 form 태그 요소를 연결하기 위해선 for 어트리뷰트를 사용하면 된다.

<label for="email-input">이메일을 입력해 주세요!</label>
<input id="email-input" type="text" placeholder="이메일을 입력해 주세요..." />

for 어트리뷰트의 값으로는 연결하고 싶은 form 태그의 id를 입력해주면 된다. 위의 예제에서는 labelfor에 적용된 값과 inputid에 연결된 값이 일치하기 때문에, 스크린 리더기는 둘이 연관이 있는 태그라는 것을 쉽게 파악할 수 있다.

참고로, label 태그는 form 요소인 input 태그와 1:1로만 대응이 가능하다. 만약에 label 태그 하위에 2개 이상의 input 태그가 존재하면 가장 첫 번째 자식 요소인 태그가 label에 연결된다.

<label> 태그의 유용한 활용 팁

<label> 태그는 웹 접근성 측면만 생각해도 굉장히 중요한 역할을 하지만, 이외에도 아주 유용한 활용이 가능하다. 예를 들어, todo-item이라는 투두 리스트 요소의 p.content 부분을 클릭했을 때 type="checkbox"input 요소가 토글되도록 하고 싶은 상황을 생각해보자.

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>EXAMPLE</title>
  </head>
  <body>
    <div class="todo-item">
      <input type="checkbox" />
      <p class="content">HTML 공부하기</p>
    </div>
    <script>
      const $todoItem = document.querySelector('.todo-item');
      $todoItem.addEventListener('click', (e) => {
        if (e.target.tagName !== 'INPUT') {
          $todoItem.children[0].checked = !$todoItem.children[0].checked;
        }
      });
    </script>
  </body>
</html>

이벤트는 캡처링-버블링을 통해 .todo-item 하위 어떠한 요소에서 이벤트가 발생하더라도 캐치할 수 있다. 하지만, input 체크박스를 토글하기 위해선 e.target.tagName을 확인해야 한다. 왜냐하면 위 예제에서 divp 태그를 클릭했다면 수동으로 inputchecked 속성을 추가해줘야 하지만, input 영역을 클릭했다면 기본 동작으로 checked가 추가되기 때문에, 수동으로 한번 더 추가해주면 원상 복귀가 돼 아무 동작도 발생하지 않은 것처럼 보일 수 있기 때문이다.

이런 경우에 label 태그를 사용하면 토글 이벤트를 쉽게 처리해줄 수 있다. label 태그는 하위에 감싸진 다른 태그를 클릭하는 이벤트를 캐치해서 input 요소를 알아서 토글해준다.

<!DOCTYPE html>
<html lang="ko">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>EXAMPLE</title>
  </head>
  <body>
    <div class="todo-item">
      <label>
        <input type="checkbox" />
        <p class="content">HTML 공부하기</p>
        <p class="date">2024. 11. 17</p>
      </label>
    </div>
  </body>
</html>

이렇게 하면 p 태그인 .content, .date를 클릭해도 같은 label 태그의 하위 요소인 input 창의 checkbox가 토글된다. 자바스크립트 코드 없이도 클릭 이벤트를 활용한 토글 이벤트를 손쉽게 구현할 수 있게 됐다.

React에서의 <label> 태그

React에서도 label 태그를 사용할 수 있다. 하지만, 사용 시 몇 가지 주의가 필요하다.

먼저, label 태그의 어트리뷰트인 for가 자바스크립트의 예약어이기 때문에, 리액트에선 for가 아니라 forHTML을 사용해야 한다. 예약어인 class가 중복돼서 className으로 사용해야 하는 것과 유사한 상황이다.

다음으로, label에 연결된 input의 상태를 React에서 관리할 때, 반드시 상태를 통해 값과 변경 이벤트를 처리해야 한다. 만약에 그렇지 않으면 React는 자체적으로 관리할 onChange 이벤트 핸들러 없이 input의 상태를 변경하는 것으로 보고 아래와 같은 warning을 반환한다.

Warning: You provided a checked prop to a form field without an onChange handler. This will render a read-only field. If the field should be mutable use defaultChecked. Otherwise, set either onChange or readOnly. Error Component Stack

 

대표적으로 input type="checkbox"checked 속성을 추가하는 것 같은 변경은 React의 상태와 함께, input 태그에 직접 onChange 핸들러를 붙여서 상태 변경이 이뤄져야 한다. 물론, 에러가 아니라 주의 정도이기 때문에 반드시 그래야하는 것은 아니며, 만약 주의를 보는 게 싫다면 readOnly 설정을 해주면 된다(e.g. <input type="checkbox" checked readOnly)

결론

HTML은 단순 문법 반복이라고만 생각해서 공부의 깊이가 얕았는데, 공부하다보니 역시 새로운 게 계속 나온다. 잘 알고 활용하면 유용한 것들이 충분히 많은 것 같고, 시간을 많이 내서 공부하기 어렵더라도 이런 종류의 내용들을 알게 됐을 때마다 잘 정리하고 활용할 수 있도록 해봐야겠다.