🏢 Spotify 기술 스택 분석

25 min read
spotifyjavapythonkubernetesgcpbigtablepubsubbeamtensorflowmicroservicesbackstagegolden-pathmlstreaming

스포티파이가 전 세계 5억 명에게 8,300만 곡을 스트리밍하는 기술 스택 심층 분석 - 마이크로서비스, 머신러닝, 그리고 음악 추천의 비밀

🏢 Spotify 기술 스택 분석

매일 10억 개 이상의 스트림을 처리하며 개인화된 음악 경험을 제공하는 Spotify의 기술 스택을 심층 분석합니다.
"Music for everyone" - 기술로 만드는 글로벌 음악 플랫폼


📊 회사 개요

서비스 규모

  • 월간 활성 사용자: 5.74억 명 (프리미엄 2.26억)
  • 콘텐츠: 1억+ 트랙, 500만+ 팟캐스트
  • 일일 스트림: 10억+ 재생
  • 서비스 지역: 184개 시장
  • 플레이리스트: 40억+ 사용자 생성

엔지니어링 조직

  • 엔지니어: 2,000명+ (전체 직원 9,000+)
  • Squad 모델: 자율적인 소규모 팀
  • Chapter & Guild: 기술 공유 조직
  • 오픈소스: 400+ 프로젝트

기술적 도전과제

  1. 개인화: 5억 명을 위한 맞춤 추천
  2. 저지연 스트리밍: 전 세계 실시간 음악 전송
  3. 콘텐츠 관리: 1억+ 트랙 메타데이터
  4. 크리에이터 도구: 아티스트 분석 플랫폼

🧩 기술 스택 상세

Backend 기술

마이크로서비스 아키텍처

// Spotify의 Apollo 프레임워크 기반 서비스
@Service
public class PlaylistService {
    private final UserService userService;
    private final TrackService trackService;
    private final RecommendationService recommendationService;

    @RequestMapping("/playlists/{id}")
    public CompletableFuture<Playlist> getPlaylist(
            @PathVariable String id,
            @RequestContext Context context) {

        return userService.getUser(context.userId())
            .thenCompose(user -> {
                // 병렬로 데이터 가져오기
                CompletableFuture<PlaylistMetadata> metadata =
                    getPlaylistMetadata(id);
                CompletableFuture<List<Track>> tracks =
                    trackService.getTracks(id);
                CompletableFuture<List<Recommendation>> recommendations =
                    recommendationService.getForPlaylist(id, user);

                return CompletableFuture.allOf(metadata, tracks, recommendations)
                    .thenApply(v -> buildPlaylist(
                        metadata.join(),
                        tracks.join(),
                        recommendations.join()
                    ));
            });
    }

    // Circuit breaker 패턴
    @HystrixCommand(
        fallbackMethod = "getDefaultRecommendations",
        commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
        }
    )
    private CompletableFuture<List<Track>> getPersonalizedTracks(String userId) {
        return mlService.getRecommendations(userId);
    }
}

Backend 기술 스택:

  • 언어: Java (60%), Python (25%), Go (10%), C++ (5%)
  • 프레임워크: Spring Boot, Apollo (자체), Flask
  • API: GraphQL Federation, REST
  • 메시징: Google Pub/Sub, Kafka (일부)
  • 서비스 메시: Kubernetes + Istio

데이터 파이프라인

# Apache Beam 기반 데이터 처리
import apache_beam as beam
from apache_beam.options.pipeline_options import PipelineOptions

class SpotifyStreamingPipeline:
    def __init__(self):
        self.pipeline_options = PipelineOptions([
            '--runner=DataflowRunner',
            '--project=spotify-data',
            '--region=europe-west1',
            '--temp_location=gs://spotify-temp/dataflow',
            '--streaming'
        ])

    def build_pipeline(self):
        with beam.Pipeline(options=self.pipeline_options) as p:
            # 실시간 스트림 이벤트 수집
            events = (p
                | 'Read from Pub/Sub' >> beam.io.ReadFromPubSub(
                    topic='projects/spotify-data/topics/play-events'
                )
                | 'Parse JSON' >> beam.Map(json.loads)
            )

            # 사용자별 집계
            user_stats = (events
                | 'Extract User Data' >> beam.Map(
                    lambda x: (x['user_id'], {
                        'track_id': x['track_id'],
                        'duration': x['duration'],
                        'timestamp': x['timestamp']
                    })
                )
                | 'Window' >> beam.WindowInto(
                    beam.window.FixedWindows(60)  # 1분 윈도우
                )
                | 'Group by User' >> beam.GroupByKey()
                | 'Calculate Stats' >> beam.Map(self.calculate_user_stats)
            )

            # BigTable에 저장
            user_stats | 'Write to Bigtable' >> beam.io.WriteToBigTable(
                project_id='spotify-data',
                instance_id='user-analytics',
                table_id='streaming_stats'
            )

            # 추천 시스템 업데이트
            user_stats | 'Update ML Features' >> beam.Map(
                self.update_recommendation_features
            )

Frontend 기술

Web Player (React)

// Spotify Web Player 컴포넌트
import { useEffect, useState } from "react";
import { useSpotifySDK } from "@spotify/web-api-sdk";

interface PlayerProps {
  trackUri: string;
  onTrackEnd: () => void;
}

export function SpotifyPlayer({ trackUri, onTrackEnd }: PlayerProps) {
  const [player, setPlayer] = useState<Spotify.Player | null>(null);
  const [deviceId, setDeviceId] = useState<string>("");
  const sdk = useSpotifySDK();

  useEffect(() => {
    // Web Playback SDK 초기화
    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 Spotify.Player({
        name: "Spotify Web Player",
        getOAuthToken: (cb) => {
          sdk.getAccessToken().then(cb);
        },
        volume: 0.5,
      });

      // 이벤트 리스너
      player.addListener("ready", ({ device_id }) => {
        console.log("Ready with Device ID", device_id);
        setDeviceId(device_id);
      });

      player.addListener("player_state_changed", (state) => {
        if (state.position === 0 && state.paused) {
          onTrackEnd();
        }

        // 재생 통계 전송
        sendPlaybackAnalytics({
          track_id: state.track_window.current_track.id,
          position: state.position,
          duration: state.duration,
          context: state.context,
        });
      });

      player.connect();
      setPlayer(player);
    };

    return () => {
      player?.disconnect();
    };
  }, []);

  const play = async () => {
    await sdk.player.play({
      device_id: deviceId,
      uris: [trackUri],
    });
  };

  return (
    <div className="player-container">
      <AudioVisualizer />
      <PlayerControls
        onPlay={play}
        onPause={() => player?.pause()}
        onNext={() => player?.nextTrack()}
      />
      <VolumeControl onChange={(volume) => player?.setVolume(volume)} />
    </div>
  );
}

Frontend 기술:

  • Web: React, TypeScript, Redux
  • Mobile: Native (Swift/Kotlin) + React Native (일부)
  • Desktop: Electron + CEF (Chromium Embedded)
  • TV/콘솔: C++ 커스텀 플레이어
  • 빌드: Webpack, Bazel

Data & ML Infrastructure

추천 시스템

# Spotify의 추천 엔진 (간소화)
class CollaborativeFilteringModel:
    def __init__(self):
        self.user_embeddings = None
        self.item_embeddings = None
        self.model = self._build_model()

    def _build_model(self):
        # Two-tower 모델
        user_input = keras.Input(shape=(USER_FEATURES,))
        item_input = keras.Input(shape=(ITEM_FEATURES,))

        # User tower
        user_tower = keras.Sequential([
            keras.layers.Dense(256, activation='relu'),
            keras.layers.Dropout(0.2),
            keras.layers.Dense(128, activation='relu'),
            keras.layers.Dense(64)
        ])

        # Item tower
        item_tower = keras.Sequential([
            keras.layers.Dense(256, activation='relu'),
            keras.layers.Dropout(0.2),
            keras.layers.Dense(128, activation='relu'),
            keras.layers.Dense(64)
        ])

        user_embedding = user_tower(user_input)
        item_embedding = item_tower(item_input)

        # Dot product for similarity
        similarity = keras.layers.Dot(axes=1)([user_embedding, item_embedding])

        model = keras.Model(
            inputs=[user_input, item_input],
            outputs=similarity
        )

        return model

    def get_recommendations(self, user_id, context):
        # 사용자 특성 추출
        user_features = self.extract_user_features(user_id)

        # 컨텍스트 고려 (시간, 장소, 기기)
        context_features = self.extract_context_features(context)

        # 후보 생성
        candidates = self.retrieve_candidates(user_features)

        # 순위 매기기
        scores = self.model.predict([
            np.repeat(user_features, len(candidates)),
            candidates
        ])

        # 다양성 추가
        diversified = self.apply_diversity(candidates, scores)

        return diversified[:100]  # Top 100

Feature Store

// Spotify의 Feature Store (Feathr 기반)
object FeatureStore {
  case class UserFeatures(
    userId: String,
    totalListeningTime: Long,
    topGenres: Seq[String],
    topArtists: Seq[String],
    listeningPatterns: Map[Int, Float], // hour -> probability
    devicePreferences: Map[String, Float],
    lastUpdated: Timestamp
  )

  case class TrackFeatures(
    trackId: String,
    audioFeatures: AudioFeatures,
    popularity: Float,
    releaseDate: Date,
    genres: Seq[String],
    collaborativeSignal: Float,
    contentSignal: Float
  )

  case class AudioFeatures(
    danceability: Float,
    energy: Float,
    key: Int,
    loudness: Float,
    mode: Int,
    speechiness: Float,
    acousticness: Float,
    instrumentalness: Float,
    liveness: Float,
    valence: Float,
    tempo: Float,
    duration: Int
  )

  def getUserFeatures(userId: String): Future[UserFeatures] = {
    // BigTable에서 특성 조회
    bigtableClient.get(s"user_features:$userId").map { row =>
      UserFeatures(
        userId = userId,
        totalListeningTime = row.getLong("total_time"),
        topGenres = row.getList("genres"),
        topArtists = row.getList("artists"),
        listeningPatterns = row.getMap("patterns"),
        devicePreferences = row.getMap("devices"),
        lastUpdated = row.getTimestamp("updated")
      )
    }
  }
}

데이터 인프라:

  • 데이터 레이크: Google Cloud Storage
  • 실시간 처리: Google Pub/Sub + Dataflow
  • 배치 처리: Apache Beam, Scio (Scala)
  • 데이터베이스: Bigtable, Spanner, PostgreSQL
  • 분석: BigQuery, Databricks
  • ML 플랫폼: Kubeflow, TensorFlow Extended

Infrastructure & DevOps

Backstage 플랫폼

# Backstage 서비스 카탈로그
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
  name: playlist-service
  description: Manages user playlists and collaborative playlists
  tags:
    - java
    - backend
    - tier1
  annotations:
    github.com/project-slug: spotify/playlist-service
    backstage.io/techdocs-ref: dir:.
    spotify.com/squad-owner: playlist-experience
spec:
  type: service
  lifecycle: production
  owner: playlist-squad
  system: playlist-platform
  dependsOn:
    - resource:playlist-db
    - component:track-service
    - component:user-service
  providesApis:
    - playlist-api

Golden Path

# Spotify의 Golden Path CLI
$ spotify create service playlist-service --lang java --type rest

✓ Created service scaffold
✓ Set up GitHub repository
✓ Configured CI/CD pipelines
✓ Created Kubernetes manifests
✓ Registered in Backstage
✓ Set up monitoring dashboards
✓ Created runbooks

Your service is ready!
Local development: cd playlist-service && ./gradlew bootRun
Deploy to staging: git push origin main

인프라 기술:

  • 클라우드: Google Cloud Platform (주력)
  • 컨테이너: Kubernetes (자체 관리)
  • CI/CD: Jenkins + Spinnaker
  • 모니터링: Prometheus + Grafana
  • 로깅: ELK Stack
  • 개발자 포털: Backstage

💡 핵심 기술 인사이트

1. Squad 모델 & 자율성

// Squad의 자율적 서비스 운영
interface SquadOwnership {
  squad: string;
  mission: string;
  services: string[];
  kpis: {
    metric: string;
    target: number;
    current: number;
  }[];
  dependencies: {
    upstream: string[];
    downstream: string[];
  };
}

// 예시: Playlist Squad
const playlistSquad: SquadOwnership = {
  squad: "playlist-experience",
  mission: "Enable users to organize and discover music through playlists",
  services: [
    "playlist-service",
    "collaborative-playlist-service",
    "playlist-recommendation-service",
  ],
  kpis: [
    { metric: "playlist_creation_rate", target: 1000000, current: 1200000 },
    {
      metric: "collaborative_playlist_mau",
      target: 50000000,
      current: 62000000,
    },
    { metric: "p99_latency_ms", target: 200, current: 187 },
  ],
  dependencies: {
    upstream: ["user-service", "track-service", "ml-platform"],
    downstream: ["mobile-app", "web-player", "home-feed"],
  },
};

2. 음악 추천 알고리즘

# Spotify의 하이브리드 추천 시스템
class HybridRecommendationEngine:
    def __init__(self):
        self.collaborative_filter = CollaborativeFiltering()
        self.content_based = ContentBasedFiltering()
        self.nlp_engine = NLPEngine()
        self.contextual_bandit = ContextualBandit()

    def generate_discover_weekly(self, user_id):
        # 1. 협업 필터링 (유사 사용자 기반)
        collaborative_tracks = self.collaborative_filter.get_recommendations(
            user_id,
            method='matrix_factorization',
            n_tracks=1000
        )

        # 2. 콘텐츠 기반 필터링 (음악적 특성)
        user_taste_profile = self.get_user_taste_profile(user_id)
        content_tracks = self.content_based.find_similar_tracks(
            user_taste_profile,
            audio_features=['energy', 'valence', 'danceability'],
            n_tracks=1000
        )

        # 3. NLP 기반 추천 (가사, 리뷰 분석)
        nlp_tracks = self.nlp_engine.get_semantic_recommendations(
            user_id,
            sources=['lyrics', 'reviews', 'social_media'],
            n_tracks=500
        )

        # 4. 앙상블 & 다양성 최적화
        candidate_pool = self.merge_recommendations([
            (collaborative_tracks, 0.4),
            (content_tracks, 0.3),
            (nlp_tracks, 0.3)
        ])

        # 5. Contextual Bandit으로 최종 선택
        final_playlist = self.contextual_bandit.select_tracks(
            candidate_pool,
            context={
                'day_of_week': datetime.now().weekday(),
                'time_of_day': datetime.now().hour,
                'user_activity': self.get_recent_activity(user_id),
                'global_trends': self.get_trending_tracks()
            },
            n_tracks=30
        )

        return self.ensure_diversity(final_playlist)

3. 실시간 오디오 분석

// C++ 기반 오디오 특성 추출
class AudioAnalyzer {
private:
    std::unique_ptr<FFTProcessor> fft;
    std::unique_ptr<ChromaExtractor> chroma;
    std::unique_ptr<RhythmAnalyzer> rhythm;

public:
    AudioFeatures analyzeTrack(const AudioBuffer& buffer) {
        AudioFeatures features;

        // 스펙트럼 분석
        auto spectrum = fft->process(buffer);
        features.spectralCentroid = calculateSpectralCentroid(spectrum);
        features.spectralRolloff = calculateSpectralRolloff(spectrum, 0.85);

        // 크로마 벡터 (화성 분석)
        auto chromaVector = chroma->extract(buffer);
        features.key = estimateKey(chromaVector);
        features.mode = estimateMode(chromaVector);

        // 리듬 특성
        auto tempogram = rhythm->extractTempogram(buffer);
        features.tempo = estimateTempo(tempogram);
        features.timeSignature = estimateTimeSignature(tempogram);

        // 고급 특성
        features.danceability = calculateDanceability(features.tempo,
                                                     features.energy,
                                                     features.rhythm_strength);
        features.energy = calculateEnergy(spectrum);
        features.valence = estimateValence(chromaVector, features.tempo);

        // 음성/악기 분리
        auto separated = separateSources(buffer);
        features.instrumentalness = calculateInstrumentalness(separated);
        features.speechiness = calculateSpeechiness(separated);

        return features;
    }
};

4. Backstage 개발자 경험

# 개발자 생산성 도구
apiVersion: scaffolder.backstage.io/v1beta3
kind: Template
metadata:
  name: spotify-microservice
  title: Spotify Microservice
  description: Create a new microservice following Spotify standards
spec:
  owner: platform-team
  type: service

  parameters:
    - title: Service Details
      required:
        - name
        - description
        - squad
      properties:
        name:
          title: Name
          type: string
          pattern: "^[a-z0-9-]+$"
        description:
          title: Description
          type: string
        squad:
          title: Squad Owner
          type: string
          enum: ${squads}

    - title: Technical Choices
      properties:
        language:
          title: Programming Language
          type: string
          enum: ["java", "python", "go", "typescript"]
        database:
          title: Database
          type: string
          enum: ["bigtable", "spanner", "postgresql", "none"]
        messaging:
          title: Messaging
          type: string
          enum: ["pubsub", "kafka", "none"]

  steps:
    - id: fetch
      name: Fetch Skeleton
      action: fetch:template
      input:
        url: ./skeleton/${{ parameters.language }}

    - id: publish
      name: Publish to GitHub
      action: publish:github
      input:
        repoUrl: github.com?owner=spotify&repo=${{ parameters.name }}

    - id: register
      name: Register in Backstage
      action: catalog:register

    - id: setup-ci
      name: Setup CI/CD
      action: jenkins:create-job

📈 성과 & 지표

기술적 성과

  • API 응답시간: P50 < 50ms, P99 < 200ms
  • 가용성: 99.95% (연간 다운타임 < 4.5시간)
  • 배포 빈도: 일일 2,000+ 배포
  • 추천 정확도: 30% 향상 (2020년 대비)
  • 스트리밍 품질: 320kbps (Premium)

비즈니스 영향

  • 사용자 참여: 평균 일일 사용 2.5시간
  • Discover Weekly: 40% 사용자가 매주 청취
  • 개인화 효과: 추천 트랙 80%+ 긍정 반응
  • 개발자 생산성: Golden Path로 50% 시간 단축

🎓 Spotify에서 배울 점

✅ 적용 가능한 패턴

  1. Squad 모델: 자율적 팀 운영
  2. Golden Path: 표준화된 개발 경로
  3. Backstage: 개발자 포털 플랫폼
  4. Feature Store: ML 특성 관리
  5. 하이브리드 추천: 다양한 알고리즘 앙상블

❌ 주의사항

  1. 복잡한 조직 구조: Squad 모델은 문화가 중요
  2. 높은 자율성: 표준화와 균형 필요
  3. 기술 다양성: 너무 많은 언어/프레임워크
  4. 데이터 규모: 대규모 인프라 필요

📚 추천 리소스


🔮 미래 전망

현재 집중 분야

  1. AI DJ: 개인화된 라디오 경험
  2. 팟캐스트 기술: 음성 콘텐츠 혁신
  3. 라이브 오디오: 실시간 방송 플랫폼
  4. 자동차 경험: 차량용 최적화
  5. 창작자 도구: 아티스트 분석 플랫폼

기술 투자 영역

  • 생성형 AI: 음악 생성, 리믹스
  • 공간 오디오: 3D 사운드 경험
  • 블록체인: 로열티 분배 투명성
  • 엣지 컴퓨팅: 오프라인 최적화
  • 그린 컴퓨팅: 탄소 중립 스트리밍

"우리는 세계 최고의 오디오 플랫폼이 되기 위해 기술의 한계를 계속 넓혀가고 있습니다."

  • Spotify Engineering

마지막 업데이트: 2025-01-28

┌─────────────┐     ┌─────────────┐     ┌─────────────┐
│   Clients   │────►│   Backend   │────►│  ML Platform│
│(Mobile/Web) │     │  for FE(BFF)│     │ (Kubeflow)  │
└─────────────┘     └─────────────┘     └─────────────┘
       │                    │                    │
       │              ┌─────▼─────┐       ┌─────▼─────┐
       │              │  API Gateway│       │  Feature   │
       │              │   (Nginx)  │       │   Store    │
       │              └───────────┘       └───────────┘
       │                    │                    │
       └────────────────────┤                    │
                      ┌─────▼─────┐       ┌─────▼─────┐
                      │Microservices│      │ Data Lake │
                      │(100s of them)│      │   (GCS)   │
                      └───────────┘       └───────────┘

핵심 설계 원칙

  1. Autonomous Teams: Squad 모델로 독립적 개발
  2. Golden Path: 표준화된 개발 경로
  3. Data-Driven: 모든 결정은 데이터 기반
  4. Fail Fast: 빠른 실험과 학습
  5. Platform Thinking: 내부 플랫폼 우선

기술 진화

  • 2006-2008: Python 모놀리스
  • 2009-2012: 마이크로서비스 전환
  • 2013-2016: 자체 인프라 → 클라우드
  • 2017-2020: ML 플랫폼 구축
  • 2021-현재: Backstage, Golden Path

Found this helpful? Share it with others!
Tweet

🔗 Related Content

You might also be interested in these articles

🏢 company

🏢 Canva 기술 스택 분석

캔바가 1억 명 이상의 사용자에게 쉽고 강력한 디자인 도구를 제공하는 기술 스택 심층 분석 - Java, TypeScript, AWS로 구축한 대규모 디자인 플랫폼

32 min read
canva, java +10
Read more
🏢 company

🏢 Netflix 기술 스택 분석

넷플릭스가 전 세계 2.4억 사용자에게 스트리밍 서비스를 제공하는 기술 스택 심층 분석 - 마이크로서비스, AWS, Chaos Engineering의 교과서

9 min read
netflix, microservices +11
Read more
🏢 company

🏢 Airbnb 기술 스택 분석

에어비앤비가 전 세계 400만 개의 숙소를 연결하는 글로벌 마켓플레이스 기술 스택 심층 분석 - Ruby on Rails에서 Service-Oriented Architecture로의 진화

24 min read
airbnb, ruby +12
Read more
🏢 company

🏢 배달의민족 기술 스택 분석

배달의민족이 한국 배달 문화를 혁신하며 동남아로 확장한 기술 스택 심층 분석 - Java, Spring, Kotlin으로 구축한 O2O 플랫폼

51 min read
baemin, woowa +10
Read more
🏢 company

🏢 카카오 기술 스택 분석

카카오가 5천만 한국인의 일상을 연결하는 기술 스택 심층 분석 - Java, Kotlin, MySQL로 구축한 국민 메신저부터 슈퍼앱까지

32 min read
kakao, java +9
Read more

Found this helpful?

Help us improve this content by contributing on GitHub or sharing your feedback with the community.