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

React Native에서 이미지 갤러리나 콘텐츠 뷰어를 구현할 때, 복잡한 제스처 처리와 애니메이션 구현으로 어려움을 겪으신 적이 있으신가요?
기존 라이브러리들은 커스터마이징이 어렵거나 성능 문제가 있었습니다. `react-native-gesture-image-viewer`는 [**React Native Reanimated**](https://docs.swmansion.com/react-native-reanimated/docs/3.x/fundamentals/getting-started/)와 [**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: /1.x/ko/guide/getting-started/installation.md
---
# 설치
:::warning React Native Reanimated v4 사용자
**React Native Reanimated v4**를 사용하고 계신다면, 최상의 경험을 위해 이 라이브러리의 **2.x 버전이 권장됩니다**.
- 👉 **[2.x 문서로 이동](/ko/guide/getting-started/installation.md)**
- 📖 **[1.x에서 2.x로 마이그레이션 가이드](/ko/guide/migration-from-1.x.md)**
v1.x도 Reanimated v4에서 동작하겠지만, v3에 최적화되어 있어 deprecation 경고나 호환성 문제가 발생할 수 있습니다.
:::
:::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` | `>=3.0.0` |
## [React Native Reanimated 설정](https://docs.swmansion.com/react-native-reanimated/docs/3.x/fundamentals/getting-started/)
```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
```
### `babel.config.js`에 다음과 같이 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-reanimated/plugin has to be listed last.
'react-native-reanimated/plugin', // [!code highlight]
],
};
```
### `metro.config.js`에 기본 구성 함수를 `wrapWithReanimatedMetroConfig`로 래핑해주세요.
```js title="metro.config.js"
// [!code highlight:3]
const { wrapWithReanimatedMetroConfig } = require('react-native-reanimated/metro-config');
const config = {
// Your existing Metro configuration options
};
module.exports = wrapWithReanimatedMetroConfig(config); // [!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@1.x
```
```sh [yarn]
yarn add react-native-gesture-image-viewer@1.x
```
```sh [pnpm]
pnpm add react-native-gesture-image-viewer@1.x
```
```sh [bun]
bun add react-native-gesture-image-viewer@1.x
```
```sh [deno]
deno add npm:react-native-gesture-image-viewer@1.x
```
---
url: /1.x/ko/guide/getting-started/quick-start.md
---
# 빠른 시작

## 예제 및 데모
- [📁 예제 프로젝트](https://github.com/saseungmin/react-native-gesture-image-viewer/tree/v1.x/example) - 실제 구현 코드와 다양한 사용 사례
- [🤖 Expo Go](https://snack.expo.dev/@harang/react-native-gesture-image-viewer) - Snack Expo에서 바로 체험
## 설치
:::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를 설치해야 합니다. **아직 설정을 하지 않았다면 [설치 가이드](/1.x/ko/guide/getting-started/installation.md)를 참고해주세요.**
:::
```sh [npm]
npm install react-native-gesture-image-viewer@1.x
```
```sh [yarn]
yarn add react-native-gesture-image-viewer@1.x
```
```sh [pnpm]
pnpm add react-native-gesture-image-viewer@1.x
```
```sh [bun]
bun add react-native-gesture-image-viewer@1.x
```
```sh [deno]
deno add npm:react-native-gesture-image-viewer@1.x
```
## 기본 사용법
`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 } from 'react-native-gesture-image-viewer';
function App() {
const images = [...];
const [visible, setVisible] = useState(false);
const [selectedIndex, setSelectedIndex] = useState(0);
const { goToIndex, goToPrevious, goToNext, currentIndex, totalCount } = useGestureViewerController();
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: /1.x/ko/guide/getting-started/ai.md
---
# AI
AI가 이 라이브러리의 `1.x` 동작, 문서, 프로젝트 규칙을 더 잘 이해해서 개발이나 트러블슈팅 과정에서 더 정확한 도움을 줄 수 있도록, 이 프로젝트는 다음 기능들을 제공합니다.
## llms.txt
[`llms.txt`](https://llmstxt.org/)는 LLM이 프로젝트 문서를 더 쉽게 탐색하고 활용할 수 있도록 돕는 표준입니다. `1.x` 문서는 아래 파일을 사용하면 됩니다.
- [1.x/llms.txt](https://react-native-gesture-image-viewer.pages.dev/1.x/ko/llms.txt): `1.x` 문서용 구조화된 인덱스 파일
```txt
https://react-native-gesture-image-viewer.pages.dev/1.x/ko/llms.txt
```
- [1.x/llms-full.txt](https://react-native-gesture-image-viewer.pages.dev/1.x/ko/llms-full.txt): `1.x` 문서 전체를 하나로 합친 파일
```txt
https://react-native-gesture-image-viewer.pages.dev/1.x/ko/llms-full.txt
```
## Markdown docs
모든 문서 페이지는 AI에게 직접 전달할 수 있는 Markdown 버전도 함께 제공합니다.
예시:
```txt
https://react-native-gesture-image-viewer.pages.dev/1.x/ko/guide/getting-started/overview.md
https://react-native-gesture-image-viewer.pages.dev/1.x/ko/guide/getting-started/quick-start.md
https://react-native-gesture-image-viewer.pages.dev/1.x/ko/guide/usage/programmatic-control.md
```
특정 기능만 물어볼 때는 전체 문서보다 관련 Markdown 페이지 하나를 제공하는 편이 더 효율적입니다.
---
url: /1.x/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: /1.x/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](/1.x/ko/guide/usage/programmatic-control.md)
`useGestureViewerController` 훅을 사용하여 `GestureViewer`를 프로그래밍 방식으로 제어할 수 있습니다.
```tsx
import { GestureViewer, useGestureViewerController } from 'react-native-gesture-image-viewer';
function App() {
const {
goToIndex,
goToPrevious,
goToNext,
currentIndex,
totalCount,
zoomIn,
zoomOut,
resetZoom,
rotate,
} = useGestureViewerController();
return (
{/* Navigation Controls */}
goToIndex(2)} />
{`${currentIndex + 1} / ${totalCount}`}
);
}
```
## [useGestureViewerEvent](/1.x/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}°`);
});
return ;
}
```
## [GestureTrigger](/1.x/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: /1.x/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: /1.x/ko/guide/usage/gesture-features.md
---
# 제스처 기능
`react-native-gesture-image-viewer`는 뷰어에 필요한 다양한 제스처를 지원합니다. 제스처 동작의 기본값은 아래 예제를 참고해주세요.
```tsx
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return (
);
}
```
## Props
### `enableLoop` (기본값: `false`)
루프 모드를 활성화합니다. `true`일 때 마지막 아이템에서 다음으로 가면 첫 번째로, 첫 번째에서 이전으로 가면 마지막으로 돌아갑니다.
### `enableDismissGesture` (기본값: `true`)
아래로 스와이프할 때 `onDismiss` 함수를 호출합니다. 모달을 아래로 스와이프해서 닫는 제스처에 유용합니다.
### `enableSwipeGesture` (기본값: `true`)
좌우 스와이프 제스처를 제어합니다. `false`일 때 가로 제스처가 비활성화됩니다.
### `enableZoomGesture` (기본값: `true`)
두 손가락 핀치 제스처를 제어합니다. `false`일 때 핀치 줌 제스처가 비활성화됩니다. 핀치 줌은 두 손가락 사이의 중심점을 기준으로 확대됩니다.
### `enableDoubleTapGesture` (기본값: `true`)
더블탭 줌 제스처를 제어합니다. `false`일 때 더블탭 줌 제스처가 비활성화됩니다. 더블탭 줌은 탭한 위치를 중심으로 확대됩니다.
### `enableZoomPanGesture` (기본값: `true`)
줌이 활성화된 상태에서만 작동하며, 확대된 상태에서 아이템 위치를 이동할 수 있게 합니다. `false`일 때 줌 상태에서의 제스처 이동이 비활성화됩니다. 콘텐츠가 화면 밖으로 나가지 않도록 자동으로 경계를 제한합니다.
---
url: /1.x/ko/guide/usage/gesture-viewer-props.md
---
# GestureViewer Props
### `enableLoop` (기본값: `false`)
루프 모드를 활성화합니다. `true`일 때 마지막 아이템에서 다음으로 가면 첫 번째로, 첫 번째에서 이전으로 가면 마지막으로 돌아갑니다.
```tsx
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
const [currentIndex, setCurrentIndex] = useState(0);
return ;
}
```
### `onIndexChange`
`index` 값이 변경될 때 `onIndexChange` 콜백함수가 호출됩니다.
```tsx
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
const [currentIndex, setCurrentIndex] = useState(0);
return ;
}
```
### `useSnap` (기본값: `false`)
스크롤 동작 모드를 설정합니다. `false`(기본값)는 페이징 모드, `true`는 스냅 모드를 사용합니다.
```tsx
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return ;
}
```
### `itemSpacing` (기본값: `0`)
아이템 간의 간격을 픽셀 단위로 설정합니다. `useSnap`이 `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 ;
}
```
### `initialIndex` (기본값: `0`)
초기 인덱스 값을 설정할 수 있습니다.
### `dismissThreshold` (기본값: `80`)
`dismissThreshold`는 수직 제스처 시 임계값을 적용하여 `onDismiss` 호출 시점을 제어할 수 있습니다.
### `resistance` (기본값: `2`)
`resistance`는 수직 제스처 시 저항값을 적용하여 수직으로 이동하는 범위를 제어할 수 있습니다.
### `maxZoomScale` (기본값: `2`)
최대 줌 배율을 제어할 수 있습니다.
## `GestureViewerProps` interface
[Go to source](https://github.com/saseungmin/react-native-gesture-image-viewer/blob/edafaf0346c3f922382cd1e5f8c5c30d49115ec7/src/types.ts#L76)
````tsx title="GestureViewerProps"
export interface GestureViewerProps {
/**
* When you want to efficiently manage multiple `GestureViewer` instances, you can use the `id` prop to use multiple `GestureViewer` components.
* @remarks `GestureViewer` automatically removes instances from memory when components are unmounted, so no manual memory management is required.
* @defaultValue 'default'
*/
id?: string;
/**
* The data to display in the `GestureViewer`.
*/
data: ItemT[];
/**
* The index of the item to display in the `GestureViewer` when the component is mounted.
* @defaultValue 0
*/
initialIndex?: number;
/**
* A callback function that is called when the index of the item changes.
*/
onIndexChange?: (index: number) => void;
/**
* A callback function that is called when the `GestureViewer` is dismissed.
*/
onDismiss?: () => void;
/**
* A callback function that is called when the dismiss interaction starts.
* @remarks Useful to hide external UI (e.g., headers, buttons) while the dismiss gesture/animation is in progress.
*/
onDismissStart?: () => void;
/**
* A callback function that is called to render the item.
*/
renderItem: (item: ItemT, index: number) => React.ReactElement;
/**
* A callback function that is called to render the container.
* @remarks Useful for composing additional UI (e.g., close button, toolbars) around the viewer.
* The second argument provides control helpers such as `dismiss()` to close the viewer.
*
* @param children - The viewer content to be rendered inside your container.
* @param helpers - Control helpers for the viewer. Currently includes `dismiss()`.
* @returns A React element that wraps and renders the provided `children`.
*/
renderContainer?: (
children: React.ReactElement,
helpers: { dismiss: () => void },
) => React.ReactElement;
/**
* Support for any list component like `ScrollView`, `FlatList`, `FlashList` through the `ListComponent` prop.
*/
ListComponent: LC;
/**
* The width of the `GestureViewer`.
* @remarks If you don't set this prop, the width of the `GestureViewer` will be the same as the width of the screen.
* @defaultValue screen width
*/
width?: number;
/**
* The height of the `GestureViewer`.
* @remarks If you don't set this prop, the height of the `GestureViewer` will be the same as the height of the screen.
* @defaultValue screen height
*/
height?: number;
/**
* Auto play mode.
* @remarks
* - When `true`, the viewer will automatically play the next item after the specified interval.
* - When `enableLoop` is enabled, the viewer will loop back to the first item after the last item.
* - When `enableLoop` is disabled, the viewer will stop at the last item.
* - When there is only one item, auto-play is disabled.
* - When zoom or rotate gestures are detected, the auto-play will be paused.
* @defaultValue false
*/
autoPlay?: boolean;
/**
* Auto play interval.
* @remarks
* - When `autoPlay` is enabled, the viewer advances to the next item after the specified interval (ms).
* - Must be a positive integer. Values below 250ms are clamped to 250ms at runtime.
* @defaultValue 3000
*/
autoPlayInterval?: number;
/**
* Enables snap scrolling mode.
*
* @remarks
* **`false` (default)**: Paging mode (`pagingEnabled: true`)
* - Scrolls by full screen size increments
*
* **`true`**: Snap mode (`snapToInterval` auto-calculated)
* - `snapToInterval` is automatically calculated based on `width` and `itemSpacing` values
* - Use this option when you need item spacing
* @defaultValue false
*
*/
useSnap?: boolean;
/**
* `dismissThreshold` controls when `onDismiss` is called by applying a threshold value during vertical gestures.
* @defaultValue 80
*/
dismissThreshold?: number;
/**
* Calls `onDismiss` function when swiping down.
* @remarks Useful for closing modals with downward swipe gestures.
* @defaultValue true
*/
enableDismissGesture?: boolean;
/**
* Controls left/right swipe gestures.
* @remarks When `false`, horizontal gestures are disabled.
* @defaultValue true
*/
enableSwipeGesture?: boolean;
/**
* `resistance` controls the range of vertical movement by applying resistance during vertical gestures.
* @defaultValue 2
*/
resistance?: number;
/**
* The props to pass to the list component.
* @remarks The `listProps` provides **type inference based on the selected list component**, ensuring accurate autocompletion and type safety in your IDE.
*/
listProps?: Partial>;
/**
* The style of the backdrop.
*/
backdropStyle?: StyleProp;
/**
* The style of the container.
*/
containerStyle?: StyleProp;
/**
* By default, the background `opacity` gradually decreases from 1 to 0 during downward swipe gestures.
* @remarks When `false`, this animation is disabled.
* @defaultValue true
*/
animateBackdrop?: boolean;
/**
* Only works when zoom is active, allows moving item position when zoomed.
* @remarks When `false`, gesture movement is disabled during zoom.
* @defaultValue true
*/
enableZoomPanGesture?: boolean;
/**
* Controls two-finger pinch gestures.
* @remarks When `false`, two-finger zoom gestures are disabled.
* @defaultValue true
*/
enableZoomGesture?: boolean;
/**
* Controls double-tap zoom gestures.
* @remarks When `false`, double-tap zoom gestures are disabled.
* @defaultValue true
*/
enableDoubleTapGesture?: boolean;
/**
* Enables infinite loop navigation.
* @defaultValue false
*/
enableLoop?: boolean;
/**
* The maximum zoom scale.
* @defaultValue 2
*/
maxZoomScale?: number;
/**
* The spacing between items in pixels.
* @remarks Only applied when `useSnap` is `true`.
* @defaultValue 0
*/
itemSpacing?: number;
/**
* Trigger-based animation settings
* @remarks You can customize animation duration, easing, and system reduce-motion behavior.
*
* @example
* ```tsx
* {
* console.log('Animation complete');
* },
* }}
* />
* ```
*/
triggerAnimation?: TriggerAnimationConfig;
}
````
---
url: /1.x/ko/guide/usage/handling-viewer-events.md
---
# 뷰어 이벤트 처리하기
`useGestureViewerEvent` 훅을 사용하여 `GestureViewer`의 특정 이벤트를 구독할 수 있습니다. 줌이나 회전과 같은 실시간 제스처 변화에 반응할 수 있습니다.
```tsx
import { GestureViewer, useGestureViewerEvent } from 'react-native-gesture-image-viewer';
function App() {
// 기본 인스턴스 (ID: 'default')의 줌 변경 이벤트 구독
useGestureViewerEvent('zoomChange', (data) => {
console.log(`줌 변경: ${data.previousScale} → ${data.scale}`);
});
// 기본 인스턴스 (ID: 'default')의 회전 변경 이벤트 구독
useGestureViewerEvent('rotationChange', (data) => {
console.log(`회전 변경: ${data.previousRotation}° → ${data.rotation}°`);
});
// 특정 인스턴스의 이벤트 구독
useGestureViewerEvent('gallery', 'zoomChange', (data) => {
console.log(`갤러리 줌: ${data.scale}배`);
});
return ;
}
```
#### 이벤트 타입
| 이벤트 | 설명 | 데이터 |
| :--------------- | :----------------------- | :----------------------------------------------- |
| `zoomChange` | 핀치 제스처 중 줌 스케일이 변경될 때 발생 | `{ scale: number, previousScale: number }` |
| `rotationChange` | 회전 제스처 중 회전 각도가 변경될 때 발생 | `{ rotation: number, previousRotation: number }` |
---
url: /1.x/ko/guide/usage/multi-instance-management.md
---
# 다중 인스턴스 관리
여러 개의 `GestureViewer` 인스턴스를 효율적으로 관리하고 싶은 경우 `id` 값을 적용하면 여러 개의 `GestureViewer`를 사용할 수 있습니다.\
`GestureViewer`는 컴포넌트가 언마운트되면 메모리에서 자동으로 인스턴스가 제거되어 메모리 관리를 수동으로 할 필요가 없습니다.
:::note
`id` 기본값은 `default`입니다.
:::
```tsx
import {
GestureViewer,
GestureTrigger,
useGestureViewerController,
useGestureViewerEvent,
} from 'react-native-gesture-image-viewer';
const firstViewerId = 'firstViewerId';
const secondViewerId = 'secondViewerId';
function App() {
const { currentIndex: firstCurrentIndex, totalCount: firstTotalCount } =
useGestureViewerController(firstViewerId);
const { currentIndex: secondCurrentIndex, totalCount: secondTotalCount } =
useGestureViewerController(secondViewerId);
useGestureViewerEvent(firstViewerId, 'zoomChange', (data) => {
console.log(`갤러리 줌: ${data.scale}배`);
});
useGestureViewerEvent(secondViewerId, 'zoomChange', (data) => {
console.log(`갤러리 줌: ${data.scale}배`);
});
return (
);
}
```
---
url: /1.x/ko/guide/usage/programmatic-control.md
---
# 프로그래밍 방식 제어
`useGestureViewerController` 훅을 사용하여 `GestureViewer`를 프로그래밍 방식으로 제어할 수 있습니다.
```tsx
import { GestureViewer, useGestureViewerController } from 'react-native-gesture-image-viewer';
function App() {
const {
goToIndex,
goToPrevious,
goToNext,
currentIndex,
totalCount,
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)} />
{`${currentIndex + 1} / ${totalCount}`}
);
}
```
## `useGestureViewerController` API Reference
| 속성 | 설명 | 타입 | 기본값 |
| :------------- | :-------------------- | :----------------------------------------------------- | :--------: |
| `goToIndex` | 특정 인덱스로 이동합니다. | `(index: number) => void` | - |
| `goToPrevious` | 이전 아이템으로 이동합니다. | `() => void` | - |
| `goToNext` | 다음 아이템으로 이동합니다. | `() => void` | - |
| `currentIndex` | 현재 표시 중인 아이템의 인덱스입니다. | `number` | `0` |
| `totalCount` | 전체 아이템의 개수입니다. | `number` | `0` |
| `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` |
### Parameters
- `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: /1.x/ko/guide/usage/style-customization.md
---
# 스타일 커스터마이징
`GestureViewer`의 스타일을 커스터마이징할 수 있습니다.
```tsx
import { GestureViewer } from 'react-native-gesture-image-viewer';
function App() {
return (
{children}}
/>
);
}
```
| 속성 | 설명 | 기본값 |
| :----------------------------------: | :--------------------------------------------------------------------------------- | :------------------------------------------------: |
| `animateBackdrop` | 기본적으로 아래로 스와이프할 때 배경의 `opacity`가 1에서 0으로 점진적으로 감소합니다. `false`일 때 이 애니메이션이 비활성화됩니다. | `true` |
| `width` | 콘텐츠 아이템의 너비입니다. 기본값은 화면 너비입니다. | `Dimensions width` |
| `height` | 콘텐츠 아이템의 높이입니다. 기본값은 화면 높이입니다. | `Dimensions height` |
| `containerStyle` | 리스트 컴포넌트를 감싸는 컨테이너의 커스텀 스타일을 지정할 수 있습니다. | `flex: 1` |
| `backdropStyle` | 뷰어의 배경 스타일을 커스터마이징할 수 있습니다. | `backgroundColor: black; StyleSheet.absoluteFill;` |
| `renderContainer(children, helpers)` | ``를 레핑하는 커스텀 래퍼 컴포넌트를 지정할 수 있습니다. | |
---
url: /1.x/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`)
:::
## 닫기 애니메이션 처리
### 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")
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: /1.x/ko/index.md
---
# React Native Gesture Image Viewer
부드럽고 유연한 이미지 뷰어
> Reanimated 기반의 이미지 제스처와 완전한 제어
[빠른 시작](/1.x/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**: 복잡한 설정 없이도 직관적이고 쉽게 구현 가능