Spotify API로 내 웹사이트 안에 뮤직 플레이어 만들기 - 2편

이은지·2022년 7월 23일
3
post-thumbnail

지난 글에서 플레이어 생성을 완료했다.
이제 플레이어를 조작해보자! 🎧

🛠 구현 목표

플레이어의 play, pause 기능을 구현한다. 구체화/세분화하면 아래와 같다.

  • 노래 재생 여부에 따라 버튼이 play/pause 중 하나로 바뀐다.
  • 재생 중인 노래가 없는 경우, play 버튼을 누르면 플레이리스트의 첫 곡이 재생된다.
    재생 중인 노래가 있는 경우, play 버튼을 누르면 pause했던 지점부터 노래를 재생한다.
  • pause 버튼을 누르면 노래를 멈춘다.
  • 플레이리스트 내 노래의 재생 버튼을 클릭하면 해당 노래가 재생된다.

✅ Web Playback SDK말고 Web API 써야 해요


제가 가장 많은 삽질을 한 곳이 여기입니다.

https://developer.spotify.com/documentation/web-playback-sdk/reference/
위 링크에 제공되어 있는 Web Player SDK API를 그 어떤 방식으로 활용해봐도 노래가 재생되지 않았다. 분명 play, pause, resume과 같이 그럴싸한 이름의 API들이 친절하게 나열되어 있었는데 🤨 문서에 나와있는 코드를 따라 쳐보고, 바꿔서 쳐봐도 내 플레이어에는 아무 일이 발생하지 않았다.

그렇게 몇 번의 삽질을 하다가 원점으로 돌아가보고자 공식문서를 처음부터 찬찬히 읽던 중, 아래와 같은 문구를 발견할 수 있었다.

이런 건 좀 더 크게 써놓았으면 좋았을 걸 ^_^

요약하자면 "Web Playback SDK는 브라우저 내 로컬 디바이스를 생성하는 역할만 하니까, 디바이스를 컨트롤하기 위해서는 Web API를 사용해라." 라는 내용이었다.

사실 Spotify 공식 문서를 처음 보면, 다 영어로 되어 있는데다 서로 비슷하게 생겨서 뭐가 뭔지 구분 조차 어렵다. 나도 Web Playback SDK랑 Web API 문서를 별 구분 없이 마구잡이로 보고 있었는데, 저 문구를 확인하고 나서야 둘의 용도가 다름을 알 수 있었다.

아무튼 우리가 원하는 기능을 구현하기 위해서는 Web API를 사용해야 한다.

✅ Web API 살펴보기

https://developer.spotify.com/documentation/web-api/reference/#/operations/get-information-about-the-users-current-playback

Web API는 Web Player SDK에 비해 더 방대하기 때문에, 원하는 걸 찾는데 시간이 더 걸렸다.
우리가 원하는 정보는 Web API - References - Player 여기에 있다. 위 링크를 클릭하면 바로 접속할 수 있다.

우리가 생성한 플레이어에 직접 메소드를 호출하는 게 아니라, 플레이어 조작 기능별로 Web API Endpoint가 존재하고, 해당 url로 요청을 보내는 방식이다.

🎧 1. 플레이어 UI 제작

우선 플레이어 UI를 만들어준다.
나는 Player 라는 컴포넌트로 따로 분리해줬다.

  • play 이자 pause 기능을 하는 버튼 하나
  • 노래 제목
  • 노래 앨범 커버

이렇게 세 가지 요소가 들어가게 간단히 만들어주면 된다.
그리고 버튼에 우리가 만들 메서드를 onClick으로 연결해주면 된다.

🎧 2. 원하는 노래 재생하기

uri 이해하기

내가 원하는 노래를 재생하기 위해선 스포티파이에게 내가 원하는 노래가 무엇인지를 알려줘야 한다.
이때 uri를 사용한다. URI는 특정 리소스를 식별하는 통합 자원 식별자(Uniform Resource Identifier)를 의미한다. 스포티파이는 uri로 각 노래를 구분하기 때문에, 재생하고 싶은 노래의 uri를 알아야 한다.

uri를 얻는 방법은 쉽다. 스포티파이 웹 플레이어에 접속한 다음, 원하는 노래에서 ... 버튼을 누르고 공유하기를 살펴보면 URI 복사 버튼이 있다. 맥의 경우 control/option 키 중 하나를 눌러야 복사 버튼이 나타난다.

우리 프로젝트 같은 경우는 애초에 Spotify를 사용해 노래를 크롤링 했기 때문에 데이터셋 자체에 uri가 포함되어 있었다.

API 사용하기

Start/Resume Playback API 링크 👉 이 링크의 API를 사용하면 된다.

Query로 device_id를, 요청 바디에는 uris 를 넣으면 된다.

// 공식문서 예시 코드

const play = ({
  spotify_uri,
  playerInstance: {
    _options: {
      getOAuthToken
    }
  }
}) => {
  getOAuthToken(access_token => {
    fetch(`https://api.spotify.com/v1/me/player/play?device_id=${id}`, {
      method: 'PUT',
      body: JSON.stringify({ uris: [spotify_uri] }),
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${access_token}`
      },
    });
  });
};

play({
  playerInstance: new Spotify.Player({ name: "..." }),
  spotify_uri: 'spotify:track:7xGfFoTpQ2E7fRF5lN10tr',
});

https://developer.spotify.com/documentation/web-playback-sdk/reference/
여기에 예시 코드가 있어서, 이 코드를 가져다 사용했다.

API 사용해 onPlay 함수 만들기

지난 시간에 생성했던 플레이어를 여기서 사용한다.
WebPlayback 이라는 컴포넌트의 useEffect 내에서 플레이어를 생성했다. 해당 코드를 다시 한 번 살펴보자.

useEffect(() => {
    const script = document.createElement("script");
    script.src = "https://sdk.scdn.co/spotify-player.js";
    script.async = true;

    document.body.appendChild(script);

    window.onSpotifyWebPlaybackSDKReady = () => {
      const player = new window.Spotify.Player({
        name: "Web Playback SDK",
        getOAuthToken: (cb: any) => {
          // Run code to get a fresh access token
          cb(token?.replace(/\"/g, ""));
        },
        volume: 0.5,
      });

      setPlayer(player);

      player.addListener("ready", ({ device_id }) => {
        setId(device_id);
        setReady(true);
        console.log("Ready with Device ID", device_id);
      });

      player.addListener("not_ready", ({ device_id }) => {
        console.log("Device ID has gone offline", device_id);
      });
      player.addListener("player_state_changed", (state) => {
        if (!state) {
          return;
        }
        setTrack(state.track_window.current_track);
        setPaused(state.paused);
        setPosition(state.position);
      });

      player.connect();
    };
  }, [token]);

여기서 우리가 사용할 건 두 가지다. 👉 player, device_id
생성한 플레이어와 device_id를 WebPlayback의 상태로 저장해뒀다.(useState)
이를 사용해 API를 호출한다.

📂core/api/spotifysdk.ts 란 파일에 API 호출 함수를 작성해놓고, WebPlayback 컴포넌트에 import 한다. player와 device_id가 WebPlayback 컴포넌트의 state로 관리되고 있기 때문이다. import한 함수에 player와 device_id를 넣어 onPlay라는 함수를 만들었다.

onPlay 함수는 인자로 재생할 노래의 uri를 받는다. uri가 undefined인 채로 호출될 경우 플레이리스트의 첫번째 노래의 uri를 넘겨준다.

근데 이게 같은 비슷한 함수를 불필요하게 두 번 작성하는 느낌이라, 리팩토링 때 아예 플레이어 관련 상태와 함수를 모두 Context API에서 관리하고 필요한 컴포넌트에서 바로 사용할 수 있게 할까 생각 중이다


// WebPlayback.tsx

import { play, pause } from "../core/api/spotifysdk";
const WebPlayback = ({ data }) => {
  const [player, setPlayer] = useState(null);
  const [device_id, setId] = useState("");
  
  const onPlay = (uri: string | undefined, is_new: boolean) => {
    if (uri === undefined) {
      play({
        spotify_uri: data[0].uri,
        device_id,
        position: current_position,
        playerInstance: player,
      });
    }
    play({
      spotify_uri: uri,
      device_id,
      position: is_new ? 0 : current_position,
      playerInstance: player,
    });
  };
  
  ...
  
};

export default WebPlayback;
  
  


// core/api/spotifysdk.js의 play 함수

export const play = ({
  spotify_uri,
  device_id,
  position,
  playerInstance: {
    _options: { getOAuthToken },
  },
}: Props) => {
  getOAuthToken((access_token: string) => {
    fetch(`https://api.spotify.com/v1/me/player/play?device_id=${device_id}`, {
      method: "PUT",
      body: JSON.stringify({ uris: [spotify_uri], position_ms: position }),
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${access_token}`,
      },
    });
  });
};

props로 함수 전달해 플레이어에서 사용하기

onPlay 함수를 props 사용해 Player에 전달해주고, 버튼 클릭 시 onPlay가 실행되게 한다. Player는 props로 current_track을 전달 받기 때문에 onPlay의 인자로 current_track.uri를 넘겨준다.

🎧 3. 일시정지 하기

이건 쉽다 ㅎ_ㅎ 2번과 같은 방식으로
Pause Playback API 👉 이 API를 이용하면 된다.


또 너무 길어져서 다음 글로!!!!
리팩토링 전에 기록하려고 한건데 자꾸 배보다 배꼽이 커지는 느낌이라 괴로워😖

✅ 이번 글에서 구현 완료한 기능 ✅

[x] 노래 재생 여부에 따라 버튼이 play/pause 중 하나로 바뀐다.
[x] 재생 중인 노래가 없는 경우, play 버튼을 누르면 플레이리스트의 첫 곡이 재생된다.
[x] pause 버튼을 누르면 노래를 멈춘다.

[ ] 재생 중인 노래가 있는 경우, play 버튼을 누르면 pause했던 지점부터 노래를 재생한다.
[ ] 플레이리스트 내 노래의 재생 버튼을 클릭하면 해당 노래가 재생된다.

profile
교육학과 출신 서타터업 프론트 개발자 👩🏻‍🏫

1개의 댓글

comment-user-thumbnail
2023년 8월 2일

혹시 무료계정으로 진행하셨나요? 무료 계정을 사용하고 있다면 Spotify Web Playback SDK를 사용하여 오디오를 재생하는 것은 지원되지 않는다는 것 같아서요ㅜㅜ

답글 달기