django spotify API access token, playlist SA

django spotify API access token, playlist SA

·

7 min read

왜 spotify를 선택했는가?

  1. 방대한 음악 데이터베이스 : spotify는 전 세계적으로 가장 큰 음악 스트리밍 서비스 중 하나로, 다양한 장르와 아티스트의 음악을 제공한다.

  2. 사용하기 쉬운 문서 및 인터페이스 : spotify api는 잘 문서화되어 있어 다양한 api 엔드포인트와 사용법을 자세히 안내하고 있어 개발자들이 쉽게 사용할 수 있다.

  3. 많은 사용자 기반 : spotify는 전 세계적으로 많은 사용자를 보유하고 있어, 사용자들이 자신의 계정과 플레이리스트를 손쉽게 통합할 수 있다.

  4. 빠른 응답 시간 : 빠르고 안정적인 응답 시간을 제공하여, 애플리케이션이 신속하게 사용자 요청에 응답할 수 있다.


우리 사이트에서는 다양한 플레이리스트를 제공하기를 원했다.

국내 플롯폼인 바이브, 멜론, 플로 등을 선택할 수도 있었지만,

보다 다양한 사용자와 규모가 큰 플랫폼인 스포티파이가 해당 프로젝트에 더 적합했으며,

다른 플랫폼에 비해 접근성이 좋았기 때문에 spotify api를 선택했다.


spotify api를 사용하기 위해서는 access token을 발급 받아야 한다.

spotify api를 시작하기 위한 방법

access token 발급 방법 (Client Credentials Flow)

  1. Create an app

    대시보드로 이동하여 앱 만들기를 한다.

  2. Request an access token

    대시보드에서 앱 만들기를 다 했다면, 토큰을 요청해보자


access token 발급을 위한 필수 입력 사항

curl -X POST "https://accounts.spotify.com/api/token" \
     -H "Content-Type: application/x-www-form-urlencoded" \
     -d "grant_type=client_credentials&client_id=your-client-id&client_secret=your-client-secret"

대시보드에서 애플리케이션을 선택하여 client_id, client_secret을 확인한다.

액세스 토큰을 발급받기 위해 https://accounts.spotify.com/api/token 으로 POST 요청을 보낸다.

  • curl -X POST : url을 사용하여 POST 요청을 보낸다.

  • https://accounts.spotify.com/api/token : 요청을 보낼 url

  • -H "Content-Type: application/x-www-form-urlencoded" : 요청의 콘텐츠 타입 지정

  • -d "grant_type=client_credentials&client_id=your-client-id&client_secret=your-client-secret" : 요청 본문에 포함될 데이터 지정

    • grant_type=client_credentials : 클라이언트 자격 증명을 사용하여 토큰을 발급받겠다는 의미 (토큰 발급 유형 지정)

    • client_id=your-client-id : 클라이언트 id (Spotify Developer Dashboard에서 획득)

    • client_secret=your-client-secret : 클라이언트 시크릿 (Spotify Developer Dashboard에서 획득)

해당 순서대로 하면 api를 사용할 준비가 되었다.

자세한 설명은 아래의 코드를 보도록 하자


access token 발급

def get_access_token():
    encoded = base64.b64encode(f"{CLIENT_ID}:{CLIENT_SECRET}".encode("utf-8"))
    headers = {
        "Authorization": f'Basic {encoded.decode("utf-8")}',
        "Content-Type": "application/x-www-form-urlencoded"
    }
    data = {
        "grant_type": "client_credentials",
    }
    response = requests.post(TOKEN_URL, headers=headers, data=data)
    response_data = response.json()
    return response_data.get("access_token")

encoded = base64.b64encode(f"{CLIENT_ID}:{CLIENT_SECRET}".encode("utf-8"))

client id와 client secret을 결합한 후 base64로 인코딩하며, 보안을 위해 자격 증명을 인코딩하는 과정

headers = {"Authorization", "Content-Type"}
data = {"grant_type": "client_credentials"}

인증해야하는 정보를 넣으며, 해당 정보를 사용하여 access token을 요청한다.

response = requests.post(TOKEN_URL, headers=headers, data=data)

headers와 data를 사용하여 토큰 url로 POST 요청을 보낸다.

response_data = response.json()

응답을 json 형식으로 반환한다.

return response_data.get("access_token")

응답 데이터에서 access token을 추출하여 반환한다.

get_access_token 함수는 api와 통신하기 위한 token 얻는 과정을 자동화한다.

이 token은 api 호출 시 인증을 위해 사용된다.


playlist 조회

class PlaylistDataAPIView(APIView):
    def get(self, request):
        access_token = get_access_token()

        if not access_token:
            return Response({"error": "토큰이 유효하지 않습니다."}, status=400)

        # spotify api 호출 헤더
        headers = {"Authorization": f"Bearer {access_token}"}

        # spotify api 호출
        spotify_api = requests.get(
            "https://api.spotify.com/v1/browse/featured-playlists", headers=headers
        )
        spotify_data = spotify_api.json()

        playlists = []
        for item in spotify_data.get("playlists", {}).get("items", []):
            playlist = {
                "name": item["name"],  # 플레이리스트 이름
                "link": item["external_urls"]["spotify"],  # 플레이리스트 링크
                "image_url": (
                    item["images"][0]["url"] if item["images"] else None
                ),  # 플레이리스트 이미지 URL (있는 경우)
            }
            playlists.append(playlist)
        return Response(playlists, status=200)

access_token = get_access_token()

get_access_token 함수를 호출하여 spotify api에 접근하기 위한 access token을 가져온다.

if not access_token:

access token이 없으면 에러 메세지와 400 상태 코드 반환

headers = {"Authorization": f"Bearer {access_token}"}

access token을 사용하여 spotify api 호출 시 사용할 인증 헤더를 설정한다.

spotify_api = requests.get("https://api.spotify.com/v1/browse/featured-playlists", headers=headers)

spotify api를 호출하여 추천 플레이리스트 endpoint에 GET 요청을 보낸다.

spotify_data = spotify_api.json()

응답을 json 형식으로 변환

        playlists = []
        for item in spotify_data.get("playlists", {}).get("items", []):
            playlist = {
                "name": item["name"],  # 플레이리스트 이름
                "link": item["external_urls"]["spotify"],  # 플레이리스트 링크
                "image_url": (
                    item["images"][0]["url"] if item["images"] else None
                ),  # 플레이리스트 이미지 URL (있는 경우)
            }
            playlists.append(playlist)

플레이리스트 데이터 추출

응답 데이터에서 플레이리스트 정보를 추출하여 playlists 리스트에 저장(append)하며,

응답 데이터에서 각 플레이리스트 항목을 순회한 후 이름, 추출한 데이터를 playlists 리스트에 추가

return Response(playlists, status=200)

추출한 플레이리스트 데이터를 200 코드와 함께 반환

client로부터 GET 요청을 받으면 spotify api에 접근하기 위한 access token을 가져온다.

토큰이 유효하지 않으면 에러를 반환하고, 유효한 경우 spotify의 추천 playlist를 가져오고,

가져온 데이터에서 이름, 링크, 이미지 url을 추출하여 리스트에 저장한 후, json 형식으로 반환한다.


playlist 검색

# playlist 검색
class PlaylistSearchAPIView(APIView):
    def get(self, request):
        search = request.query_params.get("query", None)

        access_token = get_access_token()
        if not access_token:
            return Response({"error": "토큰이 유효하지 않습니다."}, status=400)

        # spotify api 호출 헤더, 검색 api에 전달할 파라미터
        headers = {"Authorization": f"Bearer {access_token}"}
        params = {"q": search, "type": "playlist"}

        # spotify api 호출
        spotify_api = requests.get(
            "https://api.spotify.com/v1/search", headers=headers, params=params
        )

        spotify_data = spotify_api.json()

        playlists = []
        for item in spotify_data.get("playlists", {}).get("items", []):
            playlist = {
                "name": item["name"],  # 플레이리스트 이름
                "link": item["external_urls"]["spotify"],  # 플레이리스트 링크
                "image_url": (
                    item["images"][0]["url"] if item["images"] else None
                ),  # 플레이리스트 이미지 URL (있는 경우)
            }
            # 리스트에 플레이리스트 추가
            playlists.append(playlist)
        return Response(playlists, status=200)

search = request.query_params.get("query", None)

query 값을 추출하여 검색어로 사용하며, 값이 없으면 None을 기본값으로 설정한다.

access_token = get_access_token()

get_access_token 함수를 호출하여 spotify api에 접근하기 위한 access token을 가져온다.

if not access_token:

access token이 없으면 에러 메세지와 400 상태 코드 반환

headers = {"Authorization": f"Bearer {access_token}"}

params = {"q": search, "type": "playlist"}

access token을 사용하여 spotify api 호출 시 사용할 인증 헤더를 설정하며,

검색어와 검색유형(playlist)을 설정한 파라미터를 정의한다.

spotify_api = requests.get("https://api.spotify.com/v1/search", headers=headers, params=params)

spotify api의 검색 endpoint에 GET 요청을 보낸다.

spotify_data = spotify_api.json()

응답을 json 형식으로 변환

        playlists = []
        for item in spotify_data.get("playlists", {}).get("items", []):
            playlist = {
                "name": item["name"],  # 플레이리스트 이름
                "link": item["external_urls"]["spotify"],  # 플레이리스트 링크
                "image_url": (
                    item["images"][0]["url"] if item["images"] else None
                ),  # 플레이리스트 이미지 URL (있는 경우)
            }
            playlists.append(playlist)

플레이리스트 데이터 추출

응답 데이터에서 플레이리스트 정보를 추출하여 playlists 리스트에 저장(append)하며,

응답 데이터에서 각 플레이리스트 항목을 순회한 후 이름, 추출한 데이터를 playlists 리스트에 추가

return Response(playlists, status=200)

추출한 플레이리스트 데이터를 200 코드와 함께 반환

client로부터 GET 요청을 받으면 쿼리 매개변수에서 검색어를 추출하고

그 후, spotify api에 접근하기 위한 access token을 가져온다.

토큰이 유효하지 않으면 에러를 반환하고, 유효한 경우 spotify의 검색 endpoint를 호출하여 검색어에 해당하는 플레이리스트를 가져온다.

가져온 데이터에서 이름, 링크, 이미지 url을 추출하여 리스트에 저장한 후, json 형식으로 반환한다.


access token cache 작업

위에서 작성했던 로직은 페이지를 새로고침 할 때마다 access token을 받아오는 형태였다.

해당 방법을 사용하면 토큰이 자주 발급되며 불필요한 리소스가 낭비되며, 서버 부하를 증가시킨다.

그래서 cache와 redis를 활용하여 token의 유효시간을 관리하고 재사용하는 방법을 사용했다.

  1. token caching

    • redis를 캐시 저장소로 사용하여 발급된 access token 저장

    • 일정 시간 동안 유효한 토큰을 재사용

  2. token 유효 시간 관리

    • 서버에서 token의 유효시간을 설정하고, redis에 저장된 token을 일정 시간동안 유효하게 설정

    • 유효 시간이 지난 토큰은 자동으로 갱신

    • 클라이언트가 새로고침 요청을 보낼때 마다 새로운 token을 발급하는 대신 redis에서 해당 token을 조회하여 유효한 경우 해당 token 반환

# 토큰 발급
def get_access_token():
    access_token = cache.get('spotify_access_token')    
    if access_token is None:   
        encoded = base64.b64encode(f"{CLIENT_ID}:{CLIENT_SECRET}".encode("utf-8"))
        headers = {
            'Authorization': f'Basic {encoded.decode("utf-8")}',
            "Content-Type": "application/x-www-form-urlencoded"
        }
        data = {
            "grant_type": "client_credentials",
        }
        response = requests.post(TOKEN_URL, headers=headers, data=data)
        response_data = response.json()

        access_token = response_data.get('access_token')
        expires_in = 30*60

        cache.set('spotify_access_token', access_token, timeout=expires_in)
    return access_token

왜 캐싱작업을 했나요?

새로 고침을 할때마다 토큰을 받아오는 형태였는데, 해당 방법을 사용하면 토큰이 자주 발급되며 불필요한 리소스가 낭비되며 서버 부하를 증가시키기 때문에 캐시를 사용하여 토큰의 유효시간을 관리하고 재사용하려고 사용했습니다.

레디스는 왜 사용했나요?

토큰 캐싱과 같은 자주 사용되는 데이터에 대해 빠른 응답 속도를 제공하며, TTL 기능을 사용하여 키가 일정시간이 지나면 자동으로 삭제되도록 하기 위해 사용

redis ttl?

time to live로 각 키에 대해 만료 시간을 설정하여 특정 시간이 지나면 자동으로 삭제되도록 한다.
이를 통해 만료된 데이터를 효율적으로 관리하고, 유효하지 않은 데이터를 사용할 위험을 줄일 수 있다.

각 키에 TTL을 설정하여 특정 키가 일정 시간이 지나면 자동으로 삭제되도록 함

-> 네트워크 리소스 낭비를 방지함


spotify 라이브러리 동작 방식

이번 프로젝트에서 플레이리스트 관련 spotify api를 가져오는 것은

라이브러리를 사용한게 아닌 http 요청을 직접 보내서 사용하는 방식이다.

  • request를 사용하여 spotify api에 http 요청을 직접 보냈다.

  • request를 사용하여 필요한 http 요청을 직접 작성하고, spotify의 엔드포인트에 직접 요청을 보내고 응답을 처리함

토큰 발급

response = requests.post(TOKEN_URL, headers=headers, data=data)
response_data = response.json()

플레이리스트 데이터 가져오기

  • requests.get으로 spotify의 플레이리스트 api에 http 요청을 직접 보낸 후 json 형식으로 받아와 처리
spotify_api = requests.get(
    "https://api.spotify.com/v1/browse/featured-playlists?limit=40", headers=headers
)
spotify_data = spotify_api.json()

Spotify API의 데이터 접근성이 좋다?

다양한 방식으로 음악 데이터에 접근할 수 있도록 광범위한 엔드포인트 제공 → 데이터를 손쉽게 활용 가능하다.

  • 다양한 엔드포인트 제공

    • spotify api는 앨범, 아티스트, 트랙, 플레이리스트 등 다양한 엔드포인트를 제공하여 필요한 특정 데이터를 쉽게 가져올 수 있다. 특정 아티스트의 인기 트랙이나 사용자가 만든 플리를 가져올 수 있음
  • 사용자 데이터 접근

    • 사용자의 플리, 최근에 들은 트랙, 팔로우 아티스트 등 다양한 사용자 데이터를 가져올 수 있다.
  • 검색 기능

    • 검색으로 사용자가 원하는 음악, 앨범 등을 쉽게 찾을 수 있으며, 필터와 파라미터 지원


실제 코드 로직에서는

1.⁠⁠search = request.query_params.get("query", None)⁠으로 query 파라미터로 검색어를 가져온다.

2.검색어와 검색 유형을 파라미터로 설정

  • headers = {"Authorization": f"Bearer {access_token}"}

  • params = {"q": search, "type": "playlist"}

3.spotify api 호출 → requests.get으로 검색 엔드포인트에 요청을 보내고, params에 검색어를 전달하여 요청

spotify_api = requests.get(     
    "https://api.spotify.com/v1/search?limit=40", headers=headers, params=params )

검색어가 제니일 경우

query=제니 로 요청을 보내면 search 변수는 제니가 된다.

params는 {"q": "제니", "type": "playlist"}가 되어 Spotify API에 전달

requests.get으로 api 요청을 보내고, 응답을 json 형식으로 받아옴