---
url: /ko/guide/getting-started/overview.md
---
# 개요

React Native에서 이미지 갤러리나 콘텐츠 뷰어를 구현할 때, 복잡한 제스처 처리와 애니메이션 구현으로 어려움을 겪으신 적이 있으신가요?
기존 라이브러리들은 커스터마이징이 어렵거나 성능 문제가 있었습니다. `react-native-gesture-image-viewer`는 [**React Native Reanimated**](https://docs.swmansion.com/react-native-reanimated/)와 [**Gesture Handler**](https://docs.swmansion.com/react-native-gesture-handler/docs/)를 기반으로 한 고성능 **범용 제스처 뷰어** 라이브러리로, 이미지뿐만 아니라 비디오, 커스텀 컴포넌트 등 모든 콘텐츠에 완전한 커스터마이징과 직관적인 제스처 지원을 제공합니다.
## 주요 특징
- 🤌 **완전한 제스처 지원** - 핀치 줌, 더블 탭 줌, 스와이프 네비게이션, 줌 상태에서의 팬, 세로 드래그로 닫기 지원
- 🏎️ **고성능 애니메이션** - React Native Reanimated 기반의 60fps 이상의 부드럽고 반응성 높은 애니메이션
- 🎨 **완전한 커스터마이징** - 컴포넌트, 스타일, 제스처 동작까지 완벽하게 제어 가능
- 🎛️ **외부 제어 API** - 버튼 등 다른 UI 컴포넌트에서 프로그래밍 방식으로 제어 가능
- 🧩 **다중 인스턴스 관리** - 고유 ID 기반으로 여러 뷰어를 독립적으로 관리
- 🧬 **유연한 통합** - Modal, [React Native Modal](https://www.npmjs.com/package/react-native-modal), ScrollView, FlatList, [FlashList](https://www.npmjs.com/package/@shopify/flash-list), [Expo Image](https://www.npmjs.com/package/expo-image), [FastImage](https://github.com/DylanVann/react-native-fast-image) 등 원하는 컴포넌트 사용
- 🧠 **완벽한 TypeScript 지원** - 타입 추론과 안정성을 갖춘 뛰어난 개발 경험 제공
- 🌐 **크로스 플랫폼 지원** - iOS, Android, Web에서 동작하며 Expo Go 및 New Architecture 지원
- 🪄 **간편한 API** - 복잡한 설정 없이도 직관적이고 쉽게 구현 가능
---
url: /ko/guide/getting-started/installation.md
---
# 설치
:::warning React Native Reanimated v3 사용자
**React Native Reanimated v3**를 사용 중이라면, 최상의 경험을 위해 이 라이브러리의 **1.x 버전이 권장됩니다**.
👉 **[1.x 문서로 이동](/1.x/ko/guide/getting-started/installation.md)**
2.x 버전은 React Native Reanimated v4만 지원합니다.
:::
:::info 중요
`react-native-gesture-image-viewer`는 [`react-native-reanimated`](https://www.npmjs.com/package/react-native-reanimated)와 [`react-native-gesture-handler`](https://www.npmjs.com/package/react-native-gesture-handler)를 기반으로 한 높은 성능의 뷰어 라이브러리입니다.\
따라서 이 라이브러리를 사용하기 전에 React Native Reanimated와 Gesture Handler를 **반드시 설치**해야 합니다. 자세한 설정 가이드는 각 라이브러리의 공식 문서를 참고해주세요.
:::
#### 필수 요구사항
| 라이브러리 | 최소 버전 |
| :----------------------------- | :--------: |
| `react` | `>=18.0.0` |
| `react-native` | `>=0.75.0` |
| `react-native-gesture-handler` | `>=2.24.0` |
| `react-native-reanimated` | `>=4.0.0` |
| `react-native-worklets` | `>=0.5.0` |
## [React Native Reanimated 설정](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/)
### `react-native-reanimated` 및 `react-native-worklets` 설치:
```sh [npm]
npm install react-native-reanimated
```
```sh [yarn]
yarn add react-native-reanimated
```
```sh [pnpm]
pnpm add react-native-reanimated
```
```sh [bun]
bun add react-native-reanimated
```
```sh [deno]
deno add npm:react-native-reanimated
```
- `react-native-worklets`는 더 나은 모듈성을 위해 `react-native-reanimated`에서 분리되었으며 별도로 설치해야 합니다.
```sh [npm]
npm install react-native-worklets
```
```sh [yarn]
yarn add react-native-worklets
```
```sh [pnpm]
pnpm add react-native-worklets
```
```sh [bun]
bun add react-native-worklets
```
```sh [deno]
deno add npm:react-native-worklets
```
### Babel 설정
#### Expo
:::warning Expo Go 지원
Expo Go 환경에서 Reanimated 4를 사용하려면 [Expo SDK 54(beta)](https://expo.dev/go)를 사용해야 합니다.
:::
Expo를 사용할 경우 `expo prebuild`를 실행하여 ios와 android 디렉토리의 네이티브 코드를 업데이트합니다.
```sh [npm]
npm expo prebuild
```
```sh [yarn]
yarn expo prebuild
```
```sh [pnpm]
pnpm expo prebuild
```
```sh [bun]
bun expo prebuild
```
```sh [deno]
deno expo npm:prebuild
```
끝입니다! 이제 Expo 프로젝트에서 Reanimated 4가 구성되었습니다.
Expo SDK 50부터는 Expo 스타터 템플릿에 기본적으로 Reanimated Babel 플러그인이 포함되어 있습니다.
#### React Native CLI
React Native Community CLI를 사용할 때는 `babel.config.js`에 `react-native-worklets/plugin` 플러그인을 수동으로 추가해야 합니다.
```js title="babel.config.js"
module.exports = {
presets: [
... // don't add it here :)
],
plugins: [
...
// for web
'@babel/plugin-proposal-export-namespace-from', // [!code highlight]
// react-native-worklets/plugin has to be listed last.
'react-native-worklets/plugin', // [!code highlight]
],
};
```
## [React Native Gesture Handler 설정](https://docs.swmansion.com/react-native-gesture-handler/docs/fundamentals/installation)
```sh [npm]
npm install react-native-gesture-handler
```
```sh [yarn]
yarn add react-native-gesture-handler
```
```sh [pnpm]
pnpm add react-native-gesture-handler
```
```sh [bun]
bun add react-native-gesture-handler
```
```sh [deno]
deno add npm:react-native-gesture-handler
```
- `react-native-gesture-handler`는 기본적으로 추가할 설정은 없지만, 공식 문서를 참고하여 환경에 맞게 설치해주세요.
- [안드로이드 환경의 모달에서 제스처를 사용](https://docs.swmansion.com/react-native-gesture-handler/docs/fundamentals/installation#android)하려면 모달의 콘텐츠를 `GestureHandlerRootView`로 래핑해야 정상적으로 동작하지만, **라이브러리 내부에 이미 `GestureHandlerRootView`가 래핑되어 있어 모달 사용 시 추가로 래핑할 필요가 없습니다.**
## React Native Gesture Image Viewer 설치
모든 설정이 끝났습니다! 🎉\
이제 `react-native-gesture-image-viewer`를 설치하면 됩니다.
```sh [npm]
npm install react-native-gesture-image-viewer
```
```sh [yarn]
yarn add react-native-gesture-image-viewer
```
```sh [pnpm]
pnpm add react-native-gesture-image-viewer
```
```sh [bun]
bun add react-native-gesture-image-viewer
```
```sh [deno]
deno add npm:react-native-gesture-image-viewer
```
---
url: /ko/guide/getting-started/quick-start.md
---
# 빠른 시작

## 예제 및 데모
- [📁 예제 프로젝트](https://github.com/saseungmin/react-native-gesture-image-viewer/tree/main/example) - 실제 구현 코드와 다양한 사용 사례
- [🤖 Expo Snack](https://snack.expo.dev/@harang/react-native-gesture-image-viewer-v2) - Expo Snack에서 바로 체험
## 설치
:::warning 중요
`react-native-gesture-image-viewer`는 [`react-native-reanimated`](https://www.npmjs.com/package/react-native-reanimated)와 [`react-native-gesture-handler`](https://www.npmjs.com/package/react-native-gesture-handler)를 기반으로 한 높은 성능의 뷰어 라이브러리입니다.\
따라서 이 라이브러리를 사용하기 전에 React Native Reanimated와 Gesture Handler를 설치해야 합니다. **아직 설정을 하지 않았다면 [설치 가이드](/ko/guide/getting-started/installation.md)를 참고해주세요.**
:::
```sh [npm]
npm install react-native-gesture-image-viewer
```
```sh [yarn]
yarn add react-native-gesture-image-viewer
```
```sh [pnpm]
pnpm add react-native-gesture-image-viewer
```
```sh [bun]
bun add react-native-gesture-image-viewer
```
```sh [deno]
deno add npm:react-native-gesture-image-viewer
```
## 기본 사용법
`react-native-gesture-image-viewer`는 완전한 커스터마이징을 위해 제스처 동작에만 집중한 라이브러리입니다.
```tsx
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 ;
}, []);
useGestureViewerEvent('zoomChange', (data) => {
console.log(`Zoom changed from ${data.previousScale} to ${data.scale}`);
});
return (
{images.map((uri, index) => (
openModal(index)}>
))}
setVisible(false)}
/>
);
}
```
---
url: /ko/guide/getting-started/ai.md
---
# AI
AI가 이 라이브러리의 기능, 버전별 문서, 프로젝트 규칙을 더 잘 이해해서 개발이나 트러블슈팅 과정에서 더 정확한 도움을 줄 수 있도록, 이 프로젝트는 다음 기능들을 제공합니다.
## llms.txt
[`llms.txt`](https://llmstxt.org/)는 LLM이 프로젝트 문서를 더 쉽게 탐색하고 활용할 수 있도록 돕는 표준입니다. 이 사이트는 다음 파일들을 제공합니다.
- [llms.txt](https://react-native-gesture-image-viewer.pages.dev/ko/llms.txt): `2.x` 문서용 구조화된 인덱스 파일
```txt
https://react-native-gesture-image-viewer.pages.dev/ko/llms.txt
```
- [llms-full.txt](https://react-native-gesture-image-viewer.pages.dev/ko/llms-full.txt): `2.x` 문서 전체를 하나로 합친 파일
```txt
https://react-native-gesture-image-viewer.pages.dev/ko/llms-full.txt
```
사용 목적에 따라 적절한 파일을 선택하면 됩니다.
- `llms.txt`는 더 작고 가벼워서 필요한 문서만 선택적으로 가져올 때 적합합니다.
- `llms-full.txt`는 해당 버전 문서 전체를 한 번에 제공하므로, 넓은 범위의 이해나 큰 리팩터링 작업에 적합합니다.
## Markdown docs
모든 문서 페이지는 AI에게 직접 전달할 수 있는 Markdown 버전도 함께 제공합니다.
예시:
```txt
https://react-native-gesture-image-viewer.pages.dev/ko/guide/getting-started/overview.md
https://react-native-gesture-image-viewer.pages.dev/ko/guide/getting-started/quick-start.md
https://react-native-gesture-image-viewer.pages.dev/ko/guide/usage/programmatic-control.md
```
특정 기능 하나만 물어볼 때는 전체 문서보다 관련 Markdown 페이지 하나를 제공하는 편이 더 효율적입니다.
---
url: /ko/guide/migration-from-1.x.md
---
# 1.x에서 2.x로 마이그레이션
## [Reanimated 3.x에서 4.x로 마이그레이션](https://docs.swmansion.com/react-native-reanimated/docs/guides/migration-from-3.x)
### 마이그레이션 단계
### `react-native-reanimated` 패키지 업그레이드:
```sh [npm]
npm install react-native-reanimated
```
```sh [yarn]
yarn add react-native-reanimated
```
```sh [pnpm]
pnpm add react-native-reanimated
```
```sh [bun]
bun add react-native-reanimated
```
```sh [deno]
deno add npm:react-native-reanimated
```
### `react-native-worklets` 패키지 설치:
```sh [npm]
npm install react-native-worklets
```
```sh [yarn]
yarn add react-native-worklets
```
```sh [pnpm]
pnpm add react-native-worklets
```
```sh [bun]
bun add react-native-worklets
```
```sh [deno]
deno add npm:react-native-worklets
```
### 설정 업데이트
#### Expo
`ios` 및 `android` 디렉토리의 네이티브 코드를 업데이트하기 위해 prebuild를 실행합니다.
```sh [npm]
npm expo prebuild
```
```sh [yarn]
yarn expo prebuild
```
```sh [pnpm]
pnpm expo prebuild
```
```sh [bun]
bun expo prebuild
```
```sh [deno]
deno expo npm:prebuild
```
끝입니다! 이제 Expo 프로젝트에서 Reanimated 4가 구성되었습니다.
Expo SDK 50부터는 Expo 스타터 템플릿에 기본적으로 Reanimated Babel 플러그인이 포함되어 있습니다.
#### React Native CLI
React Native Community CLI를 사용할 때는 `babel.config.js`에 `react-native-worklets/plugin` 플러그인을 수동으로 추가해야 합니다.
```diff title="babel.config.js"
module.exports = {
presets: [
... // 여기에 추가하지 마세요 :)
],
plugins: [
...
// for web
'@babel/plugin-proposal-export-namespace-from', // [!code highlight]
- 'react-native-reanimated/plugin',
+ 'react-native-worklets/plugin',
],
};
```
`metro.config.js`에서 `wrapWithReanimatedMetroConfig`를 제거합니다.
```diff title="metro.config.js"
- const { wrapWithReanimatedMetroConfig } = require('react-native-reanimated/metro-config');
const config = {
// 기존 Metro 설정 옵션들
};
- module.exports = wrapWithReanimatedMetroConfig(config);
+ module.exports = config
```
## `useGestureViewerController`에서 `currentIndex`와 `totalCount` 제거
:::info ❗ 호환성 변경사항
- `useGestureViewerController`는 더 이상 `currentIndex`와 `totalCount`를 반환하지 않습니다. 대신 `useGestureViewerState`를 사용하세요.
- `GestureViewerControllerState` 타입을 `GestureViewerState`로 이름을 변경합니다.
:::
```diff
import {
GestureViewer,
- GestureViewerControllerState,
+ GestureViewerState
useGestureViewerController,
useGestureViewerEvent,
+ useGestureViewerState,
} from 'react-native-gesture-image-viewer';
const {
goToIndex, goToPrevious, goToNext, zoomIn, zoomOut, resetZoom, rotate,
- currentIndex, totalCount
} = useGestureViewerController();
+ const { currentIndex, totalCount } = useGestureViewerState();
```
## `onIndexChange` prop 제거
:::info ❗ 호환성 변경사항
`onIndexChange` prop이 제거되었습니다. 대신 `useGestureViewerState`와 `useEffect`를 사용하세요.
:::
```tsx
// 이전
console.log(index)} />;
// 이후
const { currentIndex } = useGestureViewerState();
useEffect(() => {
console.log(currentIndex);
}, [currentIndex]);
```
## 제스처 prop 이름 변경
더 나은 개발자 경험을 위해 제스처 prop 이름을 개선했습니다.
:::info ❗ 호환성 변경사항
- `enableDismissGesture` → `dismiss.enabled`
- `dismissThreshold` → `dismiss.threshold`
- `resistance` → `dismiss.resistance`
- `animateBackdrop` → `dismiss.fadeBackdrop`
- `useSnap` → `enableSnapMode`
- `enableZoomPanGesture` → `enablePanWhenZoomed`
- `enableZoomGesture` → `enablePinchZoom`
- `enableSwipeGesture` → `enableHorizontalSwipe`
:::
```diff
```
---
url: /ko/guide/performance-optimization-tips.md
---
# 성능 최적화 팁
#### `renderItem` 함수는 `useCallback`으로 감싸서 불필요한 리렌더링을 방지하세요.
```tsx
import { useCallback, useState } from 'react'; // [!code highlight]
import { ScrollView, Image, Modal } from 'react-native';
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
const images = [...];
const [visible, setVisible] = useState(false);
// [!code highlight:3]
const renderImage = useCallback((imageUrl: string) => {
return ;
}, []);
return (
setVisible(false)}>
setVisible(false)}
/>
);
}
```
#### 대용량 이미지의 경우 [`expo-image`](https://docs.expo.dev/versions/latest/sdk/image/)나 [`FastImage`](https://github.com/DylanVann/react-native-fast-image) 사용을 권장합니다.
```tsx
import { ScrollView, Modal } from 'react-native';
import { GestureViewer } from 'react-native-gesture-image-viewer';
import { Image } from 'expo-image'; // [!code highlight]
function App() {
const images = [...];
const [visible, setVisible] = useState(false);
const renderImage = useCallback((imageUrl: string) => {
return ; // [!code highlight]
}, []);
return (
setVisible(false)}>
setVisible(false)}
/>
);
}
```
#### 많은 수의 이미지를 다룰 때는 [`FlashList`](https://shopify.github.io/flash-list/) 사용을 권장합니다.
- [FlashList Performance 가이드](https://shopify.github.io/flash-list/docs/fundamentals/performance)
```tsx
import { useCallback, useState } from 'react';
import { Image, Modal } from 'react-native';
import { GestureViewer } from 'react-native-gesture-image-viewer';
import { FlashList } from '@shopify/flash-list'; // [!code highlight]
function App() {
const images = [...];
const [visible, setVisible] = useState(false);
const renderImage = useCallback((imageUrl: string) => {
return ;
}, []);
return (
setVisible(false)}>
setVisible(false)}
/>
);
}
```
#### 디바이스에서 테스트하세요. (시뮬레이터에서는 성능이 제한될 수 있습니다.)
---
url: /ko/guide/usage/basic-usage.md
---
# 기본 사용법
## GestureViewer
```tsx
import { FlatList, Image, Modal } from 'react-native';
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
const images = [...];
const [visible, setVisible] = useState(false);
const renderImage = useCallback((imageUrl: string) => {
return ;
}, []);
return (
setVisible(false)}>
setVisible(false)}
/>
);
}
```
## [useGestureViewerController](/ko/guide/usage/programmatic-control.md)
`useGestureViewerController` 훅을 사용하여 `GestureViewer`를 프로그래밍 방식으로 제어할 수 있습니다.
```tsx
import { GestureViewer, useGestureViewerController } from 'react-native-gesture-image-viewer';
function App() {
const { goToIndex, goToPrevious, goToNext, zoomIn, zoomOut, resetZoom, rotate } =
useGestureViewerController();
return (
{/* Navigation Controls */}
goToIndex(2)} />
);
}
```
## [useGestureViewerState](/ko/guide/usage/gesture-viewer-state.md)
`useGestureViewerState` 훅을 사용하여 `GestureViewer`의 현재 상태를 가져올 수 있습니다.
```tsx
import { GestureViewer, useGestureViewerState } from 'react-native-gesture-image-viewer';
function App() {
const { currentIndex, totalCount } = useGestureViewerState();
return (
{`${currentIndex + 1} / ${totalCount}`}
);
}
```
## [useGestureViewerEvent](/ko/guide/usage/handling-viewer-events.md)
`useGestureViewerEvent` 훅을 사용하여 `GestureViewer`의 특정 이벤트를 구독할 수 있습니다. 줌이나 회전과 같은 실시간 제스처 변화에 반응할 수 있습니다.
```tsx
import { GestureViewer, useGestureViewerEvent } from 'react-native-gesture-image-viewer';
function App() {
useGestureViewerEvent('zoomChange', (data) => {
console.log(`Zoom changed from ${data.previousScale} to ${data.scale}`);
});
useGestureViewerEvent('rotationChange', (data) => {
console.log(`Rotation changed from ${data.previousRotation}° to ${data.rotation}°`);
});
useGestureViewerEvent('tap', (data) => {
if (data.kind === 'single') {
console.log(`Tapped item ${data.index} at (${data.x}, ${data.y})`);
}
});
return ;
}
```
## [GestureTrigger](/ko/guide/usage/trigger-based-animations.md)
`GestureTrigger`는 썸네일과 같은 트리거 요소에서 전체 모달 뷰로의 원활한 전환을 생성하는 부드러운 트리거 기반 애니메이션을 지원합니다. 이 기능은 트리거와 모달 콘텐츠 간의 시각적 연속성을 유지하여 사용자 경험을 향상시킵니다.
```tsx
import { GestureTrigger, GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
const [visible, setVisible] = useState(false);
return (
setVisible(true)}>
setVisible(false)} />
);
}
```
---
url: /ko/guide/usage/custom-components.md
---
# 커스텀 컴포넌트
`react-native-gesture-image-viewer`는 강력한 기능으로 완벽한 컴포넌트 커스터마이징이 가능합니다. 이미지뿐만 아니라 원하는 컴포넌트로 제스처를 지원하는 아이템을 만들 수 있습니다.
## 모달 컴포넌트
다음과 같이 원하는 `Modal`을 사용하여 뷰어를 만들 수 있습니다.
**Use Modal**
```tsx
import { FlatList, Image, Modal } from 'react-native';
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
const images = [...];
const [visible, setVisible] = useState(false);
return (
setVisible(false)}>
setVisible(false)}
/>
);
}
```
**Use react-nativee-modal**
```tsx
import { FlatList, Image } from 'react-native';
import Modal from 'react-native-modal';
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
const images = [...];
const [visible, setVisible] = useState(false);
return (
setVisible(false)}
onBackdropPress={() => setVisible(false)}
hasBackdrop={false}
style={styles.modal}
useNativeDriver={true}
hideModalContentWhileAnimating={true}
animationInTiming={300}
animationOutTiming={300}
>
setVisible(false)}
/>
);
}
```
## 리스트 컴포넌트
`ListComponent` props를 통해 `ScrollView`, `FlatList`, [`FlashList`](https://shopify.github.io/flash-list/) 등 원하는 리스트를 지원합니다.\
`listProps`는 **선택한 리스트 컴포넌트에 맞는 타입 추론**을 제공하여, IDE에서 정확한 자동완성과 타입 안전성을 보장합니다.
```tsx
import { FlashList } from '@shopify/flash-list';
function App() {
return (
);
}
```
## 콘텐츠 컴포넌트
`renderItem` props를 통해 [`expo-image`](https://docs.expo.dev/versions/latest/sdk/image/), [`FastImage`](https://github.com/DylanVann/react-native-fast-image) 등 다양한 종류의 콘텐츠 컴포넌트를 주입하여 제스처를 사용할 수 있습니다.
```tsx
import { GestureViewer } from 'react-native-gesture-image-viewer';
import { Image } from 'expo-image';
function App() {
const renderImage = useCallback((imageUrl: string) => {
return (
);
}, []);
return ;
}
```
---
url: /ko/guide/usage/gesture-features.md
---
# 제스처 기능
`react-native-gesture-image-viewer`는 뷰어에 필요한 다양한 제스처를 지원합니다. 제스처 동작의 기본값은 아래 예제를 참고해주세요.
```tsx
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return (
);
}
```
## Gesture Props
### `dismiss`
어떤 수직 스와이프 방향에서 `onDismiss`를 호출할지 제어하는 제스처 옵션입니다. 기본값은 기존 동작과 동일한 아래 방향 dismiss이며, 모달 스타일 닫기 제스처에 유용합니다.
| 속성 | 설명 | 타입 | 기본값 |
| :------------- | :----------------------------------------------------------------- | :------------------- | :----: |
| `enabled` | `false`일 때 dismiss 제스처가 비활성화됩니다. | `boolean` | `true` |
| `direction` | dismiss를 트리거할 수직 스와이프 방향을 설정합니다. | `down`, `up`, `both` | `down` |
| `threshold` | `threshold`는 수직 제스처 중에 임계값을 적용하여 `onDismiss`가 호출되는 시점을 제어합니다. | `number` | `80` |
| `resistance` | `resistance`는 dismiss 제스처 중에 저항을 적용하여 수직 이동 범위를 제어합니다. | `number` | `2` |
| `fadeBackdrop` | 기본적으로 설정된 dismiss 방향으로 드래그하는 동안 배경 `opacity`가 1에서 0으로 점진적으로 감소합니다. | `boolean` | `true` |
### `enableHorizontalSwipe` (기본값: `true`)
좌우 스와이프 제스처를 제어합니다. `false`일 때 가로 제스처가 비활성화됩니다.
### `enablePinchZoom` (기본값: `true`)
두 손가락 핀치 제스처를 제어합니다. `false`일 때 핀치 줌 제스처가 비활성화됩니다. 핀치 줌은 두 손가락 사이의 중심점을 기준으로 확대됩니다.
### `enableDoubleTapZoom` (기본값: `true`)
더블탭 줌 제스처를 제어합니다. `false`일 때 더블탭 줌 제스처가 비활성화됩니다. 더블탭 줌은 탭한 위치를 중심으로 확대됩니다.
### `enablePanWhenZoomed` (기본값: `true`)
줌이 활성화된 상태에서만 작동하며, 확대된 상태에서 아이템 위치를 이동할 수 있게 합니다. `false`일 때 줌 상태에서의 제스처 이동이 비활성화됩니다. 콘텐츠가 화면 밖으로 나가지 않도록 자동으로 경계를 제한합니다.
---
url: /ko/guide/usage/gesture-viewer-props.md
---
# GestureViewer Props
### `enableLoop` (기본값: `false`)
루프 모드를 활성화합니다. `true`일 때 마지막 아이템에서 다음으로 가면 첫 번째로, 첫 번째에서 이전으로 가면 마지막으로 돌아갑니다.
```tsx
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return (
);
}
```
### `enableSnapMode` (기본값: `false`)
스냅 스크롤 모드를 활성화합니다.
- **`false` (기본값)**: 페이징 모드 (`pagingEnabled: true`)
- 전체 화면 크기 단위로 스크롤됩니다
- **`true`**: 스냅 모드 (`snapToInterval` 자동 계산)
- `snapToInterval`이 `width`와 `itemSpacing` 값을 기반으로 자동 계산됩니다
- 아이템 간격이 필요할 때 이 옵션을 사용하세요
```tsx
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return (
);
}
```
### `itemSpacing` (기본값: `0`)
아이템 간의 간격을 픽셀 단위로 설정합니다. `enableSnapMode`가 `true`일 때만 적용됩니다.
```tsx
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return (
);
}
```
### `autoPlay` (기본값: `false`)
자동 재생 모드를 활성화합니다. `true`일 때 지정된 간격 후 다음 아이템으로 자동으로 재생됩니다.
- `enableLoop`이 활성화되면 마지막 아이템에서 다음으로 가면 첫 번째로, 첫 번째에서 이전으로 가면 마지막으로 돌아갑니다.
- `enableLoop`이 비활성화되면 마지막 아이템에서 정지됩니다.
- 아이템이 하나만 있을 때 자동 재생이 비활성화됩니다.
- 줌 또는 회전 제스처가 감지되면 자동 재생이 일시 중지됩니다.
```tsx
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return (
);
}
```
### `autoPlayInterval` (기본값: `3000`)
자동 재생 간격을 밀리초 단위로 설정합니다.\
양수 정수여야 합니다. 값이 250ms 미만이면 런타임에서 250ms로 제한됩니다.
```tsx
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return (
);
}
```
### `onSingleTap`
뷰어에서 싱글 탭이 확정되면 실행됩니다. 헤더, 푸터, 캡션, 액션 버튼처럼 뷰어 컨트롤 UI를 토글할 때 유용하며, 뷰어 위에 별도의 pressable을 덮지 않아도 됩니다.
- 더블 탭 줌이 활성화되어 있으면 더블 탭이 아닌지 확인한 뒤 실행되므로 약간 늦게 호출될 수 있습니다
- 스와이프, 핀치, dismiss, 더블 탭 줌 제스처에서는 호출되지 않습니다
```tsx
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
const [showControls, setShowControls] = useState(true);
return (
setShowControls((prev) => !prev)} // [!code highlight]
/>
);
}
```
### `initialIndex` (기본값: `0`)
초기 인덱스 값을 설정할 수 있습니다.
### `maxZoomScale` (기본값: `2`)
최대 줌 배율을 제어할 수 있습니다.
## `GestureViewerProps` interface
[Go to source](https://github.com/saseungmin/react-native-gesture-image-viewer/blob/a7b58a5fc9aa396e5b72ec1e6fe6ae27d94ed044/src/types.ts#L88)
````tsx title="GestureViewerProps"
export interface GestureViewerProps {
/**
* 여러 개의 `GestureViewer` 인스턴스를 효율적으로 관리하고 싶다면 `id` prop을 사용해 각각의 `GestureViewer`를 구분할 수 있습니다.
* @remarks `GestureViewer`는 컴포넌트가 언마운트되면 인스턴스를 메모리에서 자동으로 제거하므로 별도의 수동 메모리 관리가 필요하지 않습니다.
* @defaultValue 'default'
*/
id?: string;
/**
* `GestureViewer`에 표시할 데이터입니다.
*/
data: ItemT[];
/**
* 컴포넌트가 마운트될 때 `GestureViewer`에 처음 표시할 아이템의 인덱스입니다.
* @defaultValue 0
*/
initialIndex?: number;
/**
* `GestureViewer`가 닫힐 때 호출되는 콜백 함수입니다.
*/
onDismiss?: () => void;
/**
* dismiss 인터랙션이 시작될 때 호출되는 콜백 함수입니다.
* @remarks dismiss 제스처/애니메이션이 진행되는 동안 외부 UI(예: 헤더, 버튼)를 숨길 때 유용합니다.
*/
onDismissStart?: () => void;
/**
* 아이템을 렌더링할 때 호출되는 콜백 함수입니다.
*/
renderItem: (item: ItemT, index: number) => React.ReactElement;
/**
* 뷰어 콘텐츠에서 싱글 탭이 확정되었을 때 호출되는 콜백 함수입니다.
* @remarks
* - 이 콜백은 탭이 싱글 탭으로 확정된 뒤에만 실행되므로, 더블 탭 줌이 활성화되어 있으면 약간 지연될 수 있습니다.
* - 스와이프, 핀치, dismiss, 더블 탭 줌 제스처에서는 호출되지 않습니다.
* - 전체 화면 탭 처리가 필요하다면 `renderContainer`에서 pressable을 덮어씌우는 대신 이 콜백 사용을 권장합니다.
*/
onSingleTap?: (event: GestureViewerSingleTapEvent) => void;
/**
* 컨테이너를 렌더링할 때 호출되는 콜백 함수입니다.
* @remarks 뷰어 주변에 추가 UI(예: 닫기 버튼, 툴바)를 구성할 때 유용합니다.
* 전체 화면 탭 처리는 뷰어 콘텐츠 위에 pressable을 덮어씌우기보다 `onSingleTap` 사용을 권장합니다.
* 두 번째 인자는 `dismiss()`처럼 뷰어를 닫을 수 있는 제어 헬퍼를 제공합니다.
*
* @param children - 컨테이너 내부에 렌더링할 뷰어 콘텐츠입니다.
* @param helpers - 뷰어 제어 헬퍼입니다. 현재는 `dismiss()`를 포함합니다.
* @returns 전달된 `children`을 감싸서 렌더링하는 React 엘리먼트입니다.
*/
renderContainer?: (
children: React.ReactElement,
helpers: { dismiss: () => void },
) => React.ReactElement;
/**
* `ListComponent` prop을 통해 `ScrollView`, `FlatList`, `FlashList` 같은 다양한 리스트 컴포넌트를 지원합니다.
*/
ListComponent: LC;
/**
* `GestureViewer`의 너비입니다.
* @remarks 이 prop을 지정하지 않으면 `GestureViewer`의 너비는 화면 너비와 동일해집니다.
* @defaultValue 화면 너비
*/
width?: number;
/**
* `GestureViewer`의 높이입니다.
* @remarks 이 prop을 지정하지 않으면 `GestureViewer`의 높이는 화면 높이와 동일해집니다.
* @defaultValue 화면 높이
*/
height?: number;
/**
* 리스트 컴포넌트에 전달할 props입니다.
* @remarks `listProps`는 **선택한 리스트 컴포넌트를 기준으로 타입 추론**을 제공하므로 IDE에서 더 정확한 자동완성과 타입 안정성을 보장합니다.
*/
listProps?: Partial>;
/**
* backdrop의 스타일입니다.
*/
backdropStyle?: StyleProp;
/**
* 컨테이너의 스타일입니다.
*/
containerStyle?: StyleProp;
/**
* 자동 재생 모드입니다.
* @remarks
* - `true`이면 지정된 간격 후 다음 아이템이 자동으로 재생됩니다.
* - `enableLoop`가 활성화되어 있으면 마지막 아이템 다음에 첫 번째 아이템으로 돌아갑니다.
* - `enableLoop`가 비활성화되어 있으면 마지막 아이템에서 멈춥니다.
* - 아이템이 하나뿐이면 자동 재생은 비활성화됩니다.
* - 줌 또는 회전 제스처가 감지되면 자동 재생이 일시 정지됩니다.
* @defaultValue false
*/
autoPlay?: boolean;
/**
* 자동 재생 간격입니다.
* @remarks
* - `autoPlay`가 활성화되어 있으면 지정된 간격(ms)마다 다음 아이템으로 이동합니다.
* - 양의 정수여야 합니다. 250ms 미만 값은 런타임에서 250ms로 보정됩니다.
* @defaultValue 3000
*/
autoPlayInterval?: number;
/**
* dismiss 제스처 옵션입니다.
* @remarks 위/아래 방향을 선택할 수 있는 수직 스와이프 닫기 제스처에 유용합니다.
*/
dismiss?: {
/**
* `false`이면 dismiss 제스처가 비활성화됩니다.
* @defaultValue true
*/
enabled?: boolean;
/**
* 어떤 수직 스와이프 방향에서 `onDismiss`를 호출할지 설정합니다.
* @remarks 기존 동작과 호환되는 `down`, 위로만 닫는 `up`, 양방향 모두 허용하는 `both`를 사용할 수 있습니다.
* @defaultValue 'down'
*/
direction?: GestureViewerDismissDirection;
/**
* `threshold`는 수직 제스처 중 임계값을 적용해 `onDismiss`가 호출되는 시점을 제어합니다.
* @defaultValue 80
*/
threshold?: number;
/**
* `resistance`는 dismiss 제스처 중 저항을 적용해 수직 이동 범위를 제어합니다.
* @defaultValue 2
*/
resistance?: number;
/**
* 기본적으로 설정된 dismiss 방향으로 드래그하는 동안 배경 `opacity`가 점진적으로 감소합니다.
* @remarks `false`이면 이 애니메이션이 비활성화됩니다.
* @defaultValue true
*/
fadeBackdrop?: boolean;
};
/**
* 좌우 스와이프 제스처를 제어합니다.
* @remarks `false`이면 가로 제스처가 비활성화됩니다.
* @defaultValue true
*/
enableHorizontalSwipe?: boolean;
/**
* 줌이 활성화된 경우에만 동작하며, 확대된 상태에서 아이템 위치를 이동할 수 있게 합니다.
* @remarks `false`이면 줌 중 제스처 이동이 비활성화됩니다.
* @defaultValue true
*/
enablePanWhenZoomed?: boolean;
/**
* 두 손가락 핀치 제스처를 제어합니다.
* @remarks `false`이면 두 손가락 줌 제스처가 비활성화됩니다.
* @defaultValue true
*/
enablePinchZoom?: boolean;
/**
* 더블 탭 줌 제스처를 제어합니다.
* @remarks `false`이면 더블 탭 줌 제스처가 비활성화됩니다.
* @defaultValue true
*/
enableDoubleTapZoom?: boolean;
/**
* 무한 루프 탐색을 활성화합니다.
* @defaultValue false
*/
enableLoop?: boolean;
/**
* 스냅 스크롤 모드를 활성화합니다.
*
* @remarks
* **`false` (기본값)**: 페이징 모드 (`pagingEnabled: true`)
* - 화면 전체 크기 단위로 스크롤됩니다.
*
* **`true`**: 스냅 모드 (`snapToInterval` 자동 계산)
* - `snapToInterval`은 `width`와 `itemSpacing` 값을 기준으로 자동 계산됩니다.
* - 아이템 간격이 필요할 때 이 옵션을 사용하세요.
* @defaultValue false
*
*/
enableSnapMode?: boolean;
/**
* 아이템 사이 간격(픽셀)입니다.
* @remarks `enableSnapMode`가 `true`일 때만 적용됩니다.
* @defaultValue 0
*/
itemSpacing?: number;
/**
* 최대 줌 배율입니다.
* @defaultValue 2
*/
maxZoomScale?: number;
/**
* 트리거 기반 애니메이션 설정입니다.
* @remarks 애니메이션 지속 시간, easing, 시스템 reduce-motion 동작을 커스터마이즈할 수 있습니다.
*
* @example
* ```tsx
* {
* console.log('애니메이션 완료');
* },
* }}
* />
* ```
*/
triggerAnimation?: TriggerAnimationConfig;
}
````
---
url: /ko/guide/usage/gesture-viewer-state.md
---
# GestureViewer 상태 추적
`GestureViewer`는 뷰어의 현재 상태를 추적하는 상태 관리 시스템을 제공합니다. `useGestureViewerState` 훅을 사용하여 뷰어의 상태를 접근할 수 있습니다.
## useGestureViewerState
```tsx
import { GestureViewer, useGestureViewerState } from 'react-native-gesture-image-viewer';
function App() {
const { currentIndex, totalCount } = useGestureViewerState();
return (
{`${currentIndex + 1} / ${totalCount}`}
);
}
```
### 매개변수
- `id?: string`
- `GestureViewer` 인스턴스의 ID입니다.
- 기본값: `"default"`
### API Reference
| 속성 | 설명 | 타입 | 기본값 |
| :------------- | :---------------------- | :------- | :-: |
| `currentIndex` | 현재 표시되고 있는 아이템의 인덱스입니다. | `number` | `0` |
| `totalCount` | 전체 아이템의 개수입니다. | `number` | `0` |
---
url: /ko/guide/usage/handling-viewer-events.md
---
# 뷰어 이벤트 처리하기
`useGestureViewerEvent` 훅을 사용하여 `GestureViewer`의 특정 이벤트를 구독할 수 있습니다. 줌, 회전, 탭과 같은 뷰어 상호작용에 반응할 수 있습니다.
## useGestureViewerEvent
```tsx
import { GestureViewer, useGestureViewerEvent } from 'react-native-gesture-image-viewer';
function App() {
useGestureViewerEvent('zoomChange', (data) => {
console.log(`줌 변경: ${data.previousScale} → ${data.scale}`);
});
useGestureViewerEvent('rotationChange', (data) => {
console.log(`회전 변경: ${data.previousRotation}° → ${data.rotation}°`);
});
useGestureViewerEvent('tap', (data) => {
if (data.kind === 'single') {
console.log(`아이템 ${data.index} 탭: (${data.x}, ${data.y})`);
}
});
return ;
}
```
### 특정 인스턴스의 이벤트 구독
`useGestureViewerEvent` 훅의 첫 번째 인수로 인스턴스 ID를 전달하여 특정 인스턴스의 이벤트를 구독할 수 있습니다.
```ts
// 특정 인스턴스의 이벤트 구독
useGestureViewerEvent('gallery', 'zoomChange', (data) => {
console.log(`갤러리 줌: ${data.scale}x`);
});
```
:::note
기본 `id` 값은 `default`입니다.
:::
### API Reference
```ts
// 기본 인스턴스의 이벤트 구독
function useGestureViewerEvent(
eventType: T,
callback: GestureViewerEventCallback,
): void;
// 특정 인스턴스의 이벤트 구독
function useGestureViewerEvent(
instanceId: string,
eventType: T,
callback: GestureViewerEventCallback,
): void;
```
| 이벤트 타입 | 설명 | 콜백 데이터 |
| :--------------- | :----------------------------------------- | :-------------------------------------------------------- |
| `zoomChange` | 핀치 제스처 중 줌 스케일이 변경될 때 발생 | `{ scale: number, previousScale: number }` |
| `rotationChange` | 회전 제스처 중 회전 각도가 변경될 때 발생 | `{ rotation: number, previousRotation: number }` |
| `tap` | 뷰어에서 탭이 확정되면 발생합니다. 현재는 확정된 싱글 탭만 emit합니다. | `{ kind: 'single', x: number, y: number, index: number }` |
:::tip
`tap` 이벤트를 `GestureViewer` prop로 직접 처리하고 싶다면 [`onSingleTap`](/ko/guide/usage/gesture-viewer-props.md#onsingletap)을 사용할 수 있습니다.
:::
---
url: /ko/guide/usage/multi-instance-management.md
---
# 다중 인스턴스 관리
여러 개의 `GestureViewer` 인스턴스를 효율적으로 관리하고 싶은 경우 `id` 값을 적용하면 여러 개의 `GestureViewer`를 사용할 수 있습니다.
`GestureViewer`는 컴포넌트가 언마운트되면 메모리에서 자동으로 인스턴스가 제거되어 메모리 관리를 수동으로 할 필요가 없습니다.
:::note
`id` 기본값은 `default`입니다.
:::
```tsx
import {
GestureViewer,
GestureTrigger,
useGestureViewerController,
useGestureViewerEvent,
useGestureViewerState,
} from 'react-native-gesture-image-viewer';
const firstViewerId = 'firstViewerId';
const secondViewerId = 'secondViewerId';
function App() {
const { goToIndex: goToFirstIndex } = useGestureViewerController(firstViewerId);
const { goToIndex: goToSecondIndex } = useGestureViewerController(secondViewerId);
const { currentIndex: firstCurrentIndex } = useGestureViewerState(firstViewerId);
const { currentIndex: secondCurrentIndex } = useGestureViewerState(secondViewerId);
useGestureViewerEvent(firstViewerId, 'zoomChange', (data) => {
console.log(`Gallery zoom: ${data.scale}x`);
});
useGestureViewerEvent(secondViewerId, 'zoomChange', (data) => {
console.log(`Gallery zoom: ${data.scale}x`);
});
return (
goToFirstIndex(2)} />
goToSecondIndex(0)} />
);
}
```
---
url: /ko/guide/usage/programmatic-control.md
---
# 프로그래밍 방식 제어
`useGestureViewerController` 훅을 사용하여 `GestureViewer`를 프로그래밍 방식으로 제어할 수 있습니다.
## useGestureViewerController
```tsx
import { GestureViewer, useGestureViewerController } from 'react-native-gesture-image-viewer';
function App() {
const { goToIndex, goToPrevious, goToNext, zoomIn, zoomOut, resetZoom, rotate } =
useGestureViewerController();
return (
{/* Zoom Controls & Rotation Controls */}
zoomIn(0.25)} />
zoomOut(0.25)} />
{
rotate(0);
resetZoom();
}}
/>
rotate(90)} />
rotate(90, false)} />
{/* Navigation Controls */}
goToIndex(2)} />
);
}
```
### 매개변수
- `id?: string`
- `GestureViewer` 인스턴스의 ID입니다.
- 기본값: `"default"`
## API Reference
| 속성 | 설명 | 타입 | 파라미터 기본값 |
| :------------- | :------------------ | :----------------------------------------------------- | :--------: |
| `goToIndex` | 특정 인덱스로 이동합니다. | `(index: number) => void` | - |
| `goToPrevious` | 이전 아이템으로 이동합니다. | `() => void` | - |
| `goToNext` | 다음 아이템으로 이동합니다. | `() => void` | - |
| `zoomIn` | 지정된 배수만큼 확대합니다. | `(multiplier?: number) => void` | `0.25` |
| `zoomOut` | 지정된 배수만큼 축소합니다. | `(multiplier?: number) => void` | `0.25` |
| `resetZoom` | 지정된 스케일로 줌을 초기화합니다. | `(scale?: number) => void` | `1` |
| `rotate` | 지정된 각도만큼 회전합니다. | `(angle?: RotationAngle, clockwise?: boolean) => void` | `90, true` |
- `zoomIn(multiplier?)`
- **multiplier**: 확대할 배수 (범위: `0.01 ~ 1`)
- 예: `zoomIn(0.5)` → 현재 스케일의 50% 만큼 추가 확대
- `zoomOut(multiplier?)`
- **multiplier**: 축소할 배수 (범위: `0.01 ~ 1`)
- 예: `zoomOut(0.3)` → 현재 스케일을 1.3으로 나누어 축소
- `resetZoom(scale?)`
- **scale**: 초기화할 스케일 값
- 예: `resetZoom(1.5)` → 1.5배 스케일로 초기화
- `rotate(angle?, clockwise?)`
- **angle**: 회전할 각도 (0, 90, 180, 270, 360)
- **clockwise**: 회전 방향 (true: 시계방향, false: 반시계방향)
- 예: `rotate(90)` → 90도 시계방향 회전
- 예: `rotate(90, false)` → 90도 반시계방향 회전
- 예: `rotate(0)` → 회전 초기화
---
url: /ko/guide/usage/style-customization.md
---
# 스타일 커스터마이징
`GestureViewer`의 스타일을 커스터마이징할 수 있습니다.
```tsx
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return (
{children}}
/>
);
}
```
| 속성 | 설명 | 기본값 |
| :----------------------------------: | :------------------------------------------------- | :------------------------------------------------: |
| `width` | 콘텐츠 아이템의 너비입니다. 기본값은 화면 너비입니다. | `Dimensions width` |
| `height` | 콘텐츠 아이템의 높이입니다. 기본값은 화면 높이입니다. | `Dimensions height` |
| `containerStyle` | 리스트 컴포넌트를 감싸는 컨테이너의 커스텀 스타일을 지정할 수 있습니다. | `flex: 1` |
| `backdropStyle` | 뷰어의 배경 스타일을 커스터마이징할 수 있습니다. | `backgroundColor: black; StyleSheet.absoluteFill;` |
| `renderContainer(children, helpers)` | ``를 레핑하는 커스텀 래퍼 컴포넌트를 지정할 수 있습니다. | |
:::tip
전체 화면 탭 처리는 `onSingleTap` 사용을 권장합니다. `renderContainer`는 헤더, 닫기 버튼, 툴바처럼 주변 UI를 구성할 때 가장 잘 맞습니다.
:::
---
url: /ko/guide/usage/trigger-based-animations.md
---
# 트리거 기반 모달 애니메이션
`GestureTrigger`는 썸네일과 같은 트리거 요소에서 전체 모달 뷰로의 원활한 전환을 생성하는 부드러운 트리거 기반 애니메이션을 지원합니다. 이 기능은 트리거와 모달 콘텐츠 간의 시각적 연속성을 유지하여 사용자 경험을 향상시킵니다.
## GestureTrigger 사용법
`GestureTrigger` 컴포넌트는 누를 수 있는 요소를 감싸고 부드러운 모달 전환을 위해 해당 위치를 등록합니다.
```tsx
import { GestureTrigger, GestureViewer } from 'react-native-gesture-image-viewer';
function Gallery() {
const [visible, setVisible] = useState(false);
const [selectedIndex, setSelectedIndex] = useState(0);
const openModal = (index: number) => {
setSelectedIndex(index);
setVisible(true);
};
return (
{/* 갤러리 그리드 */}
{images.map((uri, index) => (
openModal(index)}>
))}
{/* 모달 */}
setVisible(false)}
triggerAnimation={{
duration: 300,
easing: Easing.bezier(0.25, 0.1, 0.25, 1.0),
onAnimationComplete: () => console.log('애니메이션 완료!'),
}}
/>
);
}
```
### 1. GestureTrigger에 onPress 사용 (권장)
```tsx
openModal(index)}>
```
### 2. 하위 요소에 onPress 사용
```tsx
openModal(index)}>
```
:::note 중요
- 두 방법은 기능적으로 동일합니다
- `GestureTrigger`는 자동으로 하위 컴포넌트에 press 핸들러를 주입합니다
- 하위 컴포넌트는 반드시 press 가능해야 합니다 (`onPress` prop 지원)
- `GestureTrigger`와 하위 요소 모두에 `onPress`가 있는 경우, 둘 다 호출됩니다 (하위 요소의 핸들러가 먼저 호출됨)
**지원되는 하위 컴포넌트**
다음과 같은 press 가능한 컴포넌트를 하위 요소로 사용할 수 있습니다:
- `Pressable`
- `TouchableOpacity`
- `TouchableHighlight`
- `Button`
- `onPress` prop를 가지고 있는 컴포넌트 및 커스텀 컴포넌트
**다양한 컴포넌트 예시**
```tsx
// TouchableOpacity 사용
이미지 보기;
// 커스텀 컴포넌트 사용
function CustomButton({ onPress, children }) {
return {children};
}
커스텀 버튼;
```
:::
## 애니메이션 구성
`triggerAnimation` prop을 사용하여 트리거 애니메이션 동작을 사용자 정의할 수 있습니다:
```tsx
import { ReduceMotion, Easing } from 'react-native-reanimated';
function App() {
return (
{
// 애니메이션 완료 시 콜백
console.log('모달 애니메이션 완료');
},
}}
/>
);
}
```
## 다중 트리거 인스턴스
다른 ID를 사용하여 여러 트리거 인스턴스를 가질 수 있습니다:
```tsx
// 사진 갤러리 트리거
openPhotoModal(index)}>
// 비디오 갤러리 트리거
openVideoModal(index)}>
```
:::tip
애니메이션이 제대로 작동하려면 `GestureTrigger`와 `GestureViewer` 컴포넌트 간에 `id` prop이 일치해야 합니다. (기본값: `default`)
현재 보고 있는 아이템의 썸네일 위치로 닫기 애니메이션을 되돌리고 싶다면, 각 `GestureTrigger`에 `index={index}`를 함께 전달하세요.
:::
### 현재 인덱스 기준 닫기 애니메이션
갤러리처럼 여러 썸네일이 있고 모달 안에서 좌우 스와이프로 다른 아이템으로 이동할 수 있는 경우, `GestureTrigger`에 `index`를 전달하면 닫을 때 **현재 보고 있는 아이템의 트리거 위치**를 우선 사용합니다.
```tsx
{
images.map((uri, index) => (
openModal(index)}>
));
}
```
`index`를 전달하지 않으면 닫기 애니메이션은 기본적으로 **처음 열 때 사용한 트리거 위치**를 기준으로 동작합니다.
:::note fallback 동작
현재 인덱스에 해당하는 트리거를 찾을 수 없는 경우(예: 리스트 virtualization으로 썸네일이 unmount된 경우), 닫기 애니메이션은 다음 순서로 fallback 됩니다:
1. 현재 오픈을 시작한 트리거 위치
2. 트리거를 찾을 수 없으면 일반 dismiss
:::
## 닫기 애니메이션 처리
### onDismissStart 콜백
`onDismissStart` 콜백은 닫기 애니메이션이 시작될 때 즉시 트리거되며, 애니메이션이 완료되기 전에 사라져야 하는 UI 요소를 숨기는 데 유용합니다.
```tsx
function App() {
const [visible, setVisible] = useState(false);
const [showExternalUI, setShowExternalUI] = useState(false);
const handleDismissStart = () => {
setShowExternalUI(false);
};
return (
{showExternalUI && (
{`${currentIndex + 1} / ${totalCount}`}
)}
);
}
```
### 커스텀 컴포넌트에서 닫기
`renderContainer`의 `dismiss` 헬퍼를 사용하여 프로그래밍 방식으로 뷰어를 닫을 수 있습니다:
```tsx
(
{children}
닫기
)}
/>
```
:::note `renderContainer`의 `dismiss`를 사용하는 이유
트리거 기반 애니메이션을 사용할 때는 `setVisible(false)`로 직접 제어하는 대신 `renderContainer`가 제공하는 `dismiss` 메서드를 사용하는 것이 중요합니다. 그 이유는 다음과 같습니다:
```tsx
// ❌ 피하세요: 트리거 애니메이션 없이 즉시 닫힙니다
setVisible(false)} title="닫기" />
// ✅ 권장: 트리거 애니메이션을 사용하여 닫힙니다
```
**작동 방식:**
1. 트리거 애니메이션 사용 시:
- `dismiss`가 호출되면 뷰어가 원래 트리거 위치로 애니메이션되며 돌아갑니다
- 애니메이션 시작 시 `onDismissStart`가 호출됩니다
- 애니메이션 완료 후 `onDismiss`가 호출됩니다
2. 트리거 애니메이션 없이 사용 시:
- 트리거 애니메이션이 구성되지 않은 경우에도 `dismiss`는 간단한 닫기로 작동합니다
:::
### 닫기 처리 완전한 예제
```tsx
function ImageViewer() {
const [visible, setVisible] = useState(false);
const [showUI, setShowUI] = useState(true);
return (
setShowUI(false)}
onDismiss={() => setVisible(false)}
renderContainer={(children, { dismiss }) => (
{children}
{showUI && (
)}
)}
/>
);
}
```
### 트리거 애니메이션과 함께하는 닫기 동작
트리거 기반 애니메이션을 사용할 때, 닫기 애니메이션은 원래 트리거 위치로 돌아갑니다. `onDismissStart` 콜백은 이 애니메이션이 시작될 때 호출되므로, 닫기 애니메이션 중에 표시되지 않아야 하는 UI 요소를 숨길 수 있습니다.
```tsx
{
console.log('닫기 애니메이션 시작됨');
setShowUI(false);
}}
onDismiss={() => {
console.log('닫기 애니메이션 완료됨');
setVisible(false);
}}
/>
```
이 패턴은 다음과 같은 사용자 경험을 보장합니다:
1. 닫기가 시작되면 즉시 UI 요소를 숨깁니다
2. 닫기 애니메이션이 자연스럽게 완료되도록 합니다
3. 애니메이션이 완전히 완료된 후에만 리소스를 정리합니다
### 모범 사례
1. 애니메이션과 함께 뷰어를 닫으려면 항상 `renderContainer`의 `dismiss` 메서드를 사용하세요
2. 닫기 애니메이션 중에 표시되지 않아야 하는 UI 요소는 `onDismissStart`를 사용하여 숨기세요
3. 애니메이션 완료 후에 실행되어야 하는 정리 작업은 `onDismiss`에 배치하세요
#### 흔히 하는 실수
```tsx
// ❌ 피하세요: 트리거 애니메이션을 우회합니다
setVisible(false)} title="닫기" />
// ❌ 피하세요: 애니메이션이 중단됩니다
const handleClose = () => {
setShowUI(false);
setVisible(false); // 너무 일찍 닫힙니다
};
// ✅ 올바른 방법: 애니메이션이 자연스럽게 완료되도록 합니다
setShowUI(false)}
onDismiss={() => setVisible(false)}
renderContainer={(children, { dismiss }) => (
{children}
{showUI && (
)}
)}
/>
```
이 패턴은 트리거 기반 애니메이션이 일관되게 작동하도록 하며 최상의 사용자 경험을 제공합니다.
## API 참조
### GestureTrigger Props
```ts
interface GestureTriggerProps {
id?: string; // GestureViewer과 연결할 식별자 (기본값: "default")
index?: number; // 현재 인덱스 기준 dismiss 애니메이션을 위한 아이템 인덱스
children: ReactElement; // 단일 pressable 자식 요소
onPress?: (...args: unknown[]) => void; // 추가 onPress 핸들러
}
```
### TriggerAnimation 구성
```ts
interface TriggerAnimationConfig {
duration?: number; // 밀리초 단위의 애니메이션 지속 시간
easing?: EasingFunction; // 사용자 정의 이징 함수
reduceMotion?: boolean; // 시스템 감소 모션 설정 준수 여부
onAnimationComplete?: () => void; // 애니메이션 완료 시 호출될 콜백
}
```
| 속성 | 타입 | 기본값 | 설명 |
| :-------------------- | :--------------- | :------------------------------------ | :------------------- |
| `duration` | `number` | `300` | 밀리초 단위의 애니메이션 지속 시간 |
| `easing` | `EasingFunction` | `Easing.bezier(0.25, 0.1, 0.25, 1.0)` | 애니메이션을 위한 이징 함수 |
| `reduceMotion` | `ReduceMotion` | `undefined` | 시스템 감소 모션 설정 준수 여부 |
| `onAnimationComplete` | `() => void` | `undefined` | 애니메이션이 완료될 때 호출되는 콜백 |
:::note
트리거 애니메이션은 누를 때 트리거 요소의 위치를 측정하고 해당 위치에서 전체 화면으로 모달을 애니메이션하는 방식으로 작동합니다.
:::
---
url: /ko/index.md
---
# React Native Gesture Image Viewer
부드럽고 유연한 이미지 뷰어
> Reanimated 기반의 이미지 제스처와 완전한 제어
[빠른 시작](/ko/guide/getting-started/installation.html) | [GitHub](https://github.com/saseungmin/react-native-gesture-image-viewer)
## Features
- 🤌 **완전한 제스처 지원**: 핀치 줌, 더블 탭 줌, 스와이프 네비게이션, 줌 상태에서의 팬, 세로 드래그로 닫기 지원
- 🏎️ **고성능 애니메이션**: React Native Reanimated 기반의 60fps 이상의 부드럽고 반응성 높은 애니메이션
- 🎨 **완전한 커스터마이징**: 컴포넌트, 스타일, 제스처 동작까지 완벽하게 제어 가능
- 🎛️ **외부 제어 API**: 버튼 등 다른 UI 컴포넌트에서 프로그래밍 방식으로 제어 가능
- 🧩 **다중 인스턴스 관리**: 고유 ID 기반으로 여러 뷰어를 독립적으로 관리
- 🧬 **유연한 통합**: Modal, FlatList, FlashList, Expo Image, FastImage 등과 매끄럽게 연동
- 🧠 **완벽한 TypeScript 지원**: 타입 추론과 안정성을 갖춘 뛰어난 개발 경험 제공
- 🌐 **크로스 플랫폼 지원**: iOS, Android, Web에서 동작하며 Expo Go 및 New Architecture 지원
- 🪄 **간편한 API**: 복잡한 설정 없이도 직관적이고 쉽게 구현 가능