웹사이트 검색

Spotify API로 웹 음악 플레이어 만들기


새로운 개념을 배우고 있을 때, 실용적인 예제와 작업할 프로젝트가 있는 것이 항상 도움이 됩니다. Spotify API는 일반적으로 웹 API에 대한 훌륭한 소개입니다. 이를 통해 다양한 음악 관련 작업을 수행하는 간단한 유틸리티를 쉽게 구축할 수 있습니다.

API에 대하여

모든 API는 소유자가 제공하기로 선택한 기능과 데이터에 의해 제한됩니다. 하지만 아무것도 없는 것보다 무언가 있는 것이 더 낫고, 스포티파이 API는 아마도 놀랍게도 개방적이고 사용하기 쉽습니다.

API는 두 가지 주요 구성 요소가 있습니다:

  1. 아티스트, 트랙 및 재생목록에 대한 메타데이터를 제공하는 웹 API.
  2. 웹 페이지 내에 Spotify 플레이어를 포함할 수 있게 해주는 웹 재생 SDK.

일반적으로, 이러한 것들은 꽤 제한적입니다. 예를 들어, Spotify는 상업용 스트리밍 앱을 개발하기 위해 Playback SDK를 사용해서는 안 된다고 명시하고 있습니다. 하지만 본인 사용을 위해 만들 수 있는 흥미로운 프로젝트들이 여전히 있으며, Spotify API는 일반적인 웹 API의 좋은 예로, 학습과 연습에 적합합니다.

설정하기

제가 논의할 두 개의 데모는 GitHub 리포지토리에 위치해 있습니다.

이 샘플 앱을 만들기 위해 가장 필요한 것은 스포티파이 계정입니다. 이를 통해 스포티파이 개발자 대시보드에 로그인하고 앱을 만들기 시작할 수 있습니다.

로컬 웹 서버도 필요하며, 이미 설정되어 있을 수 있습니다. 그렇지 않다면 설정하는 것이 꽤 쉽습니다. Spotify 개발을 위한 적절한 위치를 선택하세요. 예를 들어, localhost/spotify 또는 localhost:8080.

한편, Spotify 웹사이트에서 "앱 만들기" 양식을 작성합니다. "웹 API"와 "웹 재생 SDK" 옵션을 모두 선택하고 리디렉션 URI를 설정하세요. 예를 들어, 저는 "top" 데모를 top.html이라는 파일에 호스팅하고 있으며, 이 파일은 http://localhost:8080/top.html에서 제공되므로, 이것이 제 리디렉션 URI 중 하나입니다. 데모에 유사한 구조를 사용하고 있다면, 각 새로운 파일을 리디렉션 URI로 추가하는 것을 잊지 마세요.

앱을 만들면, 클라이언트 ID와 클라이언트 비밀이 모두 앱의 '기본 정보' 페이지에 표시되므로 이를 기록해 두세요.

생산 앱의 경우, 클라이언트 비밀을 숨기기 위한 조치를 취해야 합니다. 이번 로컬 시연에서는 값을 내 JavaScript 파일에 직접 삽입하고 있지만, 클라이언트 비밀을 누구에게도 공개해서는 안 됩니다.

이 시점에서 샘플 파일을 다운로드하고, 로컬 문서 루트로 이동하여 실험을 시작하고 싶을 수 있습니다. 이 기사에서는 코드의 일부분만 다룰 수 있으므로 그렇게 하면 프로세스를 따라갈 수 있을 것입니다.

API를 사용하여 권한 부여하기

애플리케이션을 만드는 첫 번째 단계는 OAuth를 사용하여 인증하는 것입니다. 이는 웹 애플리케이션 인증을 위한 인기 있는 표준이므로 이에 대해 배우는 것이 중요합니다.

OAuth는 HTTP를 통해 액세스 토큰을 전송하는 방식으로 작동하며, 이 프로세스는 충분히 확립되어 있어 도우미 라이브러리를 사용할 수 있습니다. 그럼에도 불구하고 우리의 기본적인 목적을 위해서는 모든 것을 스스로 처리하는 것이 쉽습니다.

나는 access.js 파일에 이 코드를 격리시켰습니다. 첫 번째 단계는 Spotify의 인증 URL인 https://accounts.spotify.com/authorize로 리디렉션하는 것입니다. 사용자의 앱 사용 권한을 요청하기 위해 몇 가지 매개변수를 제공해야 합니다:

  • 응답 유형: 우리가 만들고 있는 샘플 앱의 유형에 대해서는 코드를 사용하세요.
  • client_id: 이것은 앱 설정 중에 기록한 클라이언트 ID입니다.
  • 범위: 권한을 부여하기 위한 공백으로 구분된 범위 문자열. 제가 여기에서 다룰 두 개의 데모에서는 "streaming user-top-read"가 충분하지만, 다른 엔드포인트를 시도해 보려면 전체 범위 집합을 확인하는 것이 좋습니다.
  • redirect_uri: 귀하의 앱에 추가한 유효한 리디렉션 URI입니다.

보안상의 이유로 state 매개변수도 사용해야 하지만, 이 데모에서는 생략해도 괜찮습니다.

access.js에서 authorize 함수는 다음과 같습니다:

function authorize() {
    let base = "https://accounts.spotify.com/authorize",
        params = {
            response_type: "code",
            client_id: CLIENT_ID,
            scope: "streaming ...",
            redirect_uri: REDIRECT_URI,
        };
    window.location.assign(base + "?" + new URLSearchParams(params));
}

    

이것은 브라우저를 관련 매개변수가 있는 Spotify URL로 리디렉션합니다. 이 URL을 살펴보면 다음과 같은 내용을 볼 수 있습니다:

사용자는 범위를 변경하지 않는 한 권한을 한 번만 부여하면 됩니다. 권한이 부여되면, 코드 매개변수와 함께 redirect_uri로 이동됩니다. 따라서 http://localhost:8080/top.html?code=...와 같은 주소로 이동하게 됩니다. 당신의 스크립트는 이 매개변수를 확인하고, 그것이 존재하면 다음 단계로 진행해야 합니다:

let p = new URLSearchParams(window.location.search);
if (p.has("code")) {
    get_access_token(p.get("code"));
}

    

이 두 번째 단계에서는 방금 얻은 코드를 Spotify에 되돌려 보내 액세스 토큰을 받는 것이 포함됩니다. 그런 다음 모든 API 요청에서 액세스 토큰을 사용할 것입니다.

이 기사에 있는 코드는 길이를 줄이기 위해 단순화되었습니다. 더 자세한 내용은 GitHub 리포지토리에서 전체 코드를 참조하세요. 특히, 토큰이 만료될 때 새로 고쳐야 합니다.

async function get_access_token(code) {
    let url = "https://accounts.spotify.com/api/token",
        data = {
            grant_type: "authorization_code",
            code: code,
            redirect_uri: REDIRECT_URI,
        },
        headers = {
            "content-type": "application/x-www-form-urlencoded",
            Authorization: "Basic " + btoa(CLIENT_ID + ":" + CLIENT_SECRET),
        };
    let opts = {
        method: "post",
        headers: headers,
        body: get_post_data(data),
    };
    const response = await fetch(url, opts),
        token = await response.json();
    if (token.access_token) {
		localStorage.setItem("access-token", token.access_token);
    } else {
        console.error("Some kind of error getting access token", token);
    }
}

    

로컬 스토리지 API를 사용하여 액세스 토큰을 기록하고 나중에 가져올 수 있습니다:

let token = localStorage.getItem("access-token");

    

이제 모든 Spotify API 요청에 대해 권한 부여 헤더에 액세스 토큰을 보내야 합니다:

async function spotify_api_get(url) {
    let headers = {
        Authorization: "Bearer " + at.access_token,
    };
    let opts = { headers: headers, method: "GET" };
    const response = await fetch(url, opts);
    return await response.json();
}

    

이 함수는 전체 spotify.js 파일을 사용하며 GET 요청만 처리합니다. 필요한 엔드포인트가 PUT 및 POST 같은 메서드를 요구할 경우 이 파일을 확장하고 유사한 함수를 작성할 수 있습니다.

OAuth는 꽤 복잡한 주제로, 이 기사의 범위를 넘어섭니다. 실험을 위해 간단한 버전을 사용할 수 있습니다; 단, 토큰이 만료되면 수동으로 재설정해야 한다는 점을 기억하세요 (localStorage.removeItem 참조).

메타데이터 정보 가져오기

모든 설정이 완료되고 클라이언트가 승인되었으므로 이제 API를 사용하기 시작할 준비가 되었습니다. 메타데이터를 가져오는 것은 좀 더 간단하므로, 먼저 'top' 샘플(top.html)부터 시작하겠습니다. 이 샘플은 여러분의 최고 아티스트와 그에 대한 간단한 정보를 보여줍니다.

Spotify API 문서를 참조하여 /me/top/artists 엔드포인트를 사용하는 방법을 시작하세요. 추가 매개변수를 피하고 응답 객체에 집중하면 간단하게 유지할 수 있으며, 이 객체의 items 속성에는 당신의 Top 아티스트에 대한 세부 정보가 포함되어 있습니다.

top.js에서 run() 함수는 이 데이터를 가져오고 표시하는 코드를 포함하고 있습니다. 매우 간단합니다: 먼저, API 엔드포인트를 호출하고 반환된 객체를 저장합니다:

let top_artists = await spotify_api_get(
    "https://api.spotify.com/v1/me/top/artists",
    at.access_token,
);

    

다음으로, 응답의 항목을 반복합니다:

top_artists.items.forEach(function (artist) {
    // ...
});

    

그리고 그 반복문 안에서, 각 항목에 대한 데이터를 포함하는 테이블 행을 추가합니다:

tr = tbody.appendChild(document.createElement("tr"));
td = tr.appendChild(document.createElement("td"));
td.appendChild(document.createTextNode(artist.name));
// ...

    

결과는 상위 아티스트와 그들의 관련 장르가 있는 간단한 테이블입니다; 내 것은 이렇게 생겼습니다:

스포티파이 플레이어 사용하기

메타데이터는 흥미롭지만, 작동하는 스포티파이 플레이어가 아마 더 유용할 것입니다. 제한이 있긴 하지만, API는 본질적으로 재생에 대한 완전한 제어를 제공하므로, 약간의 노력으로 자신만의 클라이언트를 만들 수 있습니다.

가장 큰 마찰 지점은 API가 당신의 클라이언트와 동시에 Spotify를 실행할 것을 강력하게 요구한다는 것입니다. 이것은 당신의 개발에 큰 문제가 되지 않을 것이지만, 당신이 개발할 수 있는 잠재적인 앱을 제한할 수 있습니다. 당신은 장치로서 Spotify 앱을 API 클라이언트에 명시적으로 연결해야 합니다.

게다가, 웹 앱을 새로 고칠 때마다 이 작업을 수행해야 하므로 번거로울 수 있습니다. 이를 상기시켜주는 오류 메시지를 데모에 추가했으며, 이는 약간의 고통을 덜어줍니다!

player.html 파일은 앨범 아트워크, 재생 컨트롤 및 탐색을 가능하게 하는 진행 표시기를 갖춘 간단한 클라이언트를 제공합니다. 관련 기능의 대부분은 player.js에 있습니다.

첫 번째로 해야 할 일은 Spotify의 SDK 스크립트를 삽입하는 것입니다:

<script src="https://sdk.scdn.co/spotify-player.js"></script>

    

데모에서, 나는 이를 동적으로 주입합니다. 이렇게 하면 스크립트가 먼저 액세스 토큰을 체크하고 필요할 경우 이를 얻을 수 있습니다. 유효한 액세스 토큰이 없으면 SDK 스크립트에서 오류를 생성할 수 있습니다.

다음으로, 스크립트는 새로운 Player 인스턴스를 생성하고 연결을 시도합니다:

window.onSpotifyWebPlaybackSDKReady = () => {
    player = new Spotify.Player({
        name: "bobbykjack spotify client",
        getOAuthToken: (cb) => {
            cb(at.access_token);
        },
        volume: 0.5,
    });
    setup_player_state_event();
    player.connect().then((success) => {
        if (success) {
            check_device_connected();
        } else {
            console.error("Oops, failed to connect to spotify");
        }
    });
};

    

플레이어는 유효한 액세스 토큰과 Spotify 앱에서 연결할 장치로 나타날 이름을 제공해야 합니다. check_device_connected 및 check_player_state 메서드는 장치 연결 여부를 확인합니다. 장치가 연결되지 않으면 스크립트는 제가 앞서 언급한 "연결해 주세요" 오류를 표시합니다.

플레이어 객체가 설정되고 연결되면, 추가 메서드를 호출하여 트랙을 로드하고 재생을 제어할 수 있습니다. 이러한 메서드는 Web Playback SDK 참조 문서에 자세히 설명되어 있습니다. 샘플 플레이어는 pause, seek, resume, togglePlay 및 getCurrentState와 같은 여러 메서드를 사용합니다.

일부 기능은 페이지의 버튼을 해당 SDK 메서드에 연결하기만 하면 됩니다. 예를 들어, 여기에 재생 버튼에 대한 코드가 있습니다:

play.addEventListener("click", function (ev) {
    player.resume().then(() => {
        play.setAttribute("disabled", "1");
        pause.removeAttribute("disabled");
    });
});

    

클릭하면 버튼은 Player.resume 메서드를 호출하고, 그 다음 자신을 비활성화하고 일시 정지 버튼을 활성화합니다.

일부 기능은 조금 더 복잡합니다. 트랙이 변경되면 player_state_changed 이벤트가 동일한 이름의 메서드를 호출하여 디스플레이를 업데이트하며, 예를 들어 앨범 아트워크를 변경합니다:

let src = get_image(arg.track_window.current_track.album.images, 300),
//...
set_attr("#currently_playing div.track-meta img", "src", src);

    

현재 트랙 진행 상황을 보여주기 위해, 코드는 Player.getCurrentState 메소드를 폴링합니다. 이 메소드는 현재 트랙 위치를 포함하여 재생에 대한 다양한 세부 정보를 담고 있는 객체를 반환합니다:

function check_player_state(state) {
    //...
    update_time(".position", state.position);
    //...
}

    

마지막으로, 진행 입력을 드래그하여 트랙 위치를 변경할 수 있습니다:

document
    .querySelector("#track-progress")
    .addEventListener("input", handle_seek_input);
function handle_seek_input(ev) {
    if (seek_timer) {
        window.clearTimeout(seek_timer);
    }
    seek_timer = window.setTimeout(function () {
        seek_to_ms(ev.target.value);
    }, 200);
}
function seek_to_ms(position) {
    player.pause().then(function () {
        player.seek(position).then(function () {
            player.resume();
            update_time(".position", position);
        });
    });
}

    

타이머가 Player.seek 메서드를 호출하기 전에 인위적이지만 작은 지연을 주기 위해 사용된다는 점에 유의하세요. 이 지연이 없으면 진행 바를 잠깐 드래그하는 동안 수백 번의 API 호출이 발생할 수 있으며, 이는 피하는 것이 가장 좋습니다.

많은 API와 마찬가지로, Spotify는 너무 많은 요청으로 서비스를 압도하지 않도록 속도 제한을 사용합니다. 하지만 이 제한은 꽤 관대하며, 이러한 샘플로 문제를 겪지 않을 것입니다. 만약 예상대로 작동하지 않는 경우에는 429 오류가 있는지 콘솔을 확인해 보세요.

최종 앱은 Spotify와 동기화되는 기본 플레이어를 제공합니다.


이 간단한 데모를 통해 배우고 Spotify API로 작업하기 위한 코드를 작성하기 시작할 수 있기를 바랍니다. 제한적일 것처럼 보일 수 있지만, 간단한 클라이언트를 개선하여 Spotify 경험을 향상시킬 수 있는 많은 수정 사항이 있습니다.

나는 항상 재생목록에 전체 앨범이 포함될 수 있어야 한다고 느꼈기 때문에, 이 기능은 내가 곧 작업할 예정인 기능입니다. 또한 YouTube와 같은 다른 API를 조사하여 Spotify 플레이어 또는 메타데이터와 데이터를 결합하고 두 가지의 장점을 모두 제공하는 앱을 만들 수도 있습니다.