request아이들Callback
오늘은 투두리스트 앱에 드래그&드랍을 적용하는 방법을 배웠다.
브라우저에서 드래그와 드롭 이벤트를 제공하고 이를 요소 간 데이터 전달에 이용할 수 있다.
기본적으로 draggable
요소에서 droppable
요소로의 전달이 가능하다.
요소를 draggable
로 지정하고 사용하려면 세 가지 과정이 필요하다.
요소에 draggable
속성을 true
로 설정한다.
이것만 해도 드래그 자체는 가능해지긴 한다. 이미지나 링크의 경우 기본값이 true로 설정되어 있다.
dragstart
이벤트에 대한 리스너를 추가한다.
이는 드래그가 시작할 때 발생하는 이벤트로 MouseEvent
의 하위 이벤트이다.
event.dataTransfer.setData
같은 형식으로 이벤트의 dataTransfer
객체에 전달하고자 하는 데이터를 설정한다.
데이터를 설정할 때는 데이터 값과 함께 text/plain
같은 데이터 형식도 지정해줘야 한다. 다만 데이터 형식이 키로 쓰이므로 같은 형식의 데이터를 여러 개 넣고 싶다면 추가 작업이 필요하다.
dataTransfer
객체에는 드래그 시 보일 이미지나 마우스 포인터 모양 설정도 가능하다.
요소를 droppable
로 지정하고 사용하려면 두 가지 과정이 필요하다.
요소에 dragover
이벤트에 대한 리스너를 추가해서 마우스 포인터로 알 수 있게 한다.
dataTransfer
객체의 dropEffect
를 "move"
로 설정해주는 등의 처리가 가능하다.
데이터 전달 여부에만 국한하면 꼭 필요한 과정은 아니지만 브라우저는 기본적으로 해당 속성이 "none"
으로 설정되어 있어 설정하지 않으면 사용자가 드랍할 수 있는 영역을 구분할 수 없다.
요소에 drop
이벤트에 대한 리스너를 추가하고 데이터를 받는다.
dataTransfer
객체의 getData
메서드를 사용해 받을 수 있다. 메서드의 인자로는 얻고자하는 데이터 형식을 지정해야 한다.
주의할 점으로 위 두 종류의 이벤트에 대해 브라우저의 기본 동작을 억제하는 코드를 넣어야한다. 이는 preventDefault
나 false
를 반환하는 방법으로 가능하다.
브라우저의 기본 동작으로 드랍을 받지 않거나 링크된 관련 페이지를 연다거나 하는 작업이 각 브라우저마다 다양하게 설정되어 있다. 부작용이 나타나지 않기 위해선 기본 동작을 막는 편이 좋다고 한다.
이름이 requestAnimationFrame
과 약간 비슷한데 반대의 역할을 갖고있다.
requestIdleCallback
은 꼭 시간에 맞춰 동작하지 않아도 되는 작업이 다른 필수적 작업들을 간섭하지 않게 스케쥴해주는 메서드로 전역 window
에 포함되어 있다.
자바스크립트는 기본적으로는 Call stack 기반의 싱글 스레드로 동작하므로 처리해야할 작업이 너무 많으면 일부 작업은 타이밍이 밀릴 수 있다. 그런데 화면 프레임에 맞게 UI를 업데이트 하거나 유저와의 상호작용을 처리하는 등의 작업의 경우 타이밍이 밀리면 UX 측면에서 좋지 않게 다가올 수 있다.
UI 업데이트의 경우 requsetAnimationFrame
이라는 별도의 기능으로 최우선순위를 주어 처리할 수 있지만 해당 작업이 끝나고 남은 시간을 일반 로직에서 제어하는 방법은 매우 복잡하다. 너무 긴 작업을 할당하면 다음 프레임 업데이트 작업에 영향을 미칠 수 있기 때문이다. 억지로 setTimeout
등의 비동기 함수를 사용하거나 사용자가 상호작용할 수 있는 모든 이벤트에 리스너를 달아 사용자가 상호작용 중이 아님을 판단해서 제어해야 한다.
그런데 작업이 실제 실행되는 환경인 브라우저 측에선 UI 업데이트 후 남은 시간을 아주 쉽게 알 수 있다. 때문에 브라우저의 window
객체에서 requestIdleCallback
기능을 제공하는 것이 자연스럽다.
프레임 간 UI 업데이트 후 남은 시간을 Idle period 라고 한다. 브라우저는 idle period가 되면 큐에 등록된 콜백들을 FIFO 순으로 스케쥴한다. 만약 UI 업데이트가 계획되어있지 않아 idle period가 길게 할당될 수 있다면 최대 50ms로 길이를 제한하고 연속해서 배치한다. 이를 통해 갑작스런 이벤트가 생겨나도 재빠르게 작업을 시작할 수 있도록 한다.
🔺 계획된 UI 업데이트가 없을 때 idle period 배치
등록된 콜백에는 idle period가 끝날 때 까지 남은 시간 정보를 포함하는 deadline
객체가 제공된다. 이를 이용해 Idle period 동안 작업을 다 끝내지 못했다면 해당 콜백을 다시 큐에 등록하는 코드를 추가하여 남은 작업을 처리하도록 구현할 수도 있다.
만약 처리해야할 작업이 너무 많아 idle period 자체가 나타나지 않게 된다면 requestIdleCallback
으로 등록된 콜백은 영원히 처리되지 못할 수 있다. 때문에 두 번째 인자로 timeout
속성을 가진 객체를 전달해서 대기할 수 있는 제한 시간을 지정해줄 수 있다. 제한 시간이 초과하면 우선 순위를 무시하고 콜백이 바로 실행된다. 이는 당연하게도 UX에 영향을 미치기 때문에 주의해서 사용해야 한다.
예전에 현업 개발자님의 조언에서 비동기 관련 프로세스 과정과 브라우저의 동작에 관해선 깊이 파보는 게 좋다는 내용을 들었는데 어쩌면 requestIdleCallback
이 둘 다 포함되지 않나 싶다. 그만큼 이해하기 어려웠고 참조 글도 계속 읽었는데 다른 내용들도 통합해서 정리해 봐야겠다.