돌멩이 하나/에러는 미래의 연봉

Checkbox 컴포넌트를 만들면서 알게 된 것들

미래에서 온 개발자 2024. 5. 22. 12:59

1. <input tye="checkbox"> 요소의 indeterminate 상태

 

:indeterminate - CSS: Cascading Style Sheets | MDN

The :indeterminate CSS pseudo-class represents any form element whose state is indeterminate, such as checkboxes that have been set to an indeterminate state with JavaScript, radio buttons which are members of a group in which all radio buttons are uncheck

developer.mozilla.org

 

체크박스 그룹이 있을 때 한 개 이상의 아이템에 체크가 되었을 때 Check all 체크박스에 표시되는 상태로 브라우저에서 기본으로 제공하는 css는 위의 이미지와 같이 대시(-) 부호로 표시된다. indeterminate 상태는 checked와는 다르게 HTML 속성으로 컨트롤할 수 없으며, JavaScript로만 제어할 수 있다. 

 

document.getElementById("myCheck").indeterminate = true;

 

 

2. 체크박스를 연속으로 클릭 시 체크박스의 checked 상태가 토글되는 것 외에 주변 텍스트가 선택되는 사이드 이펙트

문제 원인:

체크박스를 빠르게 연속으로 클릭할 때, 브라우저가 텍스트 드래그/선택 동작으로 해석하여 체크박스 주변의 텍스트가 선택된다. 

 

해결 방법:

<span> 요소의 onMouseDown 이벤트에서 event.preventDefault() 를 호출하여 텍스트 선택 기본 동작을 방지한다. 

 

 

 

 

처음에는 컴포넌트의 최상위 요소인 <label>에 css user-select: none 속성을 추가하여 버그를 해결하려고 했는데, 이 경우 연속으로 클릭시 매번 텍스트가 선택되는 이슈는 사라지지만 최초 한 번은 텍스트가 선택되었다. gpt-4o 선생에게 왜 이러냐고 물었더니 처음에는 클릭할 때 브라우저의 기본 동작으로 인해 텍스트 선택이 발생하고, user-select: none 때문에 텍스트는 실제로 선택되지 않지만 클릭의 흔적이 남아 셀렉션이 시각적으로 표시될 수 있다(?)고 답변해주었다. 맞는 소리인지는 모르겠다.. 

 

참고로 Bootstrap v4에서도 같은 이슈가 있었다. (v5 에서는 해결되었다.)

https://github.com/twbs/bootstrap/issues/26473

 

 

3. CSS 명시도와 순서 

이건 체크박스와 관련 있는 버그는 아니다. 위의 CheckBox 컴포넌트가 로컬에서는 indeterminatetrue일 때 파란색 배경의 하얀색 대시 부호가 정상적으로 보였다. 하지만 개발 서버에 배포했더니 배경색이 하얀색이어서 inderminate가 아닌 unchecked인 것처럼 보였다. 개발자도구에서 Element 검사를 해보면 indeterminate 상태에 따라 <span>-</span> 대시 부호 또는 check 표시 <svg>가 정상적으로 조건부 렌더링이 되고 있었다. 

 

문제는 그 부모 요소인 <span> 요소에서 checked가 false이고 indeterminate가 true일 때, 배경색에 대한 속성 값 bg-blue-500bg-white가 중복으로 작성되었고, 로컬에서는 bg-blue-500이 우선 적용되었지만, 개발 서버에 배포했을 때는 bg-white 클래스가 우선 적용된 것이다. 

 

참고로 class 안에서 속성 값을 적는 순서와는 무관했다. 즉, bg-blue-500과 bg-white 어떤 걸 먼저 적어도 마찬가지였다. 

 

"The order of the items in your class string will not make a difference."
출처 : https://stackoverflow.com/questions/75664539/tailwind-css-class-precedence-is-not-respected

 

 

이를 재현하기 위해 html + tailwind 로 작성한 코드에서는 같은 이슈가 발생하지 않았다. 로컬 환경에서 프로덕션 환경에서 tailwind의 CSS 우선 순위가 다른 것으로 추측되는데 특정 조건 내에서 이런 이슈가 있는 듯하다. 디버깅의 시작은 버그 현상의 재현인데 이 재현이 쉽지 않다는 걸 또 한 번 배운다. 

 

indeterminatechecked 상태에 따라 배경색 및 테두리 색상을 동적으로 설정하는 함수 추가하여 해결하였다. 

 

  const getBackgroundColor = () => {
    if (indeterminate) {
      return 'bg-blue-500';
    }
    return checked ? 'bg-blue-500' : 'bg-white';
  };

  const getBorderColor = () => {
    if (indeterminate) {
      return 'border-blue-500';
    }
    return checked ? 'border-blue-500' : 'border-gray-300';
  };
  
  
// before 
<span
  className={`w-4 h-4 flex items-center justify-center border rounded-sm cursor-pointer ${
    checked ? 'bg-blue-500 border-blue-500' : 'bg-white border-gray-300'
  } ${indeterminate ? 'bg-blue-500 border-blue-500' : ''}`}
>
  
// after
<span
  className={`w-4 h-4 flex items-center justify-center border rounded-sm cursor-pointer ${getBackgroundColor()} ${getBorderColor()}`}
>

 

 

+) 세 번째 문제는 신입 프론트엔드 개발자 기술면접 문제로 내기 좋은 자료 같다. 변경 전 컴포넌트에서 예상되는 문제와 해결 방법은 무엇일지 물어보면 1) 컴포넌트가 하는 일을 파악하는 능력 2) CSS 우선순위에 대한 얘기를 나누어볼 수 있을듯.