Skip to main content

๐ŸŽง Music Monday์™€ Spotify๋ฅผ ์—ฐ๊ฒฐํ•˜๋Š” ์˜คํ”ˆ์†Œ์Šค ๋™๊ธฐํ™” ๋ด‡ ๋งŒ๋“ค๊ธฐ (Ruby ์ดˆ๋ณด์ž๋„ ์‰ฝ๊ฒŒ ๋”ฐ๋ผํ•ด์š”)

Published 4 min readMarkdown

# ์„œ๋ก  โ€“ โ€œ์Œ์•… ๊ณต์œ ๊ฐ€ ์—ฌ๊ธฐ์„œ ๋ฉˆ์ถ”๋ฉด ์•ˆ ๋˜์ž–์•„์š”?โ€

Music Monday๋Š” ๋งค์ฃผ ์›”์š”์ผ๋งˆ๋‹ค ์ปค๋ฎค๋‹ˆํ‹ฐ ๋ฉค๋ฒ„๋“ค์ด ๋Œ“๊ธ€์— ์ถ”์ฒœ๊ณก์„ ๋‚จ๊ธฐ๋Š” ์Œ์•… ๊ณต์œ  ํ”„๋กœ์ ํŠธ์ž…๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋Œ“๊ธ€์— ์ ํžŒ ๊ณก๋“ค์„ ์ผ์ผ์ด ์ฐพ์•„์„œ Spotifyโ€ฏํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ์— ์ถ”๊ฐ€ํ•œ๋‹ค๋ฉด ์‹œ๊ฐ„๋„ ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๊ณ  ์‹ค์ˆ˜๋„ ์ƒ๊ธฐ๊ธฐ ๋งˆ๋ จ์ด์ฃ .

์ด๋Ÿฐ ๋ถˆํŽธ์„ ํ•ด์†Œํ•˜๊ณ ์ž Ruby๋กœ ๋งŒ๋“  ์ž‘์€ ์œ ํ‹ธ๋ฆฌํ‹ฐ, Music Monday Spotify Sync Bot์„ ์˜คํ”ˆ์†Œ์Šค๋กœ ๊ณต๊ฐœํ–ˆ์Šต๋‹ˆ๋‹ค.

์ด ๊ธ€์—์„œ๋Š” Spotify ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ, Ruby ์ž๋™ํ™”, Music Monday ๋ผ๋Š” SEO ํ‚ค์›Œ๋“œ๋ฅผ ์ค‘์‹ฌ์œผ๋กœ, ์ดˆ๋ณด์ž๋„ ์ดํ•ดํ•  ์ˆ˜ ์žˆ๋„๋ก ๋‹จ๊ณ„๋ณ„๋กœ ๊ตฌํ˜„ ๊ณผ์ •์„ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.


# 1๏ธโƒฃ ํ”„๋กœ์ ํŠธ ๊ฐœ์š” โ€“ ์–ด๋–ค ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋‚˜์š”?

| ๋ฌธ์ œ | ๊ธฐ์กด ๋ฐฉ์‹ | ์ž๋™ํ™” ์†”๋ฃจ์…˜ |

|------|-----------|--------------|

| ๋Œ“๊ธ€์— ์ ํžŒ ๊ณก์„ ์ˆ˜๋™์œผ๋กœ ๊ฒ€์ƒ‰ | Spotify ์•ฑ์„ ์—ด๊ณ  ๊ฒ€์ƒ‰ โ†’ ๊ณก ์ถ”๊ฐ€ | Ruby ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋Œ“๊ธ€์„ ํŒŒ์‹ฑ โ†’ ๊ณก ID ์ถ”์ถœ โ†’ API ํ˜ธ์ถœ๋กœ ์ž๋™ ์ถ”๊ฐ€ |

| ์—ฌ๋Ÿฌ ๊ณก์„ ์ผ๊ด„ ์ถ”๊ฐ€ํ•  ๋•Œ ์‹ค์ˆ˜ ๋ฐœ์ƒ | ํ•˜๋‚˜์”ฉ ํด๋ฆญ | ํ•œ ๋ฒˆ์— ์ „์ฒด ๊ณก์„ ์ฒ˜๋ฆฌ |

| ์ž‘์—… ๊ธฐ๋ก์ด ๋‚จ์ง€ ์•Š์Œ | ๊ธฐ์–ต์— ์˜์กด | ๋กœ๊ทธ ํŒŒ์ผ์— ์„ฑ๊ณต/์‹คํŒจ ๊ธฐ๋ก ์ €์žฅ |

์ด๋Ÿฌํ•œ ๋ฌธ์ œ๋ฅผ Ruby ์ž๋™ํ™” ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ํ•ด๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•œ ์„ค์ •๋งŒ์œผ๋กœ Music Monday ๋Œ“๊ธ€์„ ์ฝ๊ณ , ํ•ด๋‹น ๊ณก์„ Spotify ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ์— ์ž๋™์œผ๋กœ ๋™๊ธฐํ™”ํ•ฉ๋‹ˆ๋‹ค.


# 2๏ธโƒฃ ์ค€๋น„ ๋‹จ๊ณ„ โ€“ ํ•„์ˆ˜ ๋„๊ตฌ์™€ ๊ณ„์ • ์„ค์ •

# 2.1 Ruby ์„ค์น˜

  • macOS: brew install ruby

  • Windows: RubyInstaller ์‚ฌ์šฉ

  • Linux: sudo apt-get install ruby-full

# 2.2 Spotify ๊ฐœ๋ฐœ์ž ๊ณ„์ • ๋งŒ๋“ค๊ธฐ

  1. Spotify Developer Dashboard์— ๋กœ๊ทธ์ธ

  2. โ€œCreate an Appโ€ ํด๋ฆญ โ†’ Client ID์™€ Client Secret ๋ณต์‚ฌ

  3. Redirect URI์— http://localhost:8888/callback ์ž…๋ ฅ ํ›„ ์ €์žฅ

# 2.3 Music Monday API (๋˜๋Š” ๋Œ“๊ธ€ ์Šคํฌ๋ž˜ํ•‘) ์ค€๋น„

Music Monday๋Š” ๊ณต์‹ API๊ฐ€ ์—†์œผ๋ฏ€๋กœ, ๊ณต๊ฐœ๋œ RSS ํ”ผ๋“œ ํ˜น์€ HTML ํŒŒ์‹ฑ์„ ์ด์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฒˆ ์˜ˆ์ œ์—์„œ๋Š” RSS ํ”ผ๋“œ๋ฅผ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค.

# 2.4 ํ•„์š”ํ•œ Gem ์„ค์น˜

bash

gem install httparty json dotenv
  • httparty : HTTP ์š”์ฒญ ์ฒ˜๋ฆฌ

  • json : JSON ํŒŒ์‹ฑ

  • dotenv : ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๊ด€๋ฆฌ


# 3๏ธโƒฃ ์ฝ”๋“œ ๊ตฌํ˜„ โ€“ ํ•ต์‹ฌ ๋กœ์ง 4๋‹จ๊ณ„

# 3.1 ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋กœ๋“œ (.env ํŒŒ์ผ)

env

SPOTIFY_CLIENT_ID=your_client_id

SPOTIFY_CLIENT_SECRET=your_client_secret

SPOTIFY_PLAYLIST_ID=your_playlist_id

MUSIC_MONDAY_RSS=https://musicmonday.com/rss

# 3.2 Spotify ์ธ์ฆ ํ† ํฐ ์–ป๊ธฐ

ruby

require 'httparty'

require 'json'

require 'dotenv/load'

def fetch_spotify_token

  response = HTTParty.post(

    'https://accounts.spotify.com/api/token',

    body: { grant_type: 'client_credentials' },

    headers: {

      'Authorization' => 'Basic #{Base64.strict_encode64('#{ENV['SPOTIFY_CLIENT_ID']}:#{ENV['SPOTIFY_CLIENT_SECRET']}')}'

    }

  )

  JSON.parse(response.body)['access_token']

end

# 3.3 Music Monday ๋Œ“๊ธ€์—์„œ ํŠธ๋ž™ ์ •๋ณด ์ถ”์ถœ

ruby

require 'rss'

def fetch_tracks_from_monday

  rss = RSS::Parser.parse(ENV['MUSIC_MONDAY_RSS'], false)

  tracks = []

  rss.items.each do |item|

    # ์˜ˆ์‹œ: ๋Œ“๊ธ€์— 'Artist - Title' ํ˜•์‹์œผ๋กœ ์ ํžŒ ๊ฒฝ์šฐ

    if item.title =~ /(.*) - (.*)/

      tracks << { artist: $1.strip, title: $2.strip }

    end

  end

  tracks

end

# 3.4 Spotify์— ํŠธ๋ž™ ์ถ”๊ฐ€

ruby

def search_spotify_track(token, artist, title)

  query = URI.encode_www_form_component('track:#{title} artist:#{artist}')

  url = 'https://api.spotify.com/v1/search?q=#{query}&type=track&limit=1'

  response = HTTParty.get(url, headers: { 'Authorization' => 'Bearer #{token}' })

  items = JSON.parse(response.body)['tracks']['items']

  items.empty? ? nil : items.first['uri']

end

def add_tracks_to_playlist(token, uris)

  url = 'https://api.spotify.com/v1/playlists/#{ENV['SPOTIFY_PLAYLIST_ID']}/tracks'

  HTTParty.post(

    url,

    body: { uris: uris }.to_json,

    headers: {

      'Authorization' => 'Bearer #{token}',

      'Content-Type' => 'application/json'

    }

  )

end

# ๋ฉ”์ธ ์‹คํ–‰ ํ๋ฆ„

token = fetch_spotify_token

tracks = fetch_tracks_from_monday

uris = tracks.map { |t| search_spotify_track(token, t[:artist], t[:title]) }.compact

add_tracks_to_playlist(token, uris) unless uris.empty?

puts 'โœ… #{uris.size}๊ฐœ์˜ ๊ณก์ด Spotify ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ์— ์ถ”๊ฐ€๋˜์—ˆ์Šต๋‹ˆ๋‹ค.'

Tip

search_spotify_track ํ•จ์ˆ˜๋Š” ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๊ฐ€ ์—†์„ ๊ฒฝ์šฐ nil์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์‹ค์ œ ์šด์˜ ์‹œ์—๋Š” ๋กœ๊ทธ ํŒŒ์ผ์— ์‹คํŒจ ์›์ธ์„ ๋‚จ๊ฒจ๋‘๋ฉด ๋””๋ฒ„๊น…์ด ์‰ฌ์›Œ์š”.


# 4๏ธโƒฃ ๋ฐฐํฌ์™€ ์ž๋™ ์‹คํ–‰ โ€“ ๋งค์ฃผ ์›”์š”์ผ ์ž๋™ ๋™๊ธฐํ™”

  1. GitHub์— ์ €์žฅ
bash

   git init

   git add .

   git commit -m 'Initial commit โ€“ Music Monday Spotify Sync Bot'

   git push origin main
  1. GitHub Actions ์„ค์ • (.github/workflows/sync.yml)
yaml

   name: Weekly Sync

   on:

     schedule:

       - cron: '0 9 * * 1'   # ๋งค์ฃผ ์›”์š”์ผ 09:00 UTC

   jobs:

     sync:

       runs-on: ubuntu-latest

       steps:

         - uses: actions/checkout@v3

         - name: Set up Ruby

           uses: ruby/setup-ruby@v1

           with:

             ruby-version: '3.2'

         - name: Install dependencies

           run: gem install httparty json dotenv

         - name: Run sync script

           env:

             SPOTIFY_CLIENT_ID: ${{ secrets.SPOTIFY_CLIENT_ID }}

             SPOTIFY_CLIENT_SECRET: ${{ secrets.SPOTIFY_CLIENT_SECRET }}

             SPOTIFY_PLAYLIST_ID: ${{ secrets.SPOTIFY_PLAYLIST_ID }}

             MUSIC_MONDAY_RSS: ${{ secrets.MUSIC_MONDAY_RSS }}

           run: ruby sync.rb

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋งค์ฃผ ์›”์š”์ผ ์ž๋™์œผ๋กœ ์ตœ์‹  ๋Œ“๊ธ€์„ ์ฝ๊ณ , Spotify ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ์— ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค. ๋ณ„๋„์˜ ์„œ๋ฒ„๋ฅผ ์šด์˜ํ•  ํ•„์š” ์—†์ด GitHubโ€ฏActions๋งŒ์œผ๋กœ ์ถฉ๋ถ„ํ•ฉ๋‹ˆ๋‹ค.


# 5๏ธโƒฃ ํ™•์žฅ ์•„์ด๋””์–ด โ€“ ์—ฌ๋Ÿฌ๋ถ„๋งŒ์˜ ์ปค์Šคํ…€ ๋ด‡ ๋งŒ๋“ค๊ธฐ

| ์•„์ด๋””์–ด | ๊ตฌํ˜„ ๋ฐฉ๋ฒ• |

|----------|-----------|

| ๋‹ค๋ฅธ ์ŠคํŠธ๋ฆฌ๋ฐ ์„œ๋น„์Šค ์—ฐ๋™ | Appleโ€ฏMusic, YouTube Music API ํ™œ์šฉ |

| ๋Œ“๊ธ€ ํฌ๋งท ๋‹ค์–‘ํ™” | ์ •๊ทœํ‘œํ˜„์‹ ์ถ”๊ฐ€ยท์ˆ˜์ • |

| ์•Œ๋ฆผ ๊ธฐ๋Šฅ | Discord webhook ๋˜๋Š” Slack ๋ฉ”์‹œ์ง€ ์ „์†ก |

| UI ์ œ๊ณต | Sinatra ํ˜น์€ Rails๋กœ ๊ฐ„๋‹จํ•œ ์›น ๋Œ€์‹œ๋ณด๋“œ ๊ตฌ์ถ• |

์œ„์™€ ๊ฐ™์€ ๊ธฐ๋Šฅ์„ ์ฐจ๊ทผ์ฐจ๊ทผ ์ถ”๊ฐ€ํ•˜๋ฉด, Ruby ์ž๋™ํ™” ์Šคํ‚ฌ๋„ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ํ–ฅ์ƒ๋ฉ๋‹ˆ๋‹ค.


# ๊ฒฐ๋ก  โ€“ ์ง€๊ธˆ ๋ฐ”๋กœ ์‹œ์ž‘ํ•ด ๋ณด์„ธ์š”!

Music Monday์˜ ํ’๋ถ€ํ•œ ์Œ์•… ์ถ”์ฒœ์„ ๋†“์น˜์ง€ ๋ง๊ณ , Spotify ํ”Œ๋ ˆ์ด๋ฆฌ์ŠคํŠธ์— ์ž๋™์œผ๋กœ ๋™๊ธฐํ™”ํ•ด ๋ณด์„ธ์š”.

1๏ธโƒฃ Ruby์™€ ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋งŒ ์ค€๋น„ํ•˜๋ฉด 10๋ถ„ ์•ˆ์— ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

2๏ธโƒฃ GitHubโ€ฏActions๋ฅผ ์ด์šฉํ•ด ๋งค์ฃผ ์›”์š”์ผ ์ž๋™ ์‹คํ–‰๊นŒ์ง€ ์„ค์ •ํ•˜๋ฉด, Music Monday์™€ Spotify๊ฐ€ ์™„๋ฒฝํžˆ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค.

ํ–‰๋™ ์œ ๋„

  • ์ง€๊ธˆ ๋ฐ”๋กœ ๋ ˆํฌ์ง€ํ† ๋ฆฌ๋ฅผ ํฌํฌํ•˜๊ณ , .env ํŒŒ์ผ์— ๋ณธ์ธ ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•ด ๋ณด์„ธ์š”.

  • ์‹คํ–‰ ๊ฒฐ๊ณผ๋ฅผ ๋Œ“๊ธ€๋กœ ๊ณต์œ ํ•˜๊ฑฐ๋‚˜, ๊ฐœ์„  ์•„์ด๋””์–ด๋ฅผ Pull Request๋กœ ๋ณด๋‚ด ์ฃผ์„ธ์š”!

์Œ์•…์„ ์‚ฌ๋ž‘ํ•˜๋Š” ๋ชจ๋“  ๊ฐœ๋ฐœ์ž์™€ ์ฒญ์ทจ์ž๊ฐ€ ํ•จ๊ป˜ ๋งŒ๋“œ๋Š” ์˜คํ”ˆ์†Œ์Šค ๋™๊ธฐํ™” ๋ด‡, ์—ฌ๋Ÿฌ๋ถ„์˜ ์ฐธ์—ฌ๋ฅผ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค. ๐ŸŽถ๐Ÿš€

No comments yet. Be the first to comment.

More from this blog