English | ํ๊ตญ์ด
React Native์์ ์ด๋ฏธ์ง ๊ฐค๋ฌ๋ฆฌ๋ ์ฝํ ์ธ ๋ทฐ์ด๋ฅผ ๊ตฌํํ ๋, ๋ณต์กํ ์ ์ค์ฒ ์ฒ๋ฆฌ์ ์ ๋๋ฉ์ด์ ๊ตฌํ์ผ๋ก ์ด๋ ค์์ ๊ฒช์ผ์ ์ ์ด ์์ผ์ ๊ฐ์?
๊ธฐ์กด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ์ปค์คํฐ๋ง์ด์ง์ด ์ด๋ ต๊ฑฐ๋ ์ฑ๋ฅ ๋ฌธ์ ๊ฐ ์์์ต๋๋ค. react-native-gesture-image-viewer๋ React Native Reanimated์ Gesture Handler๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ ๊ณ ์ฑ๋ฅ ๋ฒ์ฉ ์ ์ค์ฒ ๋ทฐ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก, ์ด๋ฏธ์ง๋ฟ๋ง ์๋๋ผ ๋น๋์ค, ์ปค์คํ
์ปดํฌ๋ํธ ๋ฑ ๋ชจ๋ ์ฝํ
์ธ ์ ์์ ํ ์ปค์คํฐ๋ง์ด์ง๊ณผ ์ง๊ด์ ์ธ ์ ์ค์ฒ ์ง์์ ์ ๊ณตํฉ๋๋ค.
- ๐ค ์์ ํ ์ ์ค์ฒ ์ง์ - ํ์น ์ค, ๋๋ธ ํญ ์ค, ์ค์์ดํ ๋ค๋น๊ฒ์ด์ , ์ค ์ํ์์์ ํฌ, ์ธ๋ก ๋๋๊ทธ๋ก ๋ซ๊ธฐ ์ง์
- ๐๏ธ ๊ณ ์ฑ๋ฅ ์ ๋๋ฉ์ด์ - React Native Reanimated ๊ธฐ๋ฐ์ 60fps ์ด์์ ๋ถ๋๋ฝ๊ณ ๋ฐ์์ฑ ๋์ ์ ๋๋ฉ์ด์
- ๐จ ์์ ํ ์ปค์คํฐ๋ง์ด์ง - ์ปดํฌ๋ํธ, ์คํ์ผ, ์ ์ค์ฒ ๋์๊น์ง ์๋ฒฝํ๊ฒ ์ ์ด ๊ฐ๋ฅ
- ๐๏ธ ์ธ๋ถ ์ ์ด API - ๋ฒํผ ๋ฑ ๋ค๋ฅธ UI ์ปดํฌ๋ํธ์์ ํ๋ก๊ทธ๋๋ฐ ๋ฐฉ์์ผ๋ก ์ ์ด ๊ฐ๋ฅ
- ๐งฉ ๋ค์ค ์ธ์คํด์ค ๊ด๋ฆฌ - ๊ณ ์ ID ๊ธฐ๋ฐ์ผ๋ก ์ฌ๋ฌ ๋ทฐ์ด๋ฅผ ๋ ๋ฆฝ์ ์ผ๋ก ๊ด๋ฆฌ
- ๐งฌ ์ ์ฐํ ํตํฉ - Modal, React Native Modal, ScrollView, FlatList, FlashList, Expo Image, FastImage ๋ฑ ์ํ๋ ์ปดํฌ๋ํธ ์ฌ์ฉ
- ๐ง ์๋ฒฝํ TypeScript ์ง์ - ํ์ ์ถ๋ก ๊ณผ ์์ ์ฑ์ ๊ฐ์ถ ๋ฐ์ด๋ ๊ฐ๋ฐ ๊ฒฝํ ์ ๊ณต
- ๐ ํฌ๋ก์ค ํ๋ซํผ ์ง์ - iOS, Android, Web์์ ๋์ํ๋ฉฐ Expo Go ๋ฐ New Architecture ์ง์
- ๐ช ๊ฐํธํ API - ๋ณต์กํ ์ค์ ์์ด๋ ์ง๊ด์ ์ด๊ณ ์ฝ๊ฒ ๊ตฌํ ๊ฐ๋ฅ
์ ์ฒด ๋ฌธ์๋ https://react-native-gesture-image-viewer.pages.dev์์ ํ์ธํ ์ ์์ต๋๋ค.
- ๐ ์์ ํ๋ก์ ํธ - ์ค์ ๊ตฌํ ์ฝ๋์ ๋ค์ํ ์ฌ์ฉ ์ฌ๋ก
- ๐ฅ Expo Snack - Expo Snack์์ ๋ฐ๋ก ์ฒดํ
- llms.txt: ๋ฌธ์์ ๋ชจ๋ ํ์ด์ง์ ๋ํ ์ ๋ชฉ, ๋งํฌ ๋ฐ ๊ฐ๋จํ ์ค๋ช ์ด ํฌํจ๋ ๊ตฌ์กฐํ๋ ์์ธ ํ์ผ์ ๋๋ค.
- llms-full.txt: ๋ชจ๋ ๋ฌธ์ ํ์ด์ง์ ์ ์ฒด ๋ด์ฉ์ ํ๋์ ํ์ผ๋ก ํฉ์น ์ ์ฒด ๋ด์ฉ ํ์ผ์ ๋๋ค.
react-native-gesture-image-viewer๋ ์์ ํ ์ปค์คํฐ๋ง์ด์ง์ ์ํด ์ ์ค์ฒ ๋์์๋ง ์ง์คํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์
๋๋ค.
import { useCallback, useState } from 'react';
import { ScrollView, Image, Modal, View, Text, Button, Pressable } from 'react-native';
import {
GestureViewer,
GestureTrigger,
useGestureViewerController,
useGestureViewerEvent,
useGestureViewerState,
} from 'react-native-gesture-image-viewer';
function App() {
const images = [...];
const [visible, setVisible] = useState(false);
const [selectedIndex, setSelectedIndex] = useState(0);
const { goToIndex, goToPrevious, goToNext } = useGestureViewerController();
const { currentIndex, totalCount } = useGestureViewerState();
const openModal = (index: number) => {
setSelectedIndex(index);
setVisible(true);
};
const renderImage = useCallback((imageUrl: string) => {
return <Image source={{ uri: imageUrl }} style={{ width: '100%', height: '100%' }} resizeMode="contain" />;
}, []);
useGestureViewerEvent('zoomChange', (data) => {
console.log(`Zoom changed from ${data.previousScale} to ${data.scale}`);
});
return (
<View>
{images.map((uri, index) => (
<GestureTrigger key={uri} onPress={() => openModal(index)}>
<Pressable>
<Image source={{ uri }} />
</Pressable>
</GestureTrigger>
))}
<Modal transparent visible={visible}>
<GestureViewer
data={images}
initialIndex={selectedIndex}
renderItem={renderImage}
ListComponent={ScrollView}
onDismiss={() => setVisible(false)}
/>
<View>
<Button title="Prev" onPress={goToPrevious} />
<Button title="Jump to index 2" onPress={() => goToIndex(2)} />
<Button title="Next" onPress={goToNext} />
<Text>{`${currentIndex + 1} / ${totalCount}`}</Text>
</View>
</Modal>
</View>
);
}ํ๋ก์ ํธ ๊ธฐ์ฌ ๋ฐฉ๋ฒ๊ณผ ๊ฐ๋ฐ ํ๊ฒฝ ์ค์ ์ ๋ํ ์์ธํ ๋ด์ฉ์ ๊ธฐ์ฌ ๊ฐ์ด๋๋ฅผ ์ฐธ๊ณ ํด ์ฃผ์ธ์.

