Spotify는 전세계에서 가장 많이 사용하는 음악 스트리밍 서비스다. 이번에 React Native로 Spotify 노래 재생을 구현하게 되였기에 해당 과정을 기록한다.
0. 들어가기 전
이번에는 React Native Cli를 기반으로한 React Native 프로젝트에서 해당 기능을 구현했으며, 사용한 버전은 아래와 같다.
"react-native": "0.70.1"
1. 구현 방법 고려하기
Spotify에서는 개발을 위한 여러 개의 API와 SDK를 제공한다. 따라서 내 목표인 Spotify의 음악 스트리밍을 구현할 수 있는 방법이 여러개가 존재한다.
아래는 Spotify의 개발자 문서가 적힌 사이트인데, 들어가보면 Web API, SDK 등 여러가지를 지원하는 것을 확인할 수 있다.
내가 고려한 방법들은 아래와 같다.
- Webview를 기반으로 Web API와 Web Playback SDK를 사용한 구현
- RN을 위한 여러 Custom SDK를 이용한 구현
- Native SDK를 이용한 구현
- React Native Sound의 Sound 객체를 이용한 구현
위의 있는 방법들을 차례로 해본 결과 내가 구현에 성공을 한 방법은 결국은 5번이다.
(1) Webview를 기반으로 Web API를 사용한 구현
Spotify에서는 웹 기반의 API와 웹에서 사용 가능한 SDK를 제공하여 쉽게 음악 스트리밍을 웹에서 구현할 수 있었다. 나는 아래의 블로그를 참고하여, HTML + JS + SDK로 웹에서 구동되는 플레이어는 구현을 아래와 같이 했다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>Spotify Web Playback SDK Quick Start</h1>
<button id="togglePlay">Toggle Play</button>
<script src="https://sdk.scdn.co/spotify-player.js"></script>
<script>
window.onSpotifyWebPlaybackSDKReady = () => {
const token = [access Token]
const player = new Spotify.Player({
name: "Web Playback SDK Quick Start Player",
getOAuthToken: (cb) => {
cb(token);
},
volume: 0.5,
});
alert(`token : ${token}`);
player.addListener("ready", ({ device_id }) => {
console.log("Ready with Device ID", device_id);
deviceId = device_id;
});
player.addListener("not_ready", ({ device_id }) => {
alert(`Not Ready : ${device_id}`);
console.log("Device ID has gone offline", device_id);
});
player.addListener("initialization_error", ({ message }) => {
alert(message);
console.log(message);
});
player.addListener("authentication_error", ({ message }) => {
console.log(message);
});
player.addListener("account_error", ({ message }) => {
console.log(message);
});
const play = ({
spotify_uri,
playerInstance: {
_options: { getOAuthToken },
},
}) => {
getOAuthToken((access_token) => {
fetch(
`https://api.spotify.com/v1/me/player/play?device_id=${deviceId}`,
{
method: "PUT",
body: JSON.stringify({ uris: [spotify_uri] }),
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${access_token}`,
},
}
);
});
};
document.getElementById("togglePlay").onclick = function () {
alert("HELLO");
play({
playerInstance: player,
spotify_uri: "spotify:track:7xGfFoTpQ2E7fRF5lN10tr",
});
};
player.connect();
};
</script>
</body>
</html>
하지만 문제가 존재했는 데, 웹뷰로 띄었을 떄 이상하게도 SDK를 사용할 수 없어 자꾸 initialization Error가 발생하였다. 이에 대해 여러 고생을 하면서 해결을 해볼려고 했지만 결국 해결을 못 하여 해당 방법은 포기하였다.
(2) RN을 위한 여러 Custom SDK를 이용한 구현
1번 방법이 실패하자, Web SDK를 RN에서 사용할 수 있게 만들어둔 모듈을 탐색하니 역시 여러개가 나왔다.
나는 둘 다 시도를 해보았으나, react-native-spotify는 빌드 과정에서 패키지를 찾을 수 없다는 지속적인 오류가 발생하여 건너갔으며, react-native-spotify-remote는 가장 대중적으로 많이 사용하는 것 같고, 여러 자료도 많아 시도를 지속적으로 했으나, https://github.com/cjam/react-native-spotify-remote/issues/166 해당 이슈로 인하여 도저히 구현이 안 되서 포기했다.
(3) Native SDK를 이용한 구현
Native SDK에서는 스포티파이 어플리케이션의 원격 조정 할 수 있는 기능만 제공하여 구현 불가능하다는 결론을 내렸다.
2. React Native Sound의 Sound 객체를 이용한 구현
먼저 react-native-sound 모듈을 설치하자
npm install react-native-sound
나는 Spotify Web API 중에서 음악을 가져오는 API도 같이 사용하기에, 음악 리스트 스크린에서 음악 리스트 Web API를 호출하고 그 음악을 눌렀을 때 플레이어로 이동하여 음악이 실행되는 구조였다.
따라서 음악 리스트를 보면 Spotify에서 음악을 재생할 수 있는 URI를 제공해준다.
내가 테스트용으로 사용한 API다. 해당 API를 호출할 경우에 아래와 같은 데이터를 얻게 된다.
{
"href": "https://api.spotify.com/v1/me/tracks?offset=0&limit=20&locale=ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7",
"items": [
{
"added_at": "2023-10-14T16:59:18Z",
"track": {
"album": {
"album_type": "album",
"artists": [
{
"external_urls": {
"spotify": "https://open.spotify.com/artist/6OwKE9Ez6ALxpTaKcT5ayv"
},
"href": "https://api.spotify.com/v1/artists/6OwKE9Ez6ALxpTaKcT5ayv",
"id": "6OwKE9Ez6ALxpTaKcT5ayv",
"name": "AKMU",
"type": "artist",
"uri": "spotify:artist:6OwKE9Ez6ALxpTaKcT5ayv"
}
],
"external_urls": {
"spotify": "https://open.spotify.com/album/1eu07xRE0vQfN5et0Y3DAy"
},
"href": "https://api.spotify.com/v1/albums/1eu07xRE0vQfN5et0Y3DAy",
"id": "1eu07xRE0vQfN5et0Y3DAy",
"images": [
{
"height": 640,
"url": "https://i.scdn.co/image/ab67616d0000b27378551e802bd7b81d7af67dfb",
"width": 640
},
{
"height": 300,
"url": "https://i.scdn.co/image/ab67616d00001e0278551e802bd7b81d7af67dfb",
"width": 300
},
{
"height": 64,
"url": "https://i.scdn.co/image/ab67616d0000485178551e802bd7b81d7af67dfb",
"width": 64
}
],
"is_playable": true,
"name": "PLAY",
"release_date": "2014-04-07",
"release_date_precision": "day",
"total_tracks": 11,
"type": "album",
"uri": "spotify:album:1eu07xRE0vQfN5et0Y3DAy"
},
"artists": [
{
"external_urls": {
"spotify": "https://open.spotify.com/artist/6OwKE9Ez6ALxpTaKcT5ayv"
},
"href": "https://api.spotify.com/v1/artists/6OwKE9Ez6ALxpTaKcT5ayv",
"id": "6OwKE9Ez6ALxpTaKcT5ayv",
"name": "AKMU",
"type": "artist",
"uri": "spotify:artist:6OwKE9Ez6ALxpTaKcT5ayv"
}
],
"disc_number": 1,
"duration_ms": 193321,
"explicit": false,
"external_ids": {
"isrc": "KRB471400414"
},
"external_urls": {
"spotify": "https://open.spotify.com/track/6qkx0tenDglbF21CU4wa1k"
},
"href": "https://api.spotify.com/v1/tracks/6qkx0tenDglbF21CU4wa1k",
"id": "6qkx0tenDglbF21CU4wa1k",
"is_local": false,
"is_playable": true,
"name": "200%",
"popularity": 63,
"preview_url": "https://p.scdn.co/mp3-preview/7f3b1a226d83b86924194c89aff6d92d840bef4d?cid=d411b41e0fe24fdb93f556b260b0a927",
"track_number": 2,
"type": "track",
"uri": "spotify:track:6qkx0tenDglbF21CU4wa1k"
}
}
],
"limit": 20,
"next": null,
"offset": 0,
"previous": null,
"total": 1
}
자 이 데이터를 자세히 보면 preview_url이나 href에서 해당 음원의 주소를 cdn으로 되어있는 것을 전달해주는 것을 알 수 있다. 나는 이 데이터를 가지고 Sound 객체를 생성하여 구현했다.
// ** src/screen/PlayerScreen/index.js
// ** React Imports
import { useEffect, useState } from 'react';
// ** Component Imports
import PlayerScreenView from './player-screen';
// ** Utils Imports
import Sound from 'react-native-sound';
const PlayerScreen = ({
audioUrl = 'https://p.scdn.co/mp3-preview/7f3b1a226d83b86924194c89aff6d92d840bef4d?cid=d411b41e0fe24fdb93f556b260b0a927',
}) => {
const [sound, setSound] = useState(null);
const [isPlay, setIsPlay] = useState(false);
useEffect(() => {
const sound = new Sound(audioUrl, null);
setSound(sound);
}, []);
const handlePlay = () => {
if (sound) {
sound.play();
setIsPlay(true);
}
};
const handleStop = () => {
if (sound && isPlay) {
sound.pause();
setIsPlay(false);
}
};
return <PlayerScreenView handlePlay={handlePlay} handleStop={handleStop} />;
};
export default PlayerScreen;
// ** src/screen/PlayerScreen/player-screen.js
// ** RN Imports
import { Button, View } from 'react-native';
const PlayerScreenView = ({ handlePlay, handleStop }) => {
return (
<View style={{ flex: 1 }}>
<Button title="Play" onPress={handlePlay} />
<Button title="Stop" onPress={handleStop} />
</View>
);
};
export default PlayerScreenView;
리스트로 부터 넘겨받을 Audio Uri를 선언한다. 나는 리스트와의 연동이 구현이 미흡해서 default value를 테스트용으로 삽입해서 사용헀다. 넘어오는 audioUrl를 기반으로 Sound 객체를 생성하고, Button이 클릭 될 때 Sound의 play(), pause() 메서드를 이용하여 노래를 출력 하고 멈추는 코드를 구현했다.
3. 마무리하며..
금방할 거 같았던 작업을 생각보다 여러 이슈를 겪으며 오래 걸렸는 데, 그래도 나름 잘 구현이 된 거 같아서 나쁘진 않다. 다만 Spotify 쪽에서 React Native나 Flutter 쪽 SDK를 공식 지원해준다면 좀 더 편해지지 않을까 싶긴헀으며, 음악 재생을 지원해주는 게 신기했는 데, Spotify에 내 계정이 아니라 각 사용자들이 계정과 프리미엄 라이센스를 구독해야한다는 점이 있기에 실 Product에서는 사용하기엔 애매한 거 같다.
참고 자료
'F.E > React Native' 카테고리의 다른 글
[ERROR] expo start 에러 (0) | 2022.05.02 |
---|---|
[React Native] expo를 활용하여 프로젝트 생성 (0) | 2022.05.02 |