Python과 OAuth 2.0으로 Spotify 음악 타임머신 만들기

발행: (2026년 2월 13일 오전 03:22 GMT+9)
17 분 소요
원문: Dev.to

Source: Dev.to

만약 당신이 대부분의 사람들과 같다면, 전혀 모를 것입니다. 그 노래들은 사라졌습니다 — 스포티파이 서버에서 사라진 것이 아니라, 당신의 기억에서 사라진 것이죠. 2022년에 집착하던 플레이리스트는 잊혀지고, 현재 즐겨 듣는 곡들이 큐를 장악합니다.

스포티파이는 단기 요약과 추천을 제공하지만, 인생의 이전 장을 정의했던 음악을 다시 찾는 것은 의외로 어렵습니다. 시대는 기억할 수 있어도, 정확히 어떤 트랙이 반복 재생됐는지는 기억하기 힘듭니다.

이 튜토리얼에서는 그 문제를 해결하는 도구를 만들게 됩니다. OAuth 2.0을 사용해 스포티파이에 인증하고, 개인 청취 데이터를 가져오며, 파이썬으로 프로그램matically 플레이리스트를 생성합니다.

튜토리얼을 마치면 다음을 갖게 됩니다:

  • 스포티파이와의 작동하는 OAuth 2.0 통합
  • 실제 스포티파이 계정에 플레이리스트를 생성하는 스크립트 (약 30 줄의 코드)
  • 스포티파이의 시간 범위와 오디오 특징에 대한 이해
  • “잊혀진 보석” 기능의 기반, 즉 듣던 곡을 다시 발견하게 해주는 기능

전제 조건: 기본 파이썬 지식(변수, 함수, 리스트). 스포티파이 계정(무료 또는 프리미엄). 이것만 있으면 됩니다.

Source:

Step 1: Get Spotify Developer Credentials

Spotify API를 사용하는 모든 애플리케이션은 Client IDClient Secret이라는 자격 증명이 필요합니다. 이들은 애플리케이션을 Spotify에 식별하고 OAuth 인증을 가능하게 합니다.

  1. Spotify Developer Dashboard로 이동
    방문하고 Spotify 계정으로 로그인합니다.

  2. 앱 생성
    **“Create app”**을 클릭하고 양식을 작성합니다:

    • App name: Music Time Machine (또는 원하는 이름)
    • App description: Personal music analytics and playlist generator
    • Redirect URI: http://localhost:8888/callback (정확히 이 주소 — 인증 후 OAuth가 리디렉션되는 곳)
    • APIs used: “Web API” 체크
  3. 자격 증명 복사
    앱을 만든 후 **“Settings.”**를 클릭합니다. Client ID가 표시됩니다. **“View client secret”**를 클릭해 Client Secret를 확인합니다. 두 값을 모두 복사해 둡니다—곧 필요합니다.

localhost:8888/callback인가?
애플리케이션을 승인하면 Spotify가 브라우저를 이 URI로 리디렉션하면서 인증 코드를 전달합니다. 우리가 사용할 spotipy 라이브러리는 포트 8888에서 임시 로컬 서버를 열어 이 리디렉션을 잡고 코드를 추출합니다. 이는 표준 OAuth 흐름이며, 리디렉션 URI가 공개 서버일 필요는 없습니다.

단계 2: 종속성 설치

두 개의 파이썬 패키지가 필요합니다:

pip install spotipy python-dotenv
  • spotipy – OAuth 토큰 교환, 토큰 갱신 및 요청 포맷을 처리하는 Spotify API 래퍼입니다.
  • python-dotenv.env 파일에서 환경 변수를 로드합니다.

Step 3: 자격 증명을 안전하게 저장하기

  1. .env 파일을 프로젝트 디렉터리에 생성합니다. 이 파일은 비밀 정보를 저장하며 절대로 버전 관리에 커밋해서는 안 됩니다.

    SPOTIPY_CLIENT_ID=your_client_id_here
    SPOTIPY_CLIENT_SECRET=your_client_secret_here
    SPOTIPY_REDIRECT_URI=http://localhost:8888/callback

    자리표시자 값을 Step 1에서 복사한 자격 증명으로 교체합니다.

  2. .env.gitignore에 추가합니다 (버전 관리에 포함하고 싶지 않은 다른 파일도 함께):

    .env
    __pycache__/
    *.pyc
    .cache
    *.db

    .cache 파일은 OAuth 흐름을 실행할 때 생성되며, 로컬에 액세스 토큰을 저장합니다. 이 파일도 버전 관리에서 제외하십시오.

Step 4: 첫 번째 플레이리스트 만들기

이 스크립트는 Spotify에 인증하고, 당신의 상위 트랙을 가져와서 계정에 실제 플레이리스트를 생성합니다.

"""
Quick Start: Create your first Spotify playlist
Demonstrates OAuth 2.0 authentication and basic API usage
"""
import os
from dotenv import load_dotenv
import spotipy
from spotipy.oauth2 import SpotifyOAuth

# Load credentials from .env file
load_dotenv()

# Define the permissions we need
scope = "user-top-read playlist-modify-public playlist-modify-private"

# Create Spotify client with OAuth
sp = spotipy.Spotify(auth_manager=SpotifyOAuth(scope=scope))

# Get current user info
user = sp.current_user()
print(f"Authenticated as: {user['display_name']}")

# Fetch top tracks from last 4 weeks
print("\nFetching your top tracks...")
top_tracks = sp.current_user_top_tracks(limit=20, time_range='short_term')

# Extract track URIs (Spotify's unique identifiers) and names
track_uris = [track['uri'] for track in top_tracks['items']]
track_names = [track['name'] for track in top_tracks['items']]

# Create a new playlist
print("\nCreating playlist...")
playlist = sp.user_playlist_create(
    user=user['id'],
    name="My Top Tracks - Quick Start",
    public=False,
    description="Created by Music Time Machine - My current favorites"
)

# Add tracks to the playlist
sp.playlist_add_items(playlist['id'], track_uris)

print(f"\nSuccess! Created playlist with {len(track_uris)} tracks")
print(f"Playlist URL: {playlist['external_urls']['spotify']}")
print("\nYour top tracks right now:")
for i, name in enumerate(track_names, 1):
    print(f"  {i}. {name}")
  1. 스크립트를 quick_start.py 파일로 저장합니다.

  2. 실행합니다:

    python quick_start.py

다음에 일어나는 일: 브라우저가 Spotify 인증 페이지를 엽니다. “동의” 버튼을 클릭하면 브라우저가 localhost:8888/callback으로 리디렉션됩니다. spotipy가 인증 코드를 받아 액세스 토큰으로 교환하고 스크립트가 계속 진행됩니다.

Spotify 앱을 확인해 보세요 — 플레이리스트가 생성되어 있습니다. 이제 실제 API와 통합한 것입니다! 🎉

이 기반을 자유롭게 확장해 보세요: 장기(medium_term, long_term) 상위 트랙을 가져오거나, 오디오 특징을 분석하거나, 과거에 좋아했지만 최근에 재생하지 않은 트랙을 찾아주는 “Forgotten Gems” 플레이리스트를 만들어 보세요.

OAuth API, 개인화된 데이터를 가져오고, 계정에 콘텐츠를 생성 – 약 30줄의 파이썬으로

방금 일어난 일: OAuth 2.0을 60 초에 이해하기

단계Spotipy가 수행한 작업
Authorization request브라우저를 열어 https://accounts.spotify.com/authorize 로 이동하고, 클라이언트 ID, 요청된 스코프, 리디렉션 URI를 전달했습니다.
User consent앱이 필요로 하는 권한을 표시했으며, 사용자는 Agree(동의)를 클릭했습니다.
Authorization codeSpotify가 http://localhost:8888/callback 로 리디렉션하면서 URL에 임시 코드를 포함했습니다.
Token exchangeSpotipy가 코드를 (클라이언트 ID 및 Secret과 함께) Spotify 토큰 엔드포인트에 전송했고, access tokenrefresh token을 받았습니다.
API calls이후의 모든 호출(current_user_top_tracks, user_playlist_create, …)은 Authorization: Bearer … 헤더에 액세스 토큰을 포함했습니다.
Token cached토큰은 .cache 파일에 저장되었습니다. 이후 실행 시 Spotipy는 캐시를 읽어 추가 인증 단계를 건너뛰며, 액세스 토큰이 만료될 때(≈ 1 시간) Spotipy는 자동으로 refresh token을 사용해 새로운 토큰을 얻습니다.

이는 “Sign in with Google”, GitHub 연동 등 여러분의 계정에 연결되는 거의 모든 서드파티 앱이 사용하는 동일한 OAuth 흐름입니다. 패턴은 어디서든 동일하고, 차이점은 엔드포인트뿐입니다.

스코프 이해하기: 권한이 의미하는 바

scope = "user-top-read playlist-modify-public playlist-modify-private"
스코프권한
user-top-read상위 트랙 및 아티스트를 읽습니다. 이 권한이 없으면 sp.current_user_top_tracks()403 Forbidden을 반환합니다.
playlist-modify-public공개 플레이리스트를 생성하고 수정합니다.
playlist-modify-private비공개 플레이리스트를 생성하고 수정합니다 (스크립트는 public=False로 비공개 플레이리스트를 생성합니다).

최소 권한 원칙 – 실제로 필요한 권한만 요청하세요. Spotify 사용자는 동의 화면에서 정확히 어떤 권한을 요청하는지 확인할 수 있습니다. 사용하지 않는 스코프(예: user-library-modify)를 요청하면 의심을 살 수 있습니다.

더 나아가기: 시간 범위와 잊힌 보석들

Spotify의 current_user_top_tracks() 메서드는 time_range 매개변수를 받습니다:

시간 범위대략적인 기간포함 내용
short_term~4 주현재 집착
medium_term~6 개월지속적인 즐겨찾기
long_term수년전체 기간 패턴

이러한 범위를 비교하면 오래전에 잊어버린 곡들을 발견할 수 있습니다:

# Fetch top tracks for each time range
short_term = sp.current_user_top_tracks(limit=50, time_range='short_term')
long_term  = sp.current_user_top_tracks(limit=50, time_range='long_term')

# Extract track IDs into sets
short_ids = {track['id'] for track in short_term['items']}
long_ids  = {track['id'] for track in long_term['items']}

# Forgotten gems: songs in your long‑term favorites that you haven't listened to recently
forgotten = long_ids - short_ids

print(f"You loved {len(forgotten)} tracks long‑term but haven't heard them recently")

“잊힌 보석들” 플레이리스트 만들기

# Get full track details for forgotten gems
forgotten_tracks = [
    track for track in long_term['items']
    if track['id'] in forgotten
]

# Create the playlist
user = sp.current_user()
playlist = sp.user_playlist_create(
    user=user['id'],
    name="Forgotten Gems",
    public=False,
    description="Songs I loved but forgot about – rediscovered by Music Time Machine"
)

track_uris = [track['uri'] for track in forgotten_tracks]
sp.playlist_add_items(playlist['id'], track_uris)

print(f"\nCreated 'Forgotten Gems' with {len(forgotten_tracks)} tracks")
for track in forgotten_tracks:
    print(f"  - {track['name']}{track['artists'][0]['name']}")

더 나아가기: 오디오 특성 및 무드 플레이리스트

Spotify는 모든 트랙을 분석하고 오디오 특성에 대한 수치 점수를 부여합니다. 이 점수들은 API를 통해 가져올 수 있습니다:

# Fetch audio features for your top tracks
tracks = sp.current_user_top_tracks(limit=50)['items']
track_ids = [track['id'] for track in tracks]
features_list = sp.audio_features(track_ids)

# Show a few examples
for track, features in zip(tracks[:3], features_list[:3]):
    if features:
        print(f"\n{track['name']}{track['artists'][0]['name']}")
        print(f"  Energy:       {features['energy']:.2f}")
        print(f"  Valence:      {features['valence']:.2f} (happiness)")
        print(f"  Danceability: {features['danceability']:.2f}")
        print(f"  Tempo:        {features['tempo']:.0f} BPM")

주요 오디오‑특성 점수 (0.0 – 1.0, 템포 제외)

특성의미
Energy강도와 활동성 (예: AC/DC ≈ 0.9, 앰비언트 ≈ 0.1).
Valence음악적 행복감 (예: “Happy” – 0.96, “Hurt” – 0.14).
Danceability비트 강도와 규칙성 (펑크/디스코는 높고, 프리 재즈는 낮음).
Tempo분당 비트 수 (운동·러닝 플레이리스트에 유용).
Acousticness어쿠스틱 vs. 전자음 가능성.
Instrumentalness보컬 존재 여부 예측 (값이 높을수록 기악).

예시: 운동용 플레이리스트 만들기

def matches_workout_profile(features):
    """Return True if a track looks good for a high‑intensity workout."""
    return (
        features['energy']      > 0.7 and
        features['danceability'] > 0.7 and
        features['tempo']       > 120
    )

# Filter top tracks for workout suitability
workout_tracks = [
    track['uri'] for track, feat in zip(tracks, features_list)
    if feat and matches_workout_profile(feat)
]

# Create the playlist
playlist = sp.user_playlist_create(
    user=user['id'],
    name="Workout Boost",
    public=False,
    description="High‑energy tracks for the gym – generated from my top‑50"
)

sp.playlist_add_items(playlist['id'], workout_tracks)

print(f"Created 'Workout Boost' with {len(workout_tracks)} tracks")

이 스니펫들은 몇 번의 API 호출과 간단한 Python 로직을 결합하면, 원시 청취 데이터를 개인화된 실용적인 플레이리스트로 변환할 수 있음을 보여줍니다. 실험을 즐겨보세요!

def matches_workout_profile(features):
    """Check if a track suits a workout playlist"""
    return (
        features['energy'] > 0.75 and
        features['valence'] > 0.50 and
        features['tempo'] > 140 and
        features['danceability'] > 0.6
    )

# Filter your top tracks for workout‑suitable songs
workout_tracks = [
    tracks[i] for i in range(len(tracks))
    if features_list[i] and matches_workout_profile(features_list[i])
]

print(f"Found {len(workout_tracks)} workout tracks from your top 50")

앞으로 나아갈 길

이 튜토리얼에서는 작동하는 Spotify OAuth 통합을 구축하고, 프로그래밍 방식으로 재생목록을 만들었으며, 시간 범위와 오디오 특징을 탐색했습니다. 이는 탄탄한 기반이지만—그 위에 구축할 수 있는 것이 훨씬 더 많습니다.

탐구할 아이디어

  • 월간 스냅샷 – 매달 상위 50곡을 데이터베이스에 저장합니다. 몇 달이 지나면 “지난 3월에 나는 무엇을 듣고 있었을까?”와 같이 조회할 수 있는 음악 일기가 생깁니다.
  • 데이터베이스 기반 Forgotten Gems – 위의 집합 차감 방식은 실시간 API 데이터와 함께 작동합니다. 월간 스냅샷을 누적하는 SQLite 데이터베이스를 사용하면 훨씬 풍부한 히스토리를 탐색할 수 있습니다.
  • 진화 분석 – 시간이 지남에 따라 음악 취향이 어떻게 변하는지 추적합니다. 이탈률을 계산하고, 장르 변화를 포착하며, 여름 재생목록이 더 경쾌한지 확인합니다.
  • 점수 기반 무드 재생목록 생성기 – 엄격한 통과/실패 필터링 대신, 각 트랙을 무드 프로파일에 점수화하고 상위 N개의 매치를 선택합니다.

이 모든 아이디어는 동일한 패턴을 기반으로 합니다: Spotify API에서 데이터를 가져오고, 과거 데이터와 저장하거나 비교하며, 유용한 무언가를 생성합니다.

이 튜토리얼은 Mastering APIs With Python 의 16장을 기반으로 합니다. 더 깊이 파고들고 싶다면—SQLite 데이터베이스 설계, 재시도 로직을 활용한 오류 처리, 모킹을 이용한 자동 테스트, 그리고 첫 API 호출부터 AWS 배포까지 모든 것을 다루는 29개의 추가 챕터—확인해 보세요.

0 조회
Back to Blog

관련 글

더 보기 »

왜 0.1 + 0.2가 코드에서 0.3이 되지 않을까

markdown Floating‑Point Surprise python print 0.1 + 0.2 당신은 0.3이 나오길 기대합니다. 하지만 실제로는 0.30000000000000004가 나옵니다. 당신의 계산기는 0.3이라고 말하고, Excel도 0.3이라고 말합니다. Yo...