Background
KanbanWave
프로젝트 개발 중 드래그 앤 드롭(이하 DnD) 기능 구현을 위해 react-beautiful-dnd
라이브러리를 사용했다. 사용 중에 아이템을 DnD 했을 때, 아래 사진과 같은 에러가 발생했다.
이 에러 메시지는 특정 droppable 요소를 찾지 못할 때 발생한다.
검색해보니 react-beautiful-dnd
라이브러리가 아직 React 18의 StrictMode
를 지원하지 않아서 생기는 문제라고 한다.
Cause
Github issue 중에 아래와 같은 글을 발견했다.
react-beautiful-dnd
는 useLayoutEffect
을 사용하여 DnD 기능에 필요한 droppable 요소를 등록한다. 아마 이 과정에서 에러가 발생한 것이 아닐까 생각해본다.
- 컴포넌트가 처음 마운트될 때,
useLayoutEffect
가 실행되어 droppable 요소를 등록한다. StrictMode
가 활성화된 경우, React 컴포넌트를 재마운트한다.- 컴포넌트가 다시 마운트될 때
useLayoutEffect
가 다시 실행되어야 하지만, 의존성 배열이 변경되지 않았다면useLayoutEffect
가 실행되지 않을 수 있다. - 이로 인해서 droppable 요소가 제대로 등록되지 않아,
react-beautiful-dnd
가 해당 요소를 찾지 못하고 에러를 발생시킨다.
결과적으로, react-beautiful-dnd
가 droppable 요소를 관리하는 방식이 기대한대로 동작하지 않았거나, 타이밍 문제 때문에 droppable 요소가 등록되지 않는 것이다.
Solutions
몇 가지 해결 방법들이 논의되고 있다.
1. React.StrictMode 제거
전체 App 컴포넌트를 감싸고 있는 React.StrictMode
를 지우면 된다. 그런데 단지 이 라이브러리를 사용하기 위해 지우는 것이 해결책인지 잘 모르겠다.
StrictMode
에 대해서는 여기에서 자세히 다루었다.
2. Custom Droppable 컴포넌트 사용
import { useEffect, useState } from "react";
import { Droppable, DroppableProps } from "react-beautiful-dnd";
export const StrictModeDroppable = ({ children, ...props }: DroppableProps) => {
const [enabled, setEnabled] = useState(false);
useEffect(() => {
const animation = requestAnimationFrame(() => setEnabled(true));
return () => {
cancelAnimationFrame(animation);
setEnabled(false);
};
}, []);
if (!enabled) {
return null;
}
return <Droppable {...props}>{children}</Droppable>;
};
- 상태 관리:
enabled
라는 상태 변수를 사용하여Droppable
컴포넌트의 렌더링을 제어한다. useEffect
훅: 컴폰너트가 마운트딜 때requestAnimationFrame
을 사용하여setEnabled(true)
를 호출하고, 컴포넌트가 언마운트될 때cancelAnimationFrame
을 호출하여 상태를 초기화한다.- 조건부 렌더링:
enabled
가true
일 때만Droppable
컴포넌트를 렌더링한다. 이렇게 함으로써 컴포넌트가 첫 번째 렌더링 사이클 동안 마운트되지 않고, 다음 애니메이션 프레임 사이클에서 마운트된다.
먼저, 왜 이 방법으로 문제를 해결할 수 있는지를 알아보자.
requestAnimationFrame
을 사용하는 이유
requestAnimationFrame
은 브라우저의 다음 페인트(화면 갱신) 사이클이 시작되기 전에 콜백 함수가 실행되도록 예약할 수 있다. 이를 통해 컴포넌트가 완전히 마운트 된 후에 특정 작업을 수행할 수 있다. 즉, react-beautiful-dnd
가 드롭 가능한 항목을 등록하는 데 필요한 시간을 확보할 수 있는 것이다. 아! requestAnimationFrame에 대한 글은 여기에 자세히 정리되어 있다.
3. React 18을 지원하는 @hello-pangea/dnd
로 대체
(이 방법은 사실 2번을 적용한 후 알게 되었다.)
Atlassian가 더이상 react-beautiful-dnd
라이브러리를 지원하지 않는데, 고맙게도 대체할 수 있는 라이브러리가 나왔다. 오픈소스를 포크해서 새로 만든 hello-pangea/dnd
라는 라이브러리다.
가장 큰 변화는 React 18 지원, StrictMode 지원. 또한, 문법이 react-beautiful-dnd
와 같다고 한다. 이 밖에 자세한 내용은 Github issue에 나와있다.