React Native로 드래그 가능한 카드를 만들다가 애니메이션이 뚝뚝 끊기는 걸 겪었다. Animated API에 PanResponder를 붙여서 setState로 위치를 갱신하는 방식이었는데, 리스트를 렌더링하거나 네트워크 응답을 처리하는 순간 손가락 따라오던 카드가 한 박자 늦게 움직였다.
원인은 스레드다. 기존 방식은 제스처 값이 JS 스레드를 거쳐서 네이티브로 넘어간다. JS 스레드가 다른 작업으로 바쁘면 프레임이 밀린다. Reanimated 3는 이걸 워크릿(worklet) 으로 해결한다. 애니메이션 로직을 UI 스레드에서 직접 돌려서 JS 스레드가 뭘 하든 60fps를 유지한다.
설치
npx expo install react-native-reanimated react-native-gesture-handler
babel.config.js에 플러그인을 추가해야 한다. 이걸 빼먹으면 워크릿이 동작하지 않는다.
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: ['react-native-reanimated/plugin'], // 반드시 마지막에
};
};
앱 최상단은 GestureHandlerRootView로 감싸준다.
드래그 카드 구현
핵심은 useSharedValue로 위치 값을 만들고, Gesture.Pan() 콜백 안에서 그 값을 직접 갱신하는 것이다. 콜백은 워크릿이라 UI 스레드에서 실행된다.
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, {
useSharedValue,
useAnimatedStyle,
withSpring,
} from 'react-native-reanimated';
function DraggableCard() {
const x = useSharedValue(0);
const y = useSharedValue(0);
const pan = Gesture.Pan()
.onChange((e) => {
x.value += e.changeX;
y.value += e.changeY;
})
.onEnd(() => {
// 손을 떼면 스프링 애니메이션으로 원위치
x.value = withSpring(0);
y.value = withSpring(0);
});
const style = useAnimatedStyle(() => ({
transform: [{ translateX: x.value }, { translateY: y.value }],
}));
return (
<GestureDetector gesture={pan}>
<Animated.View style={[styles.card, style]} />
</GestureDetector>
);
}
useAnimatedStyle도 워크릿이다. x.value가 바뀔 때마다 UI 스레드에서 스타일을 다시 계산하니까, setState나 리렌더링이 전혀 일어나지 않는다. 손가락을 아무리 빠르게 움직여도 카드가 딱 붙어 따라온다.
JS 함수를 부르려면 runOnJS
한 가지 함정. 워크릿 안에서 일반 JS 함수를 그냥 호출하면 크래시가 난다. 예를 들어 스와이프로 카드를 삭제하고 상태를 갱신하려면 runOnJS로 감싸야 한다.
.onEnd((e) => {
if (Math.abs(e.translationX) > 120) {
runOnJS(onDismiss)(); // JS 스레드로 넘겨서 실행
} else {
x.value = withSpring(0);
}
});
워크릿은 UI 스레드에 있고 onDismiss 같은 setState 로직은 JS 스레드에 있으니, 다리를 놓아주는 게 runOnJS다.
PanResponder + setState에서 이 조합으로 갈아탄 뒤로 드래그가 밀리는 현상이 사라졌다. 포인트는 두 개다. 위치 값은 useSharedValue로 관리해서 리렌더링을 피할 것, 그리고 JS 상태를 건드릴 땐 runOnJS를 잊지 말 것. 이 둘만 지키면 스와이프·바텀시트·핀치줌 같은 제스처를 전부 같은 패턴으로 만들 수 있다.