๐Ÿ‘† 54. React Native ์Šค์™€์ดํ”„ ์ œ์Šค์ฒ˜ ๊ตฌํ˜„ ์ „๋žต โ€” Swipeable, PanGesture, BottomSheet๊นŒ์ง€

JM_Devยท2025๋…„ 6์›” 18์ผ
0
post-thumbnail

๋ชจ๋ฐ”์ผ ์•ฑ์˜ ํ•ต์‹ฌ UX ์ค‘ ํ•˜๋‚˜๋Š”
ํ™”๋ฉด์„ ์†๊ฐ€๋ฝ์œผ๋กœ ๋ฐ€์–ด์„œ(interact) ๋ฌด์–ธ๊ฐ€๋ฅผ ์กฐ์ž‘ํ•˜๋Š” ์ œ์Šค์ฒ˜๋‹ค.

ํŠนํžˆ ์Šค์™€์ดํ”„(Swipe) ์ œ์Šค์ฒ˜๋Š”

  • ๋ฆฌ์ŠคํŠธ ํ•ญ๋ชฉ ์‚ญ์ œ
  • ์ด๋ฏธ์ง€ ์Šฌ๋ผ์ด๋“œ
  • ๋ฐ”ํ…€์‹œํŠธ ์—ด๊ณ  ๋‹ซ๊ธฐ
    ๋“ฑ ๋‹ค์–‘ํ•œ ์ƒํ™ฉ์—์„œ ์‚ฌ์šฉ๋œ๋‹ค.

์ด๋ฒˆ ๊ธ€์€ React Native์—์„œ ์Šค์™€์ดํ”„ ์ œ์Šค์ฒ˜๋ฅผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๋Š” ๋‹ค์–‘ํ•œ ๋ฐฉ๋ฒ•์„ ์ •๋ฆฌํ•œ ๊ธ€์ด๋‹ค.


โœ… 1. react-native-gesture-handler ์„ค์น˜

npm install react-native-gesture-handler

ํ•„์ˆ˜์ ์œผ๋กœ ์„ค์น˜ํ•ด์•ผ ํ•˜๋Š” ๊ธฐ๋ณธ ์ œ์Šค์ฒ˜ ํ•ธ๋“ค๋Ÿฌ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
๋Œ€๋ถ€๋ถ„์˜ ์Šค์™€์ดํ”„ ๊ธฐ๋Šฅ์€ ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘๋™ํ•จ


๐Ÿ”„ 2. ๋ฆฌ์ŠคํŠธ ํ•ญ๋ชฉ ์Šค์™€์ดํ”„ (Swipeable)

import { Swipeable } from 'react-native-gesture-handler';

<Swipeable
  renderRightActions={() => (
    <TouchableOpacity style={styles.deleteBox}>
      <Text style={styles.deleteText}>์‚ญ์ œ</Text>
    </TouchableOpacity>
  )}
>
  <View style={styles.listItem}>
    <Text>ํ•  ์ผ ํ•ญ๋ชฉ</Text>
  </View>
</Swipeable>

โœ… ์ขŒ/์šฐ ์•ก์…˜ ๊ตฌํ˜„ ๊ฐ€๋Šฅ
โœ… onSwipeableOpen์œผ๋กœ ์•ก์…˜ ์ฒ˜๋ฆฌ
โŒ ์—ฌ๋Ÿฌ ํ•ญ๋ชฉ์ด ๋™์‹œ์— ์—ด๋ฆฌ๋Š” ๋ฌธ์ œ โ†’ ์™ธ๋ถ€ ์ œ์–ด ํ•„์š”


๐Ÿ“ฆ 3. ํ•˜๋‹จ ์Šฌ๋ผ์ด๋”ฉ ๋ฐ”ํ…€์‹œํŠธ

์ œ์Šค์ฒ˜๋กœ ์œ„/์•„๋ž˜๋กœ ์›€์ง์ด๋Š” ์ปดํฌ๋„ŒํŠธ

react-native-modalize ๋˜๋Š” reanimated-bottom-sheet ์‚ฌ์šฉ

<Modalize ref={modalRef} snapPoint={300}>
  <Text>์˜ต์…˜ ๋ชฉ๋ก</Text>
</Modalize>

โœ… ํ„ฐ์น˜ ์ œ์Šค์ฒ˜ + ๋ฐ”์šด์Šค ํšจ๊ณผ
โœ… scroll, dismiss ์ด๋ฒคํŠธ ๋ชจ๋‘ ์ง€์›
โœ… ์Šค์™€์ดํ”„ UX ๊ตฌํ˜„์— ํŠนํ™”


๐Ÿงฒ 4. ์ปค์Šคํ…€ ์Šค์™€์ดํ”„ (Reanimated + PanGesture)

import {
  PanGestureHandler,
  GestureHandlerRootView,
} from 'react-native-gesture-handler';
import Animated, {
  useSharedValue,
  useAnimatedGestureHandler,
  useAnimatedStyle,
  withSpring,
} from 'react-native-reanimated';

const translateX = useSharedValue(0);

const panGesture = useAnimatedGestureHandler({
  onActive: (event) => {
    translateX.value = event.translationX;
  },
  onEnd: () => {
    translateX.value = withSpring(0);
  },
});

const animatedStyle = useAnimatedStyle(() => ({
  transform: [{ translateX: translateX.value }],
}));

<GestureHandlerRootView>
  <PanGestureHandler onGestureEvent={panGesture}>
    <Animated.View style={[styles.box, animatedStyle]} />
  </PanGestureHandler>
</GestureHandlerRootView>

โœ… ์ž์œ ๋„ ๋†’์€ ์ œ์Šค์ฒ˜ ๊ตฌํ˜„
โœ… UI ์ปดํฌ๋„ŒํŠธ์˜ ์ง์ ‘์ ์ธ ์›€์ง์ž„ ์ œ์–ด ๊ฐ€๋Šฅ
โœ… ๋“œ๋ž˜๊ทธ, ์Šค์™€์ดํ”„, ์Šฌ๋ผ์ด๋“œ ๋“ฑ ๋ชจ๋“  ์œ ํ˜• ๊ตฌํ˜„ ๊ฐ€๋Šฅ
โŒ ๋Ÿฌ๋‹ ์ปค๋ธŒ ์กด์žฌ


๐Ÿ”ง ์Šค์™€์ดํ”„ ์ œ์Šค์ฒ˜ ์„ค๊ณ„ ํŒ

ํ•ญ๋ชฉ์ „๋žต
๋ฆฌ์ŠคํŠธ ํ•ญ๋ชฉ ์‚ญ์ œSwipeable ์ปดํฌ๋„ŒํŠธ ํ™œ์šฉ
๋ฐ”ํ…€์‹œํŠธ ์ œ์Šค์ฒ˜Modalize, Reanimated Bottom Sheet
์ด๋ฏธ์ง€ ์Šฌ๋ผ์ด๋“œScrollView horizontal, ๋˜๋Š” PanGesture
์ œ์Šค์ฒ˜ ์ค‘๋ณต ๋ฐฉ์ง€GestureHandler context ์ œํ•œ or exclusive ์ฒ˜๋ฆฌ
์ข…๋ฃŒ ์• ๋‹ˆ๋ฉ”์ด์…˜withSpring, withTiming ํ™œ์šฉํ•ด์„œ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ

๐Ÿ“ ๋‚ด๊ฐ€ ๋А๋‚€ ์ 

์ดˆ๊ธฐ์—๋Š” ๋ชจ๋“  ์Šค์™€์ดํ”„ UI๋ฅผ ๊ทธ๋ƒฅ ScrollView๋กœ ์ฒ˜๋ฆฌํ–ˆ๋Š”๋ฐ,
UX๊ฐ€ ์–ด์ƒ‰ํ•˜๊ณ , ๋™์ž‘์ด ๊ธฐ๊ธฐ๋งˆ๋‹ค ๋‹ค๋ฅด๊ฒŒ ๋А๊ปด์กŒ๋‹ค.

์ง€๊ธˆ์€ Swipeable๋กœ ๊ธฐ๋ณธ ์ œ์Šค์ฒ˜ ์ฒ˜๋ฆฌํ•˜๊ณ ,
ํ•˜๋‹จ ์ธํ„ฐ๋ž™์…˜์€ Modalize + Reanimated๋กœ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ์ฒ˜๋ฆฌํ•˜๊ณ  ์žˆ๋‹ค.
์ง์ ‘ ์ œ์–ด๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ์—” PanGesture๋ฅผ ์กฐํ•ฉํ•ด์„œ ๊ณ ๊ธ‰ UI๋ฅผ ๋งŒ๋“ ๋‹ค.


๐Ÿ‘† โ€œ์Šค์™€์ดํ”„๋Š” UI๊ฐ€ ์•„๋‹ˆ๋ผ, ๊ฐ๊ฐ์ด๋‹ค. ์•ฑ์˜ ์†๋ง›์€ ์—ฌ๊ธฐ์„œ ๋‚˜์˜จ๋‹ค.โ€


profile
๊ฐœ๋ฐœ์ž๋กœ ์ทจ์—…์„ ์ค€๋น„ ์ค‘ ์ด๋ฉฐ, ์—ด์‹ฌํžˆ ๊ณต๋ถ€ ์ค‘ ์ž…๋‹ˆ๋‹ค!

0๊ฐœ์˜ ๋Œ“๊ธ€