본문 바로가기

끄적끄적

V-Gesture 제작기 - 1. intro ~ prototyping

 

 

 

 

 

Intro.

개발 유투브 보다가 우연히 알고리즘에 의해 다음 영상을 보게 되었다.

https://www.youtube.com/watch?v=f7uBsb-0sGQ

 

 

tensorflow에서 제공하는 자바스크립트 모델(tfjs)을 활용하여 실시간으로 손의 위치를 측정해주는 모델인데 보다가 문득 손으로 웹과 인터렉션 할 수 있는 기능이 있으면 참 재밌겠다는 생각이 들었다.

 

먼저 Mac 유저인 본인으로써 트랙패드 제스처를 굉장히 애용하고 있고, 좀 더 나아가 제스처로써, 웹을 컨트롤 할 수 있으면 꽤나 편리하고 재밌는 경험을 줄 수 있을것 같아 한번 tfjs 기반 제스처를 이용한 인터렉션 라이브러리를 제작해 보기로 했다.

 

 

Goals & Settings

설계할 때, 가장 크게 염두해둔건 "확장의 용이함" 이었다. 물론 당연히 인터렉션 라이브러리기 때문에 웹과 인터렉션이 가능해야한다는건 너무 당연하기에 언급은 하지 않겠다.

 

확장을 중요히 생각하는 이유는 먼저 개인적으로 일일히 각 사용자들의 니즈에 맞는 인터렉션을 정의하는게 현실적으로 불가능하기 때문이다.

 

예를들어서, 현재 구현되어있는 'ClickGesture'의 경우, 검지와 엄지의 핀칭(꼬집기)를 통해 정의되는데, 해당 제스처 말고 다른 제스처로 클릭을 정의하고 싶을 수도 있고, 별 희한한 제스처를 구현하고 싶을 수도 있기 때문이다.

 

그래서 해당 모듈은 기본적으로 plugin 통해 제스처를 주입받을 수 있도록 설계되었다.

 

결국 플러그인에 의해 코어가 좌우되기에 breaking change가 발생할 경우, 직접적으로 gesture 구현체와 VGesture간 연결이 되있으면, 크리티컬하므로 이러한 케이스에 대비해 gesture 구현 레이어를 GesturePluginGesture 레이어 이렇게 두 개로 분리하여 breaking change에 대비할 수 있도록 추상 인터페이스를 만들었다.

 

 

 

 

 

여튼 일단 가장 일반적인 '클릭' 제스처를 목표로 만들기를 시작하게 되었다.

 

추가적으로 개발할 때, 최대한 유지되는 오픈소스 프로젝트처럼 운영해보고 싶어서 다른 오픈소스 프로젝트 보면서 이슈 관리든, pr이든 혼자서 북치고 장구치는 격이지만 따라했다 ㅋㅋㅋ.. 도중에 이게 뭐하는 짓이지 하면서 현타가 오긴했지만 결과적으로 봤을때 나름 깔끔하고 이뻐서 보기 좋으니 만족스럽다.

 

이슈 관리의 경우, git template 쓰는걸 매우 강추한다. 너무 편하고 내용도 알차게 채울 수 있다.

 

 

Progress.

 

먼저 시작한 작업은 prototyping이었다.

 

가장 중요한건 당연히 내 손을 인식하는 일이긴하지만, 이 부분은 결국 tfjs를 실행시키는 작업이므로 건너뛰고 

 

내가 생각하는 가장 0순위의 작업은 다음과 같다.

 

만약 제스처가 발생한 경우, 어떤식으로 DOM의 요소와 매핑할 것인가?

 

tfjs는 기본적으로 hand 데이터의 대한 정보를 기본적으로 뷰포트에 대해 절대 위치값으로 표현한다.

 

즉, 내 검지 손가락이 뷰포트의 좌측 최상단에 존재할 경우, `[0,0]`으로 표현된다는 뜻이다.

 

너무 슬프게도 DOM요소는 CSS의 "쌓임 맥락"에 의해 위치가 결정되기에, 만약 DOM요소의 뷰포트에 대한 절대 위치를 알고 싶다면 GetBoundingClientRect api 를 통해 절대 위치를 구해야한다.

 

다만 이 방법은 크나큰 문제가 있는데, 해당 api의 경우 호출 시 무조건적으로 layout thrashing이 발생한다는 점이다.

 

layout trashing이란 간단하게 말해서 DOM의 특정 속성 및 메서드를 호출 시, 기존 캐시값을 버리고 reflow를 수행하는 것인데, 문제는 모든 요소에 대해 절대 위치를 비교하기 위해 layout trashing을 발생시키면 유저입장에서 제스처가 수행될 때마다 main thread가 블로킹 되는 현상을 맛볼 수 있기에 이를 해결해야 했다.

 

https://github.com/dovigod/V-Gesture/issues/8

 

[Optimization] - prevent layout trashing · Issue #8 · dovigod/V-Gesture

Start Date 2024/05/15 Implementation PR #9 Reference Issues No response Summary Article On initialization phase, it will try to read all gClickable nodes, and bind its absolute position to itself b...

github.com

 

 

 

궁극적으로 이를 해결하기 위해 두 가지 방법이 사용되었는데 다음과 같다.

  1. 읽어야할 DOM 요소 수 자체를 줄인다.
  2. 최대한 읽기 작업을 일괄 처리할 수 있도록 한다.

 

먼저 기본적으로 모던 웹사이트들은 기본적으로 도큐먼트 하나당 기본 수십~수백개의 DOM요소를 가지고 있기에 그 수를 제한하는 것만으로도 굉장히 메리트 있다.

 

그럼, 디자인 이런거 다 버리고 코어만 남기면 되겠네요!

물론 그래도 되긴하지만.. 그런건 아니고 그냥 DOM 요소 선언 할 때마다 VGesture가 인식할 수 있는 속성값을 줌으로써 해결하였다.

 

<button> Cant click this</button>
<button vgesturable> can click</button>

 

 

2번 문제를 해결하기 위해 FastDom 라이브러리를 사용하였다.

해당 모듈의 경우, 작업을 등록 시, 특정 주기마다 작업들을 모아 일괄 처리해준다.

 

https://github.com/dovigod/V-Gesture/pull/9

 

[Feature & update] gClickable attribute & preventing layout trashing by dovigod · Pull Request #9 · dovigod/V-Gesture

Implementation During initialization phase, V-Gesture will recurse DOM subtree which holds body as root and try to find all elements with gClickable attribute. (there are some exception elements e...

github.com

 

 

 

이렇게 하더라도 아직 한가지 문제가 남는데, 만약 'vgesturable'요소가 너무 많은 경우이다.

 

애당초 vgesturable 요소가 너무 많으면 연산량이 너무 많다. VGesture 구동을 한다는 건 꽤나 많은 자원들을 소비하는 것이기에 (canvas 에 실시간으로 그리기, mediastream을 통한 손 인식 etc..) 매 이벤트마다 타겟 요소를 검색하는 작업이 상당히 부담스럽다.

 

기존에는 단순히 vgesturable DOM요소를 배열에 담고 매 이벤트마다 선형 검색을 수행하였는데, 이렇게 하니 다음 이슈가 터졌다.

 

오타 : k-d tree x -> array

 

10000번 수행시, 걸리는 시간이 layout trashing 까지 합해서 4.4초나 걸렷다.

 

물론 10000개 등록할 사람은 없긴?하겠지만 명백한 한계점이었고 최적화할 필요성을 느꼈다.

 

사실 이 부분이 개발할 때 제일 재밌었던 부분중 하나인데,

 

이 문제는 결국 2차원 평면상에서 특정 점이 주어진 경우 가장 가까운 점을 찾는 일종의 탐색 문제이다.

 

처음에는 x,y축에 대해 여러번 binary search를 진행해볼까? 하는 생각을 하였는데,  좀 더 리서치 해본 결과 일반적으로 컴퓨터 비전 분야쪽에서 사용하는 자료구조가 있길래 해당 자료구조들을 중점적으로 리서치 하였다.

 

먼저 나에겐 두 가지 옵션이 있었는데 다음과 같다.

  1. Quad Tree
  2. K-d Tree

 

결과적으로 채택한 자료 구조는 K-d Tree 인데, 먼저 K-d Tree가 확장성에 용이하단 점이다.

 

현재까지 제스처를 구현함에 있어서 2차원 데이터만으로 충분하지만, 추후에 제스처가 충분히 3차원 데이터를 사용할 가능성이 있기 때문이다.

 

만약 quad tree를 채택한다면, 3차원 데이터일 경우, octa tree를 따로 구현해야하고, 4차원일경우 hexa tree(물론 이 경우는 없다)를 구현해야하지만, K-d tree는 그 자체로 확장에 열려있기에 채택했다.

 

결과적으로 해당 자료구조를 채택해서 4.4초였던 연산을 0.16초로 드라마틱하게 줄일 수 있었다.

 

 

 

https://github.com/dovigod/V-Gesture/pull/6

 

[Optimization] K d tree for searching closest g clickable element by dovigod · Pull Request #6 · dovigod/V-Gesture

Implementation K-d tree impl for searching g-clickable elements when clickGesture event occurs Notes From To Etc N/A Checklist before merge N/A

github.com

 

 

 

이제 prototyping의 마지막 단계인 제스처의 구현만이 남았다.

 

먼저 제스처는 검지와 엄치의 꼬집음으로 표현했다.

 

사실 원래 단순히 포인팅하는 걸로 표현하고 싶긴한데, 도저히 어떻게 표현해야할질 모르겠어서 꼬집는 형태로 가닥을 잡았다.

 

굳이 3차원 데이터 까진 필요없어서 단순히 검지와 엄지의 거리가 특정 임계점 이하인 경우, ClickGuestureEvent를 발생(Dispatch)시켰다. 

 

여기에 약간의 이벤트 발생에 debouncing만 시켜줌으로써 완성하였다.

 

 

(주의) 프로토타이핑이라 굉장히 코드 지저분함

https://github.com/dovigod/V-Gesture/pull/2/files

 

[Feature] Click Gesture proto by dovigod · Pull Request #2 · dovigod/V-Gesture

Implementation Click Gesture proto implemented. Click Gesture is defined as attatching index, thumb finger together. Todos Awaring of performance issues, currently I'm using 2-dimensional factor...

github.com