이 문서는 BottomSheet 상단 핸들을 아래로 드래그해 닫는 UX를 실제 서비스에 안정적으로 적용하기 위한 정리본입니다.
1const handleDragMove = (clientY: number, el: HTMLElement) => {2 if (!isDragging || isClosingRef.current) return34 const delta = clientY - startYRef.current5 deltaYRef.current = Math.max(0, delta)67 el.style.transitionDuration = '0ms'8 el.style.transform = `translate3d(0, ${deltaYRef.current}px, 0)`9}1const handleDragEnd = (el: HTMLElement, onClose?: () => void) => {2 const shouldClose = deltaYRef.current >= 2034 if (shouldClose && onClose) {5 isClosingRef.current = true6 el.style.transitionDuration = '300ms'7 el.style.transform = 'translate3d(0, 100%, 0)'8 window.setTimeout(() => onClose(), 300)9 } else {10 el.removeAttribute('style')11 }1213 setIsDragging(false)14 startYRef.current = 015 deltaYRef.current = 016}서비스 구조상 onClose가 즉시 언마운트를 유발한다면, 애니메이션 완료 후 onClose를 호출하는 2단계 닫기 패턴이 필수입니다.
1<StyledBottomSheet2 onMouseMove={handleMouseMove}3 onMouseUp={handleMouseUp}4 onMouseOut={handleMouseOut}5 onTouchMove={handleTouchMove}6 onTouchEnd={handleTouchEnd}7 onTouchCancel={handleTouchCancel}8>9 <DragBar onMouseDown={handleMouseDown} onTouchStart={handleTouchStart}>10 <DragHandle />11 </DragBar>12 {children}13</StyledBottomSheet>좋은 BottomSheet는 단순히 닫히는 컴포넌트가 아니라, 사용자의 손동작과 일관되게 반응하는 인터랙션 컴포넌트입니다.