1. <input tye="checkbox"> 요소의 indeterminate 상태
체크박스 그룹이 있을 때 한 개 이상의 아이템에 체크가 되었을 때 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 컴포넌트가 로컬에서는 indeterminate가 true일 때 파란색 배경의 하얀색 대시 부호가 정상적으로 보였다. 하지만 개발 서버에 배포했더니 배경색이 하얀색이어서 inderminate가 아닌 unchecked인 것처럼 보였다. 개발자도구에서 Element 검사를 해보면 indeterminate 상태에 따라 <span>-</span> 대시 부호 또는 check 표시 <svg>가 정상적으로 조건부 렌더링이 되고 있었다.
문제는 그 부모 요소인 <span> 요소에서 checked가 false이고 indeterminate가 true일 때, 배경색에 대한 속성 값 bg-blue-500와 bg-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 우선 순위가 다른 것으로 추측되는데 특정 조건 내에서 이런 이슈가 있는 듯하다. 디버깅의 시작은 버그 현상의 재현인데 이 재현이 쉽지 않다는 걸 또 한 번 배운다.
indeterminate 및 checked 상태에 따라 배경색 및 테두리 색상을 동적으로 설정하는 함수 추가하여 해결하였다.
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 우선순위에 대한 얘기를 나누어볼 수 있을듯.
'돌멩이 하나 > 에러는 미래의 연봉' 카테고리의 다른 글
custom event로 렌더링과 무관한 데이터 추적하기 (0) | 2024.08.11 |
---|---|
무한 스크롤 이슈 디버깅 과정 (0) | 2024.07.27 |
React Query를 활용한 테이블에서 데이터 정렬하기 (0) | 2024.03.21 |
React ErrorBoundary와 react-query를 사용하여 예외 처리 설계하기 (0) | 2024.03.10 |
React의 key에 index를 사용하면 안 되는 이유 (feat. 무한스크롤) (0) | 2024.02.25 |