[Vue3] 캔버스 편집 모드 드래그가 자꾸 원위치로 튀는 이유 - reactive 재렌더와 jQuery UI 충돌

2026. 5. 7. 10:44 · Dev/Vue

상황

도면을 띄우고 그 위에 lamp 같은 요소들을 자유롭게 배치하는 캔버스 편집 모드를 만들고 있었다.

요소 이동은 jQuery UI draggable 로 처리하는 레거시 코드 위에서 일하던 중이었다. 

편집 모드를 켜고 lamp 를 드래그하면 잠깐 따라오다가 다시 원위치로 튀는 현상이 있었다.                                                              

콘솔에 에러는 없고, jQuery UI 자체도 정상 동작하고, Vuex 도 멀쩡했다. 다른 페이지에서 같은 라이브러리로 만든 드래그는 잘 됐는데 이 화면만 이상했다.

CSS transition: none !important 까지 박아봐도 똑같았다. 무언가 외부에서 위치를 계속 덮어쓰고 있다는 감만 있었다.

 

 

원인 추적                                                                                                              

Vuex DevTools 를 켜고 드래그 중 어떤 mutation이 fire 되는지 봤다.

mousemove 한 번에 setHoveredDeviceId 가 수십 번 호출되고 있었다. 

 

캔버스 편집 모드에는 같은 디바이스에 속한 요소들끼리 hover 시 함께 하이라이트하는 기능이 있다.

마우스가 요소 위를 지나갈 때마다 hoveredDeviceId 가 

갱신 → 컴포넌트 재렌더 → :style="{ left: bodyX+'px', top: bodyY+'px' }" 가 다시 적용되고 있었다.

 

 

jQuery UI 가 라이브로 변경하고 있던 element.style.left 값을 Vue가 매 프레임 옛 bodyX 로 덮어쓰는 구조였다. 

도대체 이 사악한 코드는 왜

 

 

[mousemove]                                                                                                            

    → setHoveredDeviceId mutation                                                                                                                    

    → 컴포넌트 재렌더                                                                                                    

    → patchStyle (el.style.left = oldBodyX + "px")  ← 여기서 jQuery UI 가 만든 위치를 덮음                                                           

 

 

Vue 3 의 patchStyle 은 reactive 가 트리거되면 inline style 의 키를 다시 적용한다.

 

props의 bodyX/bodyY는 드래그가 끝나기 전까진 갱신이 안 되니 옛 값이 그대로 박혔다. 즉 Vue 입장에선 "변한 게 없는데?" 라는 게 정답이었지만, 그 "변한 게 없는" 좌표를 매 프레임 다시 박아넣는 게 jQuery UI 의 라이브 위치를 덮어쓰는 부작용을 만들었다.                     

 

 

해결

해결 방향은 두 가지였다.

                             

  1. 드래그 중에는 hover mutation 을 막기

  2. hover state 를 Vuex 밖으로 빼서 재렌더 자체를 안 일으키게 하기 

 

 

1번은 드래그 시작/종료를 감지해 mutation 을 일시 중단해야 하고, 다른 reactive 트리거가 또 끼어들면 같은 문제가 재발한다.                

2번이 더 깔끔했다. Vue 가 모르는 변화면 재렌더가 안 일어나기 때문이다.                                                                             

  // 기존 — Vuex commit으로 reactive 갱신 
  store.commit("setHoveredDeviceId", { deviceId });  
  
  
  // 수정 — DOM 속성 직접 토글
  document.querySelectorAll(`[device_id="${deviceId}"]`).forEach((el) => { 
  	el.setAttribute("data-device-hovered", "true");
  });

 

 

CSS 도 [data-device-hovered] 셀렉터로 반응하도록 바꿨다.                                                                              

.data-box-Item[data-device-hovered] > .point2_Inbox { 
	outline: 0.75px solid rgba(255, 255, 255, 0.7); 
}

 

DOM 속성 토글은 Vue reactive 시스템 밖이라 컴포넌트가 재렌더되지 않는다. 드래그 중 마우스가 요소 위를 지나가도 :style 이 다시 적용되지 않으니 jQuery UI 가 만든 위치가 그대로 살아남는다.                                                                            

                                                                                                                                                     

 

 

배운 점                   

reactive 시스템 안에는 "사용자가 의도한 영속 상태" 만 두는 편이 맞다는 걸 다시 느꼈다.

hover, 임시 마우스 위치, 스크롤 좌표같이 자주 변하지만 영속할 필요 없는 transient state 는 reactive 밖으로 빼야 외부 DOM 조작 라이브러리와 충돌이 안 난다.                    

 

같은 패턴은 Sortable.js, d3, Three.js, leaflet 같이 직접 DOM 을 조작하는 라이브러리들 모두에 적용된다.

Vue 의 reactive 가 그 라이브러리가 다루는 속성에 트리거를 거는 순간, 라이브 동작이 깨질 위험이 따라온다.  

저작자표시 비영리 변경금지 (새창열림)

'Dev > Vue' 카테고리의 다른 글

[Vue3] canvas에 Figma 같은 정렬 가이드 만들기 - transform 위에서 정확히 그리는 법  (0) 2026.05.08
[Vue3] ref(객체)에 watch 안 걸어도 되는 이유  (0) 2026.04.20
#Vue #Generic 안녕하세요 Next에서 넘어왔습니다  (0) 2026.03.06
'Dev/Vue' 카테고리의 다른 글
  • [Vue3] canvas에 Figma 같은 정렬 가이드 만들기 - transform 위에서 정확히 그리는 법
  • [Vue3] ref(객체)에 watch 안 걸어도 되는 이유
  • #Vue #Generic 안녕하세요 Next에서 넘어왔습니다
ming0o
ming0o
Jr. 프론트엔드 개발자의 성장(개발)일지입니다. 이전 : https://velog.io/@ming0o
  • ming0o
    주토피아
    ming0o
  • 전체
    오늘
    어제
    • 분류 전체보기
      • Goals
        • 2026 목표 및 월간 회고
      • 개발자로 살아남기
        • 트러블슈팅
        • 코드 리뷰
        • 기술 정리
      • Dev
        • Vue
        • Node & Express
        • React Native
      • Challenge
        • KDT-핀테크 인턴십
        • 한이음 드림업
      • Learning
        • JavaScript
        • Algorithm
        • CS
  • 블로그 메뉴

    • 홈
    • 태그
    • 미디어로그
    • 위치로그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    try-catch문
    지피티
    수동배포
    타입에러
    트러블슈팅
    Push
    Router
    replace
    이기고만다
    Vue
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
ming0o
[Vue3] 캔버스 편집 모드 드래그가 자꾸 원위치로 튀는 이유 - reactive 재렌더와 jQuery UI 충돌
상단으로

티스토리툴바