이벤트 리스너를 지우는 CSS — ‘상태’가 JS를 대신한다
CSS-Tricks의 Daniel Schwarz가 6월 29일, CSS가 자바스크립트 이벤트 처리의 영역을 어디까지 흡수했는지 전면적으로 정리했다. :hover 같은 고전적 의사 클래스부터 폼 검증(:user-invalid), 팝오버(:popover-open), 미디어 재생 상태(:playing, :muted)까지 — 예전이라면 이벤트 리스너를 달고 클래스를 토글하던 일들이 이제 선언 한 줄로 끝난다. 폼·오버레이·미디어라는 UI의 굵직한 축이 모두 CSS 상태로 표현 가능해진 지금이, 이 경계선을 다시 그어볼 시점이다.
"CSS is accumulating more and more pseudo-classes to help us respond to JavaScript events so that we don't have to do so with JavaScript itself."CSS-Tricks, Daniel Schwarz
무슨 일인가
글의 핵심 통찰은 CSS가 추적하는 것이 ‘이벤트’가 아니라 이벤트 사이의 상태라는 점이다. :hover는 pointerenter와 pointerleave 사이의 구간이고, :checked는 change 이벤트 후의 결과다. 폼 검증은 checkValidity() 호출 대신 :valid/:invalid/:user-invalid로, 해시 변경 감지는 hashchange 리스너 대신 :target으로, 전체화면은 fullscreenchange 대신 :fullscreen으로 대체된다. 나아가 제안 단계인 event-trigger는 click·dblclick 같은 이벤트를 CSS가 직접 받아 애니메이션을 실행하는 기능으로, 상태와 이벤트의 구분 자체를 무너뜨린다. 다만 미디어 의사 클래스는 아직 Chrome이 미지원이고 event-trigger는 어떤 브라우저에도 구현되지 않았다.
여러 시각
‘JS가 하던 일을 CSS가 가져간다’는 흐름은 상태 추적에 그치지 않는다.
- web.dev — @property가 3대 엔진 Baseline에 도달하면서, 커스텀 프로퍼티에 타입·초기값·오류 폴백까지 부여할 수 있게 됐다. 그라디언트 색상처럼 JS로 보간하던 값을 CSS가 직접 애니메이션한다. 상태에 이어 ‘타입 시스템과 오류 처리’까지 CSS가 흡수한다는 같은 방향의 신호다.
- CSS-Tricks(스크롤 트리거) — Chrome 146에 실린 animation-trigger·timeline-trigger는 IntersectionObserver로 구현하던 뷰포트 진입 애니메이션을 CSS로 옮긴다. 아직 Chrome 단독 지원이지만, ‘관찰하고 반응하는 코드’를 선언으로 바꾸는 궤적은 동일하다.
왜 중요한가
이벤트 리스너는 등록·해제·클린업이라는 수명주기 관리가 따라붙고, 이는 SPA 메모리 누수와 상태 불일치 버그의 단골 원인이다. 선언적 CSS 상태는 그 수명주기 자체가 없어, 코드가 줄어드는 것 이상으로 버그의 표면적이 줄어든다. 메인 스레드 JS가 줄면 성능 예산에도 직접적인 이득이다. 다만 지원 편차가 있는 만큼, ‘CSS 우선 + JS 폴백’이라는 점진적 향상 전략이 다시 중요해진다.
실무 적용
- 폼 검증 피드백은 :user-valid/:user-invalid로 스타일링하고, JS는 제출 시점의 최종 검증만 담당하게 역할을 나눈다.
- 팝오버·아코디언·다이얼로그는 popover 속성과 :popover-open/:open 상태를 기본으로 삼아 toggle 이벤트 리스너를 걷어낸다.
- 뷰포트 진입 애니메이션은 @supports로 스크롤 트리거 CSS를 감싸고 IntersectionObserver를 폴백으로 유지한다.
Kenny의 관점
이 글이 준 가장 실용적인 도구는 “이건 상태인가, 이벤트인가”라는 질문이다. 상태라면 CSS가 1순위 후보고, 네트워크 요청처럼 진짜 ‘사건’일 때만 JS가 필요하다 — 코드 리뷰에서 바로 쓸 수 있는 기준이다. AI 기능이 붙으며 JS 번들이 계속 무거워지는 요즘, UI 상태를 CSS 레이어로 내려보내는 것은 취향이 아니라 성능 예산을 확보하는 수단이다. 이벤트 리스너를 하나 지울 때마다 앱은 조금 더 단순해진다.
출처
이 글은 아래 원문을 바탕으로 Kenny가 한국어로 요약·정리한 큐레이션입니다.
원문 보기 — CSS-Tricks ↗