UI를 만들다 보면 마우스를 올렸을 때 색상이나 스타일이 살짝 바뀌는 효과를 자주 넣게 된다.
보통은 CSS :hover나 Tailwind의 hover: 클래스로 간단히 해결된다.
그런데 이번에 이런 상황이 있었다.

슬라이더의 진행도를 JS로 계산해서 linear-gradient로 표현하고 있었는데,
hover시에 어두운 부분의 색상을 살짝 바꾸고 싶었다.
단순히 색만 바뀌는 게 아니라, 진행도가 JS 계산식으로 동적으로 결정되는 구조라서 hover: 클래스로는 적용이 불가능했다.
왜냐하면 CSS의 :hover는 정적인 스타일 변경에만 적용되며, JS로 계산된 값에는 접근할 수 없기 때문이다.
슬라이더의 진행도(percent)를 기반으로 그라디언트를 계산하는 구조라서, hover 상태에서 이 값을 조정하려면 JS가 개입해야 했다.
결국 onMouseEnter / onMouseLeave로 hover 상태를 감지하고, 그 값을 이용해 그라디언트를 재계산하는 방법을 선택했다.
const [isHover, setIsHover] = useState(false);
<input
onMouseEnter={() => setIsHover(true)}
onMouseLeave={() => setIsHover(false)}
style={{
background: `linear-gradient(to right, #fff ${percent}%, ${
isHover ? "#535764" : "#414450"
} ${percent}%)`,
transition: "background 0.3s ease-in-out",
}}
/>
코드는 간단했지만, 한 가지 의문이 들었다.
“이렇게까지 해야 하나?
hover 하나 때문에 굳이 상태를 만들고 렌더링을 다시 돌리는 게 맞을까?”
왜 이런 고민이 생겼을까
React에서는 보통 상태(state)가 바뀌면 컴포넌트가 리렌더링된다.
즉, isHover를 true로 바꾸면 내부적으로 React가 DOM을 갱신하게 된다.
hover는 사실 브라우저가 CSS 레벨에서 처리해주는 시각적 상태이기 때문에, 굳이 리렌더를 일으킬 필요는 없다.
그렇다고 인라인 스타일에 :hover를 쓸 수도 없으니, 결국 선택지는 두 가지뿐이었다.
- CSS / Tailwind로 처리
- 정적인 값만 바뀔 때
- 단순한 색상 변화나 크기 변화 등
- React의 리렌더 없이 브라우저 수준에서 빠르게 동작
- 상태(state)로 관리
- hover 시 동적으로 계산된 값(JS 변수 등)을 써야 할 때
- hover 상태가 다른 컴포넌트나 로직에 영향을 미칠 때
이번 케이스는 그라디언트 위치를 계산해야 했기 때문에 결국 2번 방식을 선택했다.
같은 고민을 한 사람들
이 고민은 나만 한 게 아니었다.
Reddit의 r/reactjs - “Why would you use state for hover instead of plain CSS?” 글에서도
이 문제를 두고 여러 의견이 오갔다.
“CSS로 할 수 있는 건 CSS로 하는 게 낫다. 성능도 단순성도 그게 좋다.”
“하지만 hover 상태가 다른 UI나 로직에 영향을 줘야 한다면 state로 관리할 수밖에 없다.”
“트리 구조나 중첩된 hover 영역처럼 CSS로 표현하기 어려운 상황도 있다.”
결국 결론은 “필요할 때만, 이유가 있을 때만”이었다.
무조건 state로 하는 것도, 무조건 CSS로만 고집하는 것도 답은 아니다.
나의 결론
hover 상태를 state로 관리하는 건 결국 리렌더링을 발생시키는 방식이다.
따라서 단순한 시각적 변화만 필요하다면 CSS로 처리하는 게 가장 효율적이다.
하지만 JS 변수나 계산된 값이 필요하고, 그 값이 hover에 따라 바뀌어야 한다면 state를 사용하는 게 불가피하다.
이번 일을 통해 느낀 건, state는 기능적으로 가능하다고 해서 무조건 써야 하는 것은 아니라는 것이다.
상황의 성격에 따라, UI 변화만 필요한 경우엔 CSS에 맡기고, 데이터나 계산이 필요한 경우에만 state를 사용하는 게 적절하다.
