Post

상태 머신으로 푸는 캔버스 인터랙션 - HoneyFlow 개발기 (4)

상태 머신과 이벤트 모델을 바탕으로 2초 홀드 후 드래그 인터랙션을 구현하며 mouseLeave 한계, 거리 기반 판별, Konva 애니메이션 피드백, 커스텀 훅 설계를 정리했습니다.

상태 머신으로 푸는 캔버스 인터랙션 - HoneyFlow 개발기 (4)

0. Abstract

  • 이 글은 ‘2초간 홀드 후 드래그하여 노드를 이동’시키는 복잡한 인터랙션을 구현한 경험을 다룹니다.
  • 이 과정에서 발생한 mouseLeave 이벤트의 동작 제약기존 드래그 로직과의 충돌이라는 두 가지 핵심 기술 과제를 해결한 과정을 공유합니다.
  • 상태 머신(State Machine) 관점에서 인터랙션을 모델링하고, 요소 간 거리 계산을 통해 이벤트의 한계를 극복하며, 커스텀 훅을 활용해 상태와 로직을 캡슐화한 아키텍처를 설명합니다.

1. 요구사항 분석 및 상태 모델링

주어진 과제는 “이미 생성된 노드를 2초간 홀드 후 드래그 앤 드롭으로 이동시킨다”였습니다. 이 인터랙션을 사용자 시나리오에 따라 분석하면, 노드는 다음과 같은 상태를 가질 수 있습니다.

  • IDLE (기본): 아무런 상호작용이 없는 상태
  • HOLDING (홀드 중): 사용자가 mouseDown 후 2초가 지나지 않은 상태
  • MOVING (이동 가능): 2초 홀드를 성공하여 드래그로 위치를 변경할 수 있는 상태

이처럼 인터랙션을 유한한 상태의 전환으로 모델링하면, 각 상태에서 처리해야 할 로직과 상태 간 전환 조건을 명확히 정의할 수 있어 복잡한 문제를 체계적으로 해결할 수 있습니다.


2. 핵심 과제 1: ‘홀드’ 상태 감지 및 취소 처리

1) setTimeout을 이용한 상태 전환 예약

IDLE 상태에서 HOLDING 상태를 거쳐 MOVING 상태로 전환하기 위해, mouseDown 이벤트 발생 시 setTimeout을 사용했습니다. 2초(HOLD_DURATION) 후에 isMoving 상태를 true로 변경하는 콜백을 등록하여 ‘상태 전환’을 예약합니다.

2) 기술적 난관: 드래그 중 mouseLeave 이벤트의 부재

img

HOLDING 상태를 취소하는 조건은 ‘2초가 지나기 전에 사용자가 마우스 포인터를 노드 밖으로 옮기는 것’입니다. 처음에는 mouseLeave 이벤트를 사용해 이를 감지하려 했습니다.

하지만 mouseLeave드래그 제스처가 시작되면(즉, 마우스 버튼이 눌린 상태로 움직이면) 발생하지 않는 특성이 있습니다. 브라우저는 드래그가 시작된 요소를 기준으로 이벤트를 계속 전파하기 때문입니다. 따라서 이 방법으로는 홀드 취소를 정확히 감지할 수 없었습니다.

3) 해결책: 기하학적 거리 계산

img

이벤트의 한계를 극복하기 위해, 마우스 포인터의 실시간 좌표를 이용하는 방식으로 접근했습니다. 드래그 이벤트가 발생할 때마다 포인터의 좌표와 원형 노드의 중심 좌표 사이의 거리를 계산했습니다.

이 방법은 특정 이벤트에 의존하지 않고 좌표라는 순수 데이터를 기반으로 판단하므로, 드래그 중에도 안정적으로 ‘노드 이탈’을 감지할 수 있는 견고한 해결책이 되었습니다.


3. 핵심 과제 2: 드래그 로직 충돌 해결 아키텍처

우리 서비스에는 이미 ‘드래그를 통한 노드 생성’ 로직이 존재했습니다. 따라서 ‘홀드 후 이동’ 로직은 기존 기능과 충돌할 가능성이 있었습니다.

이 문제는 앞서 정의한 isMoving 상태를 통해 해결했습니다. 모든 드래그 관련 상태와 이벤트 핸들러를 useDragAndMove라는 커스텀 훅으로 캡슐화했습니다. 다른 드래그 로직(e.g., 노드 생성)에서는 이 훅에서 노출된 moveState.isMoving 값을 확인하여, true일 경우 로직을 실행하지 않도록 분기 처리를 추가했습니다.

이처럼 커스텀 훅을 통해 관심사를 분리(Separation of Concerns)하고 상태에 따라 로직의 동작을 제어함으로써, 기능 간의 충돌을 방지하고 예측 가능한 코드를 작성할 수 있었습니다.


4. 사용자 경험(UX) 개선: 시각적 피드백 제공

기능 구현 후 사용자 입장에서 테스트해보니, 2초간 노드를 누르고 있을 때 아무런 시각적 변화가 없어 현재 시스템이 내 입력을 인지하고 있는지 알기 어려웠습니다. 이는 좋은 UX가 아닙니다.

사용자에게 ‘홀드가 진행 중’임을 명확히 알려주기 위해 Konva의 애니메이션 기능을 활용했습니다. HOLDING 상태가 되면 노드에 그림자 효과(shadowBlur)를 점진적으로 추가하고, 홀드가 취소되면 효과를 제거하는 애니메이션을 적용했습니다. 이러한 시각적 피드백은 사용자가 시스템의 상태를 직관적으로 이해하고 다음 행동을 예측하는 데 큰 도움을 줍니다.


5. 결론 및 회고

img

‘홀드 앤 드래그’라는 단순해 보이는 인터랙션 뒤에는 브라우저 이벤트 모델의 특성, 상태 관리의 복잡성, 그리고 사용자 경험에 대한 고려가 숨어있었습니다.

문제를 작은 단위로 나누고 상태를 명확히 정의하는 접근법을 통해 복잡한 요구사항을 체계적으로 구현할 수 있었습니다. 다만, 여러 인터랙션이 추가되면서 이벤트 핸들러를 등록하는 코드가 비대해지는 경향이 있었습니다. 향후에는 이벤트 핸들러 등록 로직을 별도의 모듈로 분리하여, 각 컴포넌트의 책임을 더욱 명확하게 하는 방향으로 리팩토링할 계획입니다.

This post is licensed under CC BY 4.0 by the author.