이번 시간에는 Spotify Web API를 쉽게 사용할 수 있는 라이브러리인 `spotify-web-api-java`를 활용해서 아티스트 정보를 검색하는 API를 만들어보고자 한다.
https://github.com/spotify-web-api-java/spotify-web-api-java
GitHub - spotify-web-api-java/spotify-web-api-java: A Java wrapper for Spotify's Web API.
A Java wrapper for Spotify's Web API. Contribute to spotify-web-api-java/spotify-web-api-java development by creating an account on GitHub.
github.com
Spotify API 사용하기 with Spring Boot
build.gradle
- spotify-web-api-java를 사용하기 위해서는 `build.gradle`에 다음의 내용을 추가해야 한다.
allprojects {
repositories {
maven { url 'https://jitpack.io' }
}
}
...
dependencies {
...
implementation 'se.michaelthelin.spotify:spotify-web-api-java:8.4.1'
}
- Spotify API 클라이언트를 설정하기 위한 구성 클래스를 정의한다.
package com.duboocho.hilarious;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import se.michaelthelin.spotify.SpotifyApi;
@Configuration
public class SpotifyConfig {
@Value("${spotify.client-id}")
private String clientId;
@Value("${spotify.client-secret}")
private String clientSecret;
@Bean
public SpotifyApi spotifyApi() {
return new SpotifyApi.Builder()
.setClientId(clientId)
.setClientSecret(clientSecret)
.build();
}
}
application.properties
- 지난 시간에 발급받은 `Client ID`와 `Client Secret` 값은 환경 변수로 관리한다.
spring.application.name=hilarious
spotify.client-id=${SPOTIFY_CLIENT_ID}
spotify.client-secret=${SPOTIFY_CLIENT_SECRET}
Artist 정보 조회 API 만들기
🔗 spotify-web-api-java - SearchArtistsExample
`SpotifyApi` 클래스를 활용하면 Web API의 기능들을 편리하게 사용할 수 있다.
지난 시간에 사용했던 search API를 활용하여 다음과 같은 정보들을 얻어오고자 한다.
- 아티스트 프로필 사진
- 아티스트 장르
- 아티스트의 대표곡
이를 위해서는 라이브러리가 제공하는 클래스에 대해서 알아야 한다.
Artist 클래스
아티스트에 대한 세부 정보를 담고 있는 클래스
@JsonDeserialize(builder = Artist.Builder.class)
public class Artist extends AbstractModelObject implements IArtistTrackModelObject, ISearchModelObject {
private final ExternalUrl externalUrls;
private final Followers followers;
private final String[] genres; //아티스트가 주로 활동하는 장르 목록
private final String href;
private final String id; //고유 식별자. API 호출 시 사용됨
private final Image[] images; //아티스트 관련 이미지 목록
private final String name; //아티스트 이름
private final Integer popularity;
private final ModelObjectType type;
private final String uri;
...
}
Track 클래스
곡 정보에 대한 세부 정보를 담고 있는 클래스
@JsonDeserialize(builder = Track.Builder.class)
public class Track extends AbstractModelObject implements IArtistTrackModelObject, ISearchModelObject, IPlaylistItem {
private final AlbumSimplified album; //트랙이 속한 앨범
private final ArtistSimplified[] artists;
private final CountryCode[] availableMarkets;
private final Integer discNumber;
private final Integer durationMs;
private final Boolean explicit;
private final ExternalId externalIds;
private final ExternalUrl externalUrls;
private final String href;
private final String id;
private final Boolean isPlayable;
private final TrackLink linkedFrom;
private final Restrictions restrictions;
private final String name; //트랙(곡)의 제목
private final Integer popularity;
private final String previewUrl; //트랙의 미리 듣기 URL
private final Integer trackNumber;
private final ModelObjectType type;
private final String uri;
...
}
TrackInfo
`Track` 클래스에서 내가 원하는 정보만 추출하기 위한 클래스
- 곡 제목, 앨범 제목, 미리 듣기 URL
package com.duboocho.hilarious.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class TrackInfo {
private String trackName;
private String albumName;
private String previewUri;
}
SearchResponseDto
아티스트명으로 검색했을 때 반환하고자 하는 데이터 정의
- 아티스트 정보 - 아티스트 ID(이건 개발용), 아티스트 이름, 대표 이미지, 활동하는 장르 목록
- 아티스트의 대표곡
package com.duboocho.hilarious.dto;
import lombok.Data;
import se.michaelthelin.spotify.model_objects.specification.Artist;
import java.util.List;
@Data
public class SearchResponseDto {
private String artistId;
private String artistName;
private String imageUrl;
private String[] genres;
private List<TrackInfo> tracks;
public SearchResponseDto(Artist artist, List<TrackInfo> tracks) {
this.artistId = artist.getId();
this.artistName = artist.getName();
this.imageUrl = artist.getImages()[0].getUrl();
this.genres = artist.getGenres();
this.tracks = tracks;
}
}
SpotifyService
아티스트명으로 검색했을 때, 해당 아티스트 관련 정보와 대표곡 3곡을 반환하도록 구현했다.
package com.duboocho.hilarious;
import com.duboocho.hilarious.dto.SearchResponseDto;
import com.duboocho.hilarious.dto.TrackInfo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.hc.core5.http.ParseException;
import org.springframework.stereotype.Service;
import se.michaelthelin.spotify.SpotifyApi;
import se.michaelthelin.spotify.exceptions.SpotifyWebApiException;
import se.michaelthelin.spotify.model_objects.credentials.ClientCredentials;
import se.michaelthelin.spotify.model_objects.specification.Artist;
import se.michaelthelin.spotify.model_objects.specification.Paging;
import se.michaelthelin.spotify.model_objects.specification.Track;
import se.michaelthelin.spotify.requests.authorization.client_credentials.ClientCredentialsRequest;
import se.michaelthelin.spotify.requests.data.artists.GetArtistsTopTracksRequest;
import se.michaelthelin.spotify.requests.data.search.simplified.SearchArtistsRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import static com.neovisionaries.i18n.CountryCode.KR;
@Slf4j
@Service
@RequiredArgsConstructor
public class SpotifyService {
private static final int SEARCH_LIMIT = 1;
private static final int TOP_TRACK_LIMIT = 3;
private static final String ERROR_FETCHING_ACCESS_TOKEN = "Error fetching access token";
private static final String ERROR_SEARCHING_ARTIST = "Error searching artist";
private final SpotifyApi spotifyApi;
private String accessToken;
public List<SearchResponseDto> search(String artistName) {
try {
if (accessToken == null || accessToken.isEmpty()) {
accessToken = fetchAccessToken();
}
Artist[] artists = searchArtist(artistName);
List<SearchResponseDto> results = new ArrayList<>();
for (Artist artist : artists) {
List<TrackInfo> tracks = getTopTracks(artist.getId());
results.add(new SearchResponseDto(artist, tracks));
}
return results;
} catch (IOException | SpotifyWebApiException | ParseException e) {
log.error(ERROR_SEARCHING_ARTIST + e.getMessage());
return new ArrayList<>();
}
}
private Artist[] searchArtist(String artistName) throws IOException, ParseException, SpotifyWebApiException {
SearchArtistsRequest request = spotifyApi.searchArtists(artistName)
.limit(SEARCH_LIMIT)
.build();
Paging<Artist> searchResult = request.execute();
return Optional.ofNullable(searchResult.getItems()).orElse(new Artist[0]);
}
private List<TrackInfo> getTopTracks(String artistId) throws IOException, ParseException, SpotifyWebApiException {
GetArtistsTopTracksRequest request = spotifyApi.getArtistsTopTracks(artistId, KR)
.build();
Track[] tracks = request.execute();
List<TrackInfo> trackInfos = new ArrayList<>();
for (int i = 0; i < Math.min(TOP_TRACK_LIMIT, tracks.length); i++) {
Track track = tracks[i];
trackInfos.add(new TrackInfo(track.getName(), track.getAlbum().getName(), track.getPreviewUrl()));
}
return trackInfos;
}
private String fetchAccessToken() {
ClientCredentialsRequest clientCredentialsRequest = spotifyApi.clientCredentials().build();
try {
final ClientCredentials clientCredentials = clientCredentialsRequest.execute();
spotifyApi.setAccessToken(clientCredentials.getAccessToken());
return spotifyApi.getAccessToken();
} catch (IOException | SpotifyWebApiException | ParseException e) {
log.error(ERROR_FETCHING_ACCESS_TOKEN + e.getMessage());
return "error";
}
}
}
SpotifyController
package com.duboocho.hilarious;
import com.duboocho.hilarious.dto.SearchResponseDto;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequiredArgsConstructor
public class SpotifyController {
private final SpotifyService spotifyService;
@GetMapping("/search")
public ResponseEntity<List<SearchResponseDto>> search(@RequestParam("artistName") String artistName) {
return ResponseEntity.ok(spotifyService.search(artistName));
}
}
실행 결과
`localhost:8080/search?artistName=day6`로 검색했을 때 결과
'Project > hilarious' 카테고리의 다른 글
[SpotifyAPI][2] DB에 데이터 저장하기 (0) | 2024.12.03 |
---|---|
[SpotifyAPI][0] App 생성 및 간단한 API 테스트 (0) | 2024.10.31 |