|
2 | 2 |
|
3 | 3 | > [English](./README.md) | 한국어 |
4 | 4 |
|
5 | | -> [!note] |
6 | | -> **V1 사용자:** [V1 문서](/packages/react-native-youtube-bridge/docs/v1.md) | [V2 마이그레이션 가이드](/packages/react-native-youtube-bridge/docs/migration-v2.md) |
| 5 | +<div align="center"> |
| 6 | + <img src="./assets/logo.png" width="300px" alt="React Native Youtube Bridge 로고" /> |
| 7 | +</div> |
7 | 8 |
|
8 | | -## 개요 |
9 | | - |
10 | | -React Native에서 YouTube 플레이어를 사용하려면 복잡한 설정이 필요합니다. |
11 | | -하지만 현재 지속적으로 유지보수되고 있는 React Native용 YouTube 플레이어 라이브러리가 없는 상황입니다. (가장 인기 있는 react-native-youtube-iframe의 [최근 릴리즈는 2023년 07월 02일](https://github.com/LonelyCpp/react-native-youtube-iframe/releases/tag/v2.3.0)) |
12 | | - |
13 | | -`react-native-youtube-bridge`는 [YouTube iframe Player API](https://developers.google.com/youtube/iframe_api_reference)를 React Native에서 쉽게 사용할 수 있도록 도와주는 라이브러리입니다. |
14 | 9 |
|
15 | | -- ✅ TypeScript 지원 |
16 | | -- ✅ iOS, Android, Web 플랫폼 지원 |
17 | | -- ✅ New Architecture 지원 |
18 | | -- ✅ YouTube 네이티브 플레이어 모듈 없이도 사용 가능 |
19 | | -- ✅ 다양한 [YouTube iframe Player API](https://developers.google.com/youtube/iframe_api_reference) 기능 지원 |
20 | | -- ✅ 다중 인스턴스 지원 - 여러 플레이어를 독립적으로 관리 가능 |
21 | | -- ✅ Expo의 접근 방식과 매우 유사한 직관적이고 사용하기 쉬운 Hook 기반 API 제공 |
22 | | -- ✅ Expo 지원 |
23 | | -- ✅ 유연한 렌더링 모드 (인라인 HTML & 웹뷰) |
24 | | - |
25 | | -## 예제 |
| 10 | +## 개요 |
26 | 11 |
|
27 | | -> 빠른 시작을 원하신다면 [예제](/example/)를 확인해보세요. |
| 12 | +React Native에서 YouTube 플레이어를 사용하려면 YouTube IFrame API, WebView 동작, 이벤트, 플랫폼 차이를 직접 연결해야 하는 경우가 많습니다. |
28 | 13 |
|
29 | | -- [웹 데모](https://react-native-youtube-bridge-example.pages.dev/) |
30 | | -- [Expo Go](https://snack.expo.dev/@harang/react-native-youtube-bridge) |
| 14 | +`react-native-youtube-bridge`는 iOS, Android, Web에서 사용할 수 있는 타입 안전한 Hook 기반 YouTube 플레이어 라이브러리입니다. |
31 | 15 |
|
32 | 16 | <p align="center"> |
33 | | - <img src="./assets/example.gif" width="600" /> |
| 17 | + <img src="./assets/example.gif" width="600" alt="react-native-youtube-bridge 데모" /> |
34 | 18 | </p> |
35 | 19 |
|
36 | | -## 설치 |
37 | | - |
38 | | -```bash |
39 | | -npm install react-native-youtube-bridge |
40 | | - |
41 | | -pnpm add react-native-youtube-bridge |
42 | | - |
43 | | -yarn add react-native-youtube-bridge |
44 | | - |
45 | | -bun add react-native-youtube-bridge |
46 | | -``` |
47 | | - |
48 | | -## 사용법 |
49 | | - |
50 | | -```tsx |
51 | | -import { YoutubeView, useYouTubePlayer } from 'react-native-youtube-bridge'; |
52 | | - |
53 | | -function App() { |
54 | | - const videoIdOrUrl = 'AbZH7XWDW_k'; |
55 | | - |
56 | | - // OR useYouTubePlayer({ videoId: 'AbZH7XWDW_k' }) |
57 | | - // OR useYouTubePlayer({ url: 'https://youtube.com/watch?v=AbZH7XWDW_k' }) |
58 | | - const player = useYouTubePlayer(videoIdOrUrl); |
59 | | - |
60 | | - return <YoutubeView player={player} />; |
61 | | -} |
62 | | -``` |
63 | | - |
64 | | -### 이벤트 |
| 20 | +### 주요 특징 |
65 | 21 |
|
66 | | -YouTube iframe API의 상태 변화를 애플리케이션에 전달하기 위해 [이벤트](https://developers.google.com/youtube/iframe_api_reference#Events)를 발생시킵니다. |
67 | | - |
68 | | -`useYouTubeEvent` hook을 사용하여 완벽한 타입 추론을 지원하며, 두 가지 방법으로 이벤트를 쉽게 감지하여 사용할 수 있습니다. |
69 | | - |
70 | | -```tsx |
71 | | -import { YoutubeView, useYouTubeEvent, useYouTubePlayer } from 'react-native-youtube-bridge'; |
72 | | - |
73 | | -function App() { |
74 | | - const player = useYouTubePlayer(videoIdOrUrl); |
75 | | - |
76 | | - // State-based event listening |
77 | | - const playbackRate = useYouTubeEvent(player, 'playbackRateChange', 1); |
78 | | - const isMuted = useYouTubeEvent(player, 'muteChange', false); |
79 | | - const progress = useYouTubeEvent(player, 'progress', progressInterval); |
80 | | - |
81 | | - // Callback-based event listening |
82 | | - useYouTubeEvent(player, 'ready', (playerInfo) => { |
83 | | - console.log('Player is ready!'); |
84 | | - Alert.alert('Alert', 'YouTube player is ready!'); |
85 | | - }); |
86 | | - |
87 | | - useYouTubeEvent(player, 'autoplayBlocked', () => { |
88 | | - console.log('Autoplay was blocked'); |
89 | | - }); |
90 | | - |
91 | | - useYouTubeEvent(player, 'error', (error) => { |
92 | | - console.error('Player error:', error); |
93 | | - Alert.alert('Error', `Player error (${error.code}): ${error.message}`); |
94 | | - }); |
95 | | - |
96 | | - return <YoutubeView player={player} />; |
97 | | -} |
98 | | -``` |
99 | | - |
100 | | -`muteChange` 이벤트를 구독하면 YouTube 플레이어 기본 UI의 스피커 버튼 또는 `player.mute()` / `player.unMute()` 호출로 변경된 음소거 상태를 실시간으로 받을 수 있습니다. |
101 | | -성능 최적화를 위해 `muteChange`를 구독할 때만 muted tracking이 활성화됩니다. |
102 | | - |
103 | | -`useYouTubeEvent` hook은 callback으로 값을 전달받는 방식과 state로 값을 바로 사용할 수 있는 두 가지 방법을 제공합니다. |
104 | | - |
105 | | -1. Callback 방식: 의존성에 따라 리렌더링이 필요한 경우 4번째 인자에 dependency array를 주입해주면 됩니다. |
106 | | -2. State 방식: |
107 | | - 1. `progress` event의 경우 3번째 인자에 interval 값을 설정할 수 있습니다. (기본값: 1000ms) |
108 | | - 2. 나머지 event의 경우 3번째 인자에 기본 값을 설정할 수 있습니다. |
109 | | - |
110 | | -### 기능 |
111 | | - |
112 | | -YouTube iframe API의 [함수들](https://developers.google.com/youtube/iframe_api_reference#Functions)을 `useYouTubePlayer`를 통해 반환된 player 인스턴스 메서드를 호출하여 음소거, 재생, 볼륨 조절 등 다양한 플레이어 기능을 제어할 수 있습니다. |
113 | | - |
114 | | -```tsx |
115 | | -import { YoutubeView, useYouTubePlayer } from 'react-native-youtube-bridge'; |
116 | | - |
117 | | -function App() { |
118 | | - const player = useYouTubePlayer(videoIdOrUrl); |
| 22 | +- 🎥 **YouTube IFrame Player API** - 네이티브 YouTube 모듈 대신 YouTube iframe player를 사용 |
| 23 | +- 🪝 **Hook 기반 API** - `useYouTubePlayer`로 플레이어를 만들고 `YoutubeView`로 렌더링 |
| 24 | +- 🔔 **타입 안전 이벤트** - `useYouTubeEvent`로 ready, state, progress, mute, error 이벤트 구독 |
| 25 | +- 🌐 **크로스 플랫폼** - iOS, Android, React Native Web 지원 |
| 26 | +- 🧩 **유연한 렌더링 모드** - 기본 inline HTML 또는 외부 WebView 플레이어 페이지 사용 가능 |
| 27 | +- 🧠 **TypeScript 지원** - 플레이어 메서드, 이벤트, source 입력, view props 타입 제공 |
| 28 | +- 🚀 **Expo 친화적** - Expo와 최신 React Native 프로젝트에서 사용하기 좋음 |
119 | 29 |
|
120 | | - const [isPlaying, setIsPlaying] = useState(false); |
121 | | - const [currentTime, setCurrentTime] = useState(0); |
| 30 | +## 빠른 시작 |
122 | 31 |
|
123 | | - const onPlay = useCallback(() => { |
124 | | - if (isPlaying) { |
125 | | - player.pause(); |
126 | | - return; |
127 | | - } |
| 32 | +### 📚 문서 |
128 | 33 |
|
129 | | - player.play(); |
130 | | - }, [isPlaying]); |
| 34 | +전체 문서는 <https://react-native-youtube-bridge-docs.pages.dev/ko/>에서 확인할 수 있습니다. |
131 | 35 |
|
132 | | - const seekTo = (time: number, allowSeekAhead: boolean) => { |
133 | | - player.seekTo(time, allowSeekAhead); |
134 | | - }; |
| 36 | +- [시작하기](https://react-native-youtube-bridge-docs.pages.dev/ko/guide/getting-started/overview.html) |
| 37 | +- [API 레퍼런스](https://react-native-youtube-bridge-docs.pages.dev/ko/guide/usage/api-reference.html) |
| 38 | +- [1.x 문서](https://react-native-youtube-bridge-docs.pages.dev/1.x/ko/) |
| 39 | +- [1.x에서 마이그레이션](https://react-native-youtube-bridge-docs.pages.dev/ko/guide/migration-from-1.x.html) |
135 | 40 |
|
136 | | - const stop = () => player.stop(); |
| 41 | +### 예제 및 데모 |
137 | 42 |
|
138 | | - return ( |
139 | | - <View> |
140 | | - <YoutubeView player={player} /> |
| 43 | +- [📁 예제 프로젝트](/example/) - 예제 React Native 앱 |
| 44 | +- [🌐 웹 데모](https://react-native-youtube-bridge-example.pages.dev/) - 호스팅된 데모 |
| 45 | +- [🤖 Expo Snack](https://snack.expo.dev/@harang/react-native-youtube-bridge) - Expo Snack에서 바로 체험 |
141 | 46 |
|
142 | | - <View style={styles.controls}> |
143 | | - <TouchableOpacity |
144 | | - style={[styles.button, styles.seekButton]} |
145 | | - onPress={() => seekTo(currentTime > 10 ? currentTime - 10 : 0)} |
146 | | - > |
147 | | - <Text style={styles.buttonText}>⏪ -10초</Text> |
148 | | - </TouchableOpacity> |
| 47 | +### 🤖 AI |
149 | 48 |
|
150 | | - <TouchableOpacity style={[styles.button, styles.playButton]} onPress={onPlay}> |
151 | | - <Text style={styles.buttonText}>{isPlaying ? '⏸️ 일시정지' : '▶️ 재생'}</Text> |
152 | | - </TouchableOpacity> |
| 49 | +- [llms.txt](https://react-native-youtube-bridge-docs.pages.dev/ko/llms.txt): 문서 페이지와 설명을 담은 구조화된 색인 파일입니다. |
| 50 | +- [llms-full.txt](https://react-native-youtube-bridge-docs.pages.dev/ko/llms-full.txt): 전체 문서를 하나의 파일로 합친 전체 내용 파일입니다. |
153 | 51 |
|
154 | | - <TouchableOpacity style={[styles.button, styles.stopButton]} onPress={stop}> |
155 | | - <Text style={styles.buttonText}>⏹️ 정지</Text> |
156 | | - </TouchableOpacity> |
| 52 | +### 설치 |
157 | 53 |
|
158 | | - <TouchableOpacity |
159 | | - style={[styles.button, styles.seekButton]} |
160 | | - onPress={() => seekTo(currentTime + 10, true)} |
161 | | - > |
162 | | - <Text style={styles.buttonText}>⏭️ +10초</Text> |
163 | | - </TouchableOpacity> |
164 | | - </View> |
165 | | - </View> |
166 | | - ); |
167 | | -} |
| 54 | +```bash |
| 55 | +npm install react-native-youtube-bridge |
168 | 56 | ``` |
169 | 57 |
|
170 | | -### 초기 플레이어 매개변수 |
171 | | - |
172 | | -YouTube 내장 플레이어의 [매개변수](https://developers.google.com/youtube/player_parameters#Parameters)를 설정하여 초기 재생 환경을 맞춤화할 수 있습니다. |
| 58 | +### 기본 사용법 |
173 | 59 |
|
174 | 60 | ```tsx |
175 | 61 | import { YoutubeView, useYouTubePlayer } from 'react-native-youtube-bridge'; |
176 | 62 |
|
177 | 63 | function App() { |
178 | | - const player = useYouTubePlayer(videoIdOrUrl, { |
179 | | - autoplay: true, |
180 | | - controls: true, |
181 | | - playsinline: true, |
182 | | - rel: false, |
183 | | - muted: true, |
184 | | - }); |
185 | | - |
186 | | - return <YoutubeView player={player} />; |
187 | | -} |
188 | | -``` |
189 | | - |
190 | | -### 스타일 |
191 | | - |
192 | | -YouTube 플레이어의 스타일을 원하는 대로 커스터마이징할 수 있습니다. |
193 | | - |
194 | | -```tsx |
195 | | -function App() { |
196 | | - return ( |
197 | | - <YoutubeView |
198 | | - player={player} |
199 | | - height={400} |
200 | | - width={200} |
201 | | - style={{ |
202 | | - borderRadius: 10, |
203 | | - }} |
204 | | - // 웹 플랫폼 지원 |
205 | | - iframeStyle={{ |
206 | | - aspectRatio: 16 / 9, |
207 | | - }} |
208 | | - // iOS, Android 플랫폼 지원 |
209 | | - webViewStyle={ |
210 | | - { |
211 | | - // ... |
212 | | - } |
213 | | - } |
214 | | - // iOS, Android 플랫폼 지원 |
215 | | - webViewProps={ |
216 | | - { |
217 | | - // ... |
218 | | - } |
219 | | - } |
220 | | - /> |
221 | | - ); |
222 | | -} |
223 | | -``` |
224 | | - |
225 | | -### 재생 진행률 추적 |
226 | | - |
227 | | -- `useYouTubeEvent` hook을 사용하여 `progress` 이벤트의 리스너를 등록하여 재생 진행률을 추적할 수 있습니다. |
228 | | -- 세 번째 인자에 interval 값을 설정하여 해당 간격(ms)마다 이벤트가 호출됩니다. |
229 | | -- interval을 원치 않으면 `0`으로 설정하면 됩니다. |
230 | | -- 기본값은 1000ms입니다. |
231 | | - |
232 | | -```tsx |
233 | | -function App() { |
234 | | - const progressInterval = 1000; |
235 | | - |
236 | | - const player = useYouTubePlayer(videoIdOrUrl); |
237 | | - const progress = useYouTubeEvent(player, 'progress', progressInterval); |
| 64 | + const player = useYouTubePlayer('AbZH7XWDW_k'); |
238 | 65 |
|
239 | 66 | return <YoutubeView player={player} />; |
240 | 67 | } |
241 | 68 | ``` |
242 | 69 |
|
243 | | -### 플레이어 렌더링 및 소스 설정 (ios, android) |
244 | | - |
245 | | -**인라인 HTML vs 웹뷰 모드** |
246 | | -YouTube 플레이어 렌더링 방식을 제어하고 호환성을 위한 소스 URL을 설정합니다. |
247 | | - |
248 | | -1. **인라인 HTML 모드** (`useInlineHtml: true`)는 앱 내에서 직접 HTML을 로드하여 플레이어를 렌더링합니다. (default) |
249 | | -2. **웹뷰 모드** (`useInlineHtml: false`)는 외부 플레이어 페이지를 로드합니다. |
250 | | - - 기본 URI는 https://react-native-youtube-bridge.pages.dev 입니다. |
251 | | - - 직접 제작한 커스텀 플레이어 페이지를 외부 웹뷰로 사용하려면, `@react-native-youtube-bridge/web`으로 플레이어를 구축한 후 `webViewUrl`에 해당 URL을 설정하세요. 자세한 구현 방법은 [웹 플레이어 가이드](https://github.com/react-native-bridges/react-native-youtube-bridge/tree/main/packages/web)를 참고해 주세요. |
252 | | - |
253 | | -> [!NOTE] |
254 | | -> **webViewUrl 활용법** |
255 | | -> |
256 | | -> - `useInlineHtml: true`인 경우: WebView source의 HTML `baseUrl`로 설정됩니다. |
257 | | -> - `useInlineHtml: false`인 경우: WebView source의 `uri`를 override합니다. |
258 | | -> |
259 | | -> **임베드 제한 해결**: 인라인 HTML 사용 시 YouTube iframe에서 `embed not allowed` 오류가 발생하여 영상이 정상적으로 로드되지 않는다면, 웹뷰 모드로 전환하여 외부 플레이어를 통해 YouTube iframe을 로드해주세요. |
260 | | -
|
261 | | -```tsx |
262 | | -// 인라인 HTML (default) |
263 | | -<YoutubeView |
264 | | - player={player} |
265 | | - useInlineHtml |
266 | | -/> |
267 | | - |
268 | | -// 커스텀 플레이어 페이지를 사용한 외부 웹뷰 |
269 | | -<YoutubeView |
270 | | - player={player} |
271 | | - useInlineHtml={false} |
272 | | - // default: https://react-native-youtube-bridge.pages.dev |
273 | | - webViewUrl="https://your-custom-player.com" |
274 | | -/> |
275 | | -``` |
276 | | - |
277 | | -**커스텀 플레이어 페이지** |
278 | | - |
279 | | -직접 제작한 커스텀 플레이어 페이지를 사용하려면, `@react-native-youtube-bridge/web`을 활용하여 React 기반의 플레이어 페이지를 구축할 수 있습니다. |
280 | | - |
281 | | -```tsx |
282 | | -import { YoutubePlayer } from '@react-native-youtube-bridge/web'; |
283 | | - |
284 | | -function CustomPlayerPage() { |
285 | | - return <YoutubePlayer />; |
286 | | -} |
287 | | - |
288 | | -export default CustomPlayerPage; |
289 | | -``` |
290 | | - |
291 | | -> 자세한 내용은 [웹 플레이어 가이드](./packages/web/)를 참고해 주세요. |
292 | | -
|
293 | | -### YouTube oEmbed API |
294 | | - |
295 | | -`useYoutubeOEmbed` 훅을 통해 YouTube 비디오의 메타데이터를 가져올 수 있습니다. |
296 | | -이 훅은 YouTube URL만 지원합니다. |
297 | | - |
298 | | -```tsx |
299 | | -import { useYoutubeOEmbed } from 'react-native-youtube-bridge'; |
300 | | - |
301 | | -function App() { |
302 | | - const { oEmbed, isLoading, error } = useYoutubeOEmbed( |
303 | | - 'https://www.youtube.com/watch?v=AbZH7XWDW_k', |
304 | | - ); |
305 | | - |
306 | | - if (isLoading) return <Text>Loading...</Text>; |
307 | | - if (error) return <Text>Error: {error.message}</Text>; |
308 | | - if (!oEmbed) return null; |
309 | | - |
310 | | - return ( |
311 | | - <> |
312 | | - <Text>{oEmbed.title}</Text> |
313 | | - <Image |
314 | | - source={{ uri: oEmbed?.thumbnail_url }} |
315 | | - style={{ width: oEmbed?.thumbnail_width, height: oEmbed?.thumbnail_height }} |
316 | | - /> |
317 | | - </> |
318 | | - ); |
319 | | -} |
320 | | -``` |
| 70 | +이벤트, 플레이어 제어, 렌더링 모드, WebView 커스터마이징, 마이그레이션은 [전체 문서](https://react-native-youtube-bridge-docs.pages.dev/ko/)를 참고하세요. |
321 | 71 |
|
322 | 72 | ## 기여하기 |
323 | 73 |
|
324 | | -리포지토리 기여 방법과 개발 워크플로우를 알아보려면 [기여 가이드](CONTRIBUTING.md)를 참고하세요. |
| 74 | +프로젝트 기여 방법과 개발 환경 설정은 [기여 가이드](CONTRIBUTING.md)를 참고해 주세요. |
325 | 75 |
|
326 | 76 | ## 라이선스 |
327 | 77 |
|
|
0 commit comments