라이브러리 없이 현대 UI 인터랙션 설계하기

라이브러리 없이 현대 UI 인터랙션 설계하기

Popover API·CSS Anchor Positioning·TanStack Query 패턴이 가리키는 공통 방향—도구를 줄이고 경계를 명확히 하는 것이 더 나은 설계다.

Popover API CSS Anchor Positioning TanStack Query Socket.io 번들 최적화 웹 표준 실시간 UI 컴포넌트 설계
광고

35KB를 0으로 만드는 선택

프론트엔드 개발자라면 한 번쯤 이 코드를 작성해봤을 것이다. 버튼을 클릭하면 뜨는 툴팁, 영역 밖을 클릭하면 닫히는 드롭다운, z-index: 9999를 뚫고 올라와야 하는 모달 위의 팝오버. 결과물은 보통 Floating UI(35KB), 세 개의 이벤트 리스너, ResizeObserver, 그리고 뷰포트 끝에서 위치가 틀어지지 않기를 바라는 조용한 기도였다.

Dev.to에 올라온 Vivian Voss의 글 The Native Popover That Positions Itself는 이 구조가 이미 무너지기 시작했다고 말한다. 브라우저가 그 일을 전부 네이티브로 처리하기 시작했기 때문이다. 2025년 4월 기준 모든 주요 브라우저에 안착한 Popover API는 단 두 줄의 HTML로 z-index 관리, 외부 클릭 감지(light dismiss), 키보드 ESC 처리, 포커스 관리를 한꺼번에 해결한다. JavaScript는 한 줄도 필요 없다.

위치 조정 문제도 곧 같은 방식으로 해결된다. Chrome 125+와 Safari 26+에 탑재된 CSS Anchor Positioningposition-anchor로 팝오버를 트리거 요소에 묶고, position-try-fallbacks로 뷰포트를 벗어날 때 자동으로 방향을 뒤집는다. getBoundingClientRect도, IntersectionObserver도, 스크롤 이벤트도 없다. Firefox는 아직 플래그 뒤에 있지만 지원이 임박했다. Popper.js(28KB), Tippy.js(22KB), Floating UI(35KB)가 해결하던 문제를 브라우저가 0KB로 대체하는 흐름이다.

도구가 줄면 경계가 선명해진다

한편, 실시간 데이터를 다루는 React 앱에서도 비슷한 '경계 설계'의 문제가 반복된다. TanStack Query와 Socket.io를 함께 쓸 때 가장 먼저 맞닥뜨리는 질문은 이것이다. "실시간으로 바뀌는 데이터도 결국 서버 상태 아닌가? 그럼 TanStack Query로 관리해야 하나, 소켓으로 관리해야 하나."

velog의 heisjun이 정리한 패턴은 이 혼란을 명쾌하게 정리한다. 두 도구는 경쟁 관계가 아니라 데이터 흐름의 방향이 다른 도구다. TanStack Query는 클라이언트가 요청을 시작하는 pull 모델이고, Socket.io는 서버가 먼저 이벤트를 밀어주는 push 모델이다. 이 둘을 억지로 하나의 레이어로 합치려 할 때 설계가 어색해진다.

실무에서 검증된 경계는 단순하다. 유저 프로필이나 상품 목록처럼 클라이언트가 요청해서 가져오는 데이터는 TanStack Query가 담당하고, 채팅 메시지 수신이나 알림처럼 서버가 먼저 밀어주는 이벤트는 Socket.io가 담당한다. 그리고 두 계층을 연결하는 핵심 패턴은 하나다. Socket.io 이벤트가 수신되면 TanStack Query 캐시를 업데이트한다. invalidateQueries로 리페치를 트리거하거나, setQueryData로 캐시를 직접 수정하는 방식으로.

소켓 연결은 싱글톤으로 유지해야 한다. 컴포넌트마다 새로운 연결을 맺으면 불필요한 연결이 쌓이고, useEffect 클린업에서 리스너를 제거하지 않으면 컴포넌트가 재마운트될 때 이벤트 핸들러가 중복으로 등록된다. 소켓 이벤트 처리 로직은 커스텀 훅으로 분리해 컴포넌트가 TanStack Query로 데이터를 읽기만 하도록 구조를 잡는 것이 관심사 분리의 핵심이다.

두 흐름이 가리키는 공통 방향

두 이야기를 나란히 놓으면 하나의 방향이 보인다. 도구의 역할 경계를 명확히 하고, 필요 이상의 추상화를 걷어내는 것. Popover API와 CSS Anchor Positioning은 외부 라이브러리가 채우던 자리를 플랫폼 자체가 흡수하는 흐름이고, TanStack Query + Socket.io 패턴은 두 라이브러리의 역할 경계를 억지로 합치지 않고 각자의 강점을 살리는 설계다. 방향은 다르지만 결론은 같다. 좋은 설계는 도구를 늘리는 것이 아니라 경계를 선명하게 긋는 데서 시작한다.

번들 크기와 직결되는 Core Web Vitals 관점에서도 이 흐름은 중요하다. 팝오버 하나를 위해 35KB 라이브러리를 로드하는 결정이 LCP와 TBT에 영향을 미친다는 건 이제 상식이다. 브라우저가 네이티브로 제공하는 기능의 범위가 넓어질수록, '이 라이브러리가 정말 필요한가'를 먼저 묻는 습관이 DX와 UX 모두에서 이득이 된다.

지금 당장 실험해볼 수 있는 것들

Popover API는 오늘 프로덕션에 쓸 수 있다. 간단한 툴팁이나 드롭다운부터 네이티브 구현으로 교체해보는 것이 현실적인 첫 걸음이다. CSS Anchor Positioning은 Safari 26+와 Chrome 125+를 지원 범위로 잡는 프로젝트라면 점진적으로 도입 가능하고, Firefox 지원이 안정화되는 시점이 전면 전환의 적기가 될 것이다. TanStack Query + Socket.io 패턴은 실시간 기능을 추가할 때 '어느 레이어가 이 데이터를 소유해야 하는가'를 먼저 물어보는 것만으로도 설계의 복잡도를 크게 낮출 수 있다.

라이브러리를 줄이는 것 자체가 목표가 되어서는 안 된다. 하지만 플랫폼이 충분히 성숙했을 때 그 위에 쌓아올린 추상화를 걷어내는 결정은, 코드베이스를 더 가볍게 만드는 동시에 팀이 진짜 문제에 집중할 수 있는 공간을 만들어준다. 지금이 그 판단을 다시 내려볼 시점이다.

출처

더 많은 AI 트렌드를 Seedora 앱에서 확인하세요