๐ŸงŠ 40. React Native ๋ชจ๋‹ฌ ์™„์ „ ์ •๋ฆฌ โ€” ๊ธฐ๋ณธ Modal๋ถ€ํ„ฐ BottomSheet, Portal๊นŒ์ง€

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

๋ชจ๋ฐ”์ผ ์•ฑ์—์„œ ๋ชจ๋‹ฌ์€ ์ •๋ง ์ž์ฃผ ์“ฐ์ธ๋‹ค.

  • ์•ˆ๋‚ด์ฐฝ(Alert)
  • ์‚ฌ์šฉ์ž ๋™์˜, ํ™•์ธ
  • ์˜ต์…˜ ์„ ํƒ
  • ํ•˜๋‹จ ์‹œํŠธ (BottomSheet)

React Native์—์„œ๋„ ๊ธฐ๋ณธ <Modal />์ด ์žˆ์ง€๋งŒ,
์‹ค์ œ๋กœ ์•ฑ์„ ๋งŒ๋“ค๋‹ค ๋ณด๋ฉด ํ•˜๋‹จ์—์„œ ์˜ฌ๋ผ์˜ค๋Š” ์‹œํŠธ, ์ค‘์ฒฉ๋œ ๋ชจ๋‹ฌ, ๋‹คํฌ ๋ฐฐ๊ฒฝ ์ฒ˜๋ฆฌ ๋“ฑ
๊ธฐ๋ณธ ๊ธฐ๋Šฅ๋งŒ์œผ๋กœ๋Š” ๋ถ€์กฑํ•œ ์ƒํ™ฉ์ด ์ž์ฃผ ์ƒ๊ธด๋‹ค.

์ด๋ฒˆ ๊ธ€์€ React Native์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”
๋ชจ๋‹ฌ ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ• 3๊ฐ€์ง€๋ฅผ ์ •๋ฆฌํ–ˆ๋‹ค.


โœ… 1. ๊ธฐ๋ณธ Modal ์ปดํฌ๋„ŒํŠธ

import { Modal, View, Text, Button } from 'react-native';

<Modal
  visible={isVisible}
  transparent={true}
  animationType="slide"
  onRequestClose={() => setIsVisible(false)}
>
  <View style={styles.overlay}>
    <View style={styles.modalBox}>
      <Text>์•ˆ๋‚ด ๋ฉ”์‹œ์ง€</Text>
      <Button title="๋‹ซ๊ธฐ" onPress={() => setIsVisible(false)} />
    </View>
  </View>
</Modal>

ํŠน์ง•

  • visible๋กœ ์—ด๊ณ  ๋‹ซ์Œ
  • transparent ์„ค์ • ์‹œ ๋ฐฐ๊ฒฝ ์–ด๋‘ก๊ฒŒ ๊ฐ€๋Šฅ
  • iOS์—์„œ ์Šค์™€์ดํ”„๋กœ ๋‹ซ๊ธฐ ์ง€์› (onRequestClose)

โœ… ๊ฐ„๋‹จํ•œ ์•Œ๋ฆผ, ํ™•์ธ์ฐฝ์— ์œ ์šฉ
โŒ ์ค‘์ฒฉ ๋ชจ๋‹ฌ, ์ปค์Šคํ…€ UI ์ œ์•ฝ ๋งŽ์Œ


๐Ÿ’ก ์Šคํƒ€์ผ ํŒ

const styles = StyleSheet.create({
  overlay: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: 'rgba(0,0,0,0.5)',
  },
  modalBox: {
    backgroundColor: 'white',
    padding: 24,
    borderRadius: 10,
    width: '80%',
  },
});

๐Ÿ“ฅ 2. BottomSheet ๋ฐฉ์‹ (react-native-modalize ๋“ฑ)

npm install react-native-modalize
import { Modalize } from 'react-native-modalize';

const modalRef = useRef<Modalize>(null);

<Modalize ref={modalRef} snapPoint={300}>
  <Text>์˜ต์…˜ ๋ชฉ๋ก</Text>
</Modalize>
  • ํ•˜๋‹จ์—์„œ ์˜ฌ๋ผ์˜ค๋Š” ์‹œํŠธ ๊ตฌ์กฐ
  • ํ„ฐ์น˜ ์ œ์Šค์ฒ˜, snapPoint ์„ค์ • ๋“ฑ ํ’๋ถ€ํ•œ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•
  • UI/UX์ ์œผ๋กœ ํ›จ์”ฌ ์ž์—ฐ์Šค๋Ÿฌ์›€

โœ… ์นด์นด์˜คํ†ก, ์ธ์Šคํƒ€, ์œ ํŠœ๋ธŒ ๋“ฑ ์ฃผ์š” ์•ฑ์ด ์‚ฌ์šฉ
โœ… ๋ชจ๋‹ฌ์ฒ˜๋Ÿผ UX๋ฅผ ์ฐจ๋‹จํ•˜์ง€ ์•Š๊ณ ๋„ ๋ ˆ์ด์–ด ์ œ๊ณต ๊ฐ€๋Šฅ
โŒ ์„ค์น˜ ํ•„์š”, ์ปจํŠธ๋กค ๋กœ์ง ์ถ”๊ฐ€๋จ


๐ŸŒ€ 3. Portal ๋ฐฉ์‹ (react-native-paper ๋“ฑ)

import { Portal, Modal } from 'react-native-paper';

<Portal>
  <Modal visible={visible} onDismiss={() => setVisible(false)}>
    <Text>๋‹ค๋ฅธ ์ปดํฌ๋„ŒํŠธ ์œ„์— ๋– ์žˆ๋Š” ๋ชจ๋‹ฌ</Text>
  </Modal>
</Portal>
  • ํ™”๋ฉด ์–ด๋””์— ์žˆ๋“ , ์ตœ์ƒ์œ„ ๋ ˆ์ด์–ด๋กœ ๋ชจ๋‹ฌ์„ ๋„์›€
  • ์ค‘์ฒฉ ๊ตฌ์กฐ์—์„œ๋„ ๋ชจ๋‹ฌ ์œ„์น˜ ์ถฉ๋Œ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์Œ

โœ… ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ์—์„œ ๋ชจ๋‹ฌ ์ปจํŠธ๋กค ์‹œ ์œ ์šฉ
โœ… ๋‹คํฌ ์˜ค๋ฒ„๋ ˆ์ด, ๋”ค ์ฒ˜๋ฆฌ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๊ฐ€๋Šฅ
โŒ paper ์Šคํƒ€์ผ์— ์ข…์†๋จ


โš–๏ธ ๋น„๊ต ์š”์•ฝ

๋ฐฉ์‹ํŠน์ง•์ถ”์ฒœ ์‚ฌ์šฉ์ฒ˜
๊ธฐ๋ณธ <Modal>RN ๊ธฐ๋ณธ ์ œ๊ณต, ๋‹จ์ˆœ ํŒ์—…์•Œ๋ฆผ, ํ™•์ธ, ๊ฐ„๋‹จํ•œ ์ž…๋ ฅ
BottomSheetUX์— ์ตœ์ , ์ปค์Šคํ„ฐ๋งˆ์ด์ง• ๊ฐ•๋ ฅ์˜ต์…˜ ์„ ํƒ, ์•ก์…˜ ์‹œํŠธ
Portal์œ„์น˜ ๋ฌด๊ด€ํ•˜๊ฒŒ ๋„์›€์ค‘์ฒฉ ๋ชจ๋‹ฌ, ๊ธ€๋กœ๋ฒŒ ์ฒ˜๋ฆฌ

๐Ÿง  ์‹ค์ „ ํŒ

  • ๋‹ซ์„ ๋•Œ๋Š” onRequestClose ๋˜๋Š” setVisible(false)๋กœ ์ƒํƒœ ๋ณ€๊ฒฝ
  • KeyboardAvoidingView์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ ์‹œ ์ž…๋ ฅ์ฐฝ ๊ฐ€๋ฆผ ๋ฐฉ์ง€
  • backdrop ํ„ฐ์น˜ ์‹œ ๋‹ซํžˆ๋Š” ๊ธฐ๋Šฅ: TouchableWithoutFeedback ์ฒ˜๋ฆฌ or ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์˜ต์…˜ ์‚ฌ์šฉ
  • ์—ฌ๋Ÿฌ ๋ชจ๋‹ฌ ๊ฒน์น˜์ง€ ์•Š๋„๋ก ๋ชจ๋‹ฌ ์ƒํƒœ ๊ด€๋ฆฌ ์ž˜ ๋ถ„๋ฆฌํ•  ๊ฒƒ

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

์ฒ˜์Œ์—๋Š” RN์˜ <Modal />๋งŒ ์จ๋„ ์ถฉ๋ถ„ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋Š”๋ฐ,
์‹ค์ œ๋กœ ์•ฑ์„ ๋งŒ๋“ค๋‹ค ๋ณด๋ฉด ๋ชจ๋‹ฌ์ด ๊ฒน์น˜๊ณ ,
ํ•˜๋‹จ ์‹œํŠธ๊ฐ€ ํ•„์š”ํ•˜๊ณ , ๊ธ€๋กœ๋ฒŒํ•˜๊ฒŒ ํ˜ธ์ถœ๋ผ์•ผ ํ•˜๊ณ โ€ฆ
๊ทธ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ๋„๊ตฌ๋‚˜ ํŒจํ„ด์ด ํ•„์š”ํ•˜๋‹ค๋Š” ๊ฑธ ๊นจ๋‹ฌ์•˜๋‹ค.

์ง€๊ธˆ์€ ์•„๋ž˜ ๊ตฌ์กฐ๋กœ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ์–ด:

  • ๋‹จ์ˆœ ์•ˆ๋‚ด์ฐฝ โ†’ Modal
  • ์„ ํƒ/์„ค์ • ๋“ฑ โ†’ Modalize
  • ์ „์—ญ ์•Œ๋ฆผ โ†’ Portal + ์ƒํƒœ๊ด€๋ฆฌ

๐ŸงŠ "๋ชจ๋‹ฌ์€ ๋‹จ์ˆœํ•œ UI๊ฐ€ ์•„๋‹ˆ๋‹ค. ์•ฑ์˜ ํ๋ฆ„์„ ๋Š์ง€ ์•Š๋Š” UX ์ „๋žต์ด๋‹ค."


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

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