본문 바로가기

WEB개발/REACT

[기본 event] elements 드래그

 

기본적인 이벤트

<div
  onMouseDown={startLongPress}
  onMouseUp={removeAndReset}
  onMouseLeave={removeAndReset}

  onTouchStart={startLongPress}
  onTouchEnd={removeAndReset}

  onContextMenu={(e) => e.preventDefault()}
>

 

    const startLongPress = useCallback((cat: string, clientX: number, clientY: number) => {
        if (longPressTimer.current) clearTimeout(longPressTimer.current);
        if (cleanupRef.current) cleanupRef.current();

        pressStartPos.current = { x: clientX, y: clientY };

        const onMove = (e: MouseEvent | TouchEvent) => {
            const p = e instanceof TouchEvent ? e.touches[0] : (e as MouseEvent);
            /**
             * 구체적 의미
                draggingCatRef.current가 null이면
                아직 900ms 길게 누른 후에 드래그가 활성화되지 않은 상태
                이때 이동 거리가 8px 이상이면

                removeAndReset()를 호출해서 긴 누르기 타이머를 취소
                드래그 진입을 포기
                
                왜 필요한가?
                사용자가 길게 누르려다가 손이 조금 움직였을 때
                또는 스크롤/슬라이드 의도일 때
                그 동작을 “드래그 시작”으로 잘못 인식하지 않게 하기 위함
             */
            if (!draggingCatRef.current) {
                const dx = p.clientX - pressStartPos.current.x;
                const dy = p.clientY - pressStartPos.current.y;
                if (Math.sqrt(dx * dx + dy * dy) > 8) removeAndReset();
                return;
            }
            if (e instanceof TouchEvent && e.cancelable) e.preventDefault();
            setGhostPos({ x: p.clientX, y: p.clientY });
            setOverDeleteZone(isOverZone(p.clientX, p.clientY));
        };

        const onEnd = (e: MouseEvent | TouchEvent) => {
            const p = e instanceof TouchEvent
                ? (e as TouchEvent).changedTouches[0]
                : (e as MouseEvent);
            if (draggingCatRef.current && isOverZone(p.clientX, p.clientY)) {
                const catToDelete = draggingCatRef.current;
                onDelete(catToDelete);
                onFilterChange('all');
            }
            removeAndReset();
        };

        const removeAndReset = () => {
            document.removeEventListener('mousemove', onMove);
            document.removeEventListener('mouseup', onEnd);
            document.removeEventListener('touchmove', onMove);
            document.removeEventListener('touchend', onEnd);
            if (longPressTimer.current) {
                clearTimeout(longPressTimer.current);
                longPressTimer.current = null;
            }
            cleanupRef.current = null;
            draggingCatRef.current = null;
            setDraggingCat(null);
            setOverDeleteZone(false);
        };

        cleanupRef.current = removeAndReset;
        //900ms 동안 그대로 있으면 draggingCat를 설정해 실제 드래그 모드에 진입
        longPressTimer.current = setTimeout(() => {
            draggingCatRef.current = cat;
            setDraggingCat(cat);
            setGhostPos({ x: clientX, y: clientY });
        }, 900);

        document.addEventListener('mousemove', onMove);
        document.addEventListener('mouseup', onEnd);
        document.addEventListener('touchmove', onMove, { passive: false });
        document.addEventListener('touchend', onEnd);
    }, [onDelete, onFilterChange, isOverZone]);

 

        const removeAndReset = () => {
            document.removeEventListener('mousemove', onMove);
            document.removeEventListener('mouseup', onEnd);
            document.removeEventListener('touchmove', onMove);
            document.removeEventListener('touchend', onEnd);
            if (longPressTimer.current) {
                clearTimeout(longPressTimer.current);
                longPressTimer.current = null;
            }
            cleanupRef.current = null;
            draggingCatRef.current = null;
            setDraggingCat(null);
            setOverDeleteZone(false);
        };
함수 역할
preventDefault() 브라우저 기본 동작 막기

  • <a> 클릭 시 페이지 이동
  • <form> submit 시 새로고침
  • 우클릭 메뉴 표시
  • 드래그 시작
  • 스페이스바 스크롤
stopPropagation() 이벤트 전파 막기
// 버튼 밖으로 마우스가 나가도 드래그 유지하려면
// 특정 엘리먼트에만 걸면 마우스가 벗어나는 순간 드래그 끊김 ❌
element.addEventListener('mousemove', onMove);  // 엘리먼트 밖 나가면 중단
document.addEventListener('mousemove', onMove); // 어디서든 드래그 유지 ✅

 

'WEB개발 > REACT' 카테고리의 다른 글

unmount  (0) 2026.05.26
vite-plugin-route-meta  (0) 2026.01.15
pull to refresh  (0) 2026.01.12
react router (navigate)  (0) 2025.12.17
[hook] useCallback  (0) 2025.12.16