[Team Project] Spotify API 의 사이드 이펙트

liinyeye·2024년 7월 10일
0

Project

목록 보기
27/44

🔎 문제 정의

1. 상황

팀 프로젝트에서 Spotify API를 활용하기로 결정했지만, 이후 API가 제공하는 기능과 데이터가 기획 방향과 맞지 않는다는 문제점을 발견했다. 프로젝트 기간이 일주일도 채 되지 않는 짧은 시간이었기에, 기획을 전면 수정하는 것은 현실적으로 어려웠다. 이에 기존 기획을 유지하면서도 문제를 해결할 수 있는 대안을 모색하여 프로젝트를 진행하기로 결정했다.

2. 문제가 됐던 부분

  • 전 곡 재생은 Spotify API는 무료 버전에서 지원하지 않음
  • 대신 제공되는 미리 듣기 음원이 없는 경우도 존재
  • 원하는 플레이리스트를 불러오기 위해서는 각 플레이리스트의 고유 ID가 필요함
  • 사용자별 추천 플레이리스트를 사용하려면 Spotify 구독이 필요함

💡 Spotify API 한계를 뛰어넘은 해결 과정

1. 미리 듣기 음원 사용, 없을 경우 적절한 알림 띄워주기

플레이리스트의 트랙 중 미리듣기 음원 데이터(preview_url)가 없는 경우는 null로 명시적으로 값을 지정해, 클라이언트에서 조건에 따라 사용할 수 있도록 해줬다.

미리 듣기 음원 처리

const isPlayingAvailable = track.preview_url;

서버 측 데이터

const processedPlaylist: SpotifyPlaylistTracks = {
            id: playlistResponse.data.id,
            name: playlistResponse.data.name,
            external_urls: {
              spotify: playlistResponse.data.external_urls.spotify
            },
            tracks: tracksResponse.data.items.map((item) => ({
              ...item.track,
              preview_url: item.track.preview_url ?? null,
              external_urls: {
                spotify: item.track.external_urls.spotify
              }
            }))
          };

2. 플레이리스트 ID 및 메타데이터 DB에서 별도로 관리하기

트랙이 담겨있는 플레이리스트 ID에 대한 정보만 얻을 수 있는 api가 없었기 때문에 따로 플레이리스트를 찾아 필요한 메타 데이터만 정리해 데이터베이스에서 따로 관리하기로 결정했다.

3. 여러 API 요청 병렬 처리로 성능 최적화 및 에러 처리

Supabase DB에서 가져온 플레이리스트 ID를 활용해 트랙 정보를 가져와 플레이리스트별 트랙을 보여준다.

const playlistsWithTracks = await Promise.all( ... )

이 때 Promise.all을 사용해 필요한 API 요청을 병렬 처리하여 로딩 시간을 최소화했으며, 에러가 발생한 요청은 null 값을 반환하도록 설정하고, 이를 UI에서 필터링하여 사용자에게 오류 없이 올바른 플레이리스트만 표시되도록 구현했다.

  • 속도 측정 테스트
    - 순차 실행 속도 : 6550ms
    - 병렬 실행 속도 : 1350ms
  • 결론
    병렬 실행 > 순차 실행 : 75.43% 더 빠름
export const GET = async () => {
  const playlistsIds = await getPlaylistIdList();

  try {
    const playlistsWithTracks = await Promise.all(
      playlistsIds.map(async (playlistId) => {
        try {
          const [playlistResponse, tracksResponse] = await Promise.all([
            spotifyApiAxios.get<SpotifyPlaylist>(`/playlists/${playlistId}`, {
              headers: {
                "Content-Type": "application/json"
              }
            }),
            spotifyApiAxios.get<{ items: { track: SpotifyTrack }[] }>(`/playlists/${playlistId}/tracks`, {
              params: {
                fields:
                  "items(track(id,name,preview_url,external_urls,duration_ms,artists(id,name),album(id,name,images)))",
                limit: 8
              }
            })
          ]);

          // ... 플레이리스트 트랙 관련 로직
          
          return processedPlaylist;
        } catch (error) {
          console.error(`Error fetching playlist ${playlistId}:`, error);
          return null;
        }
      })
    );

    const validPlaylists = playlistsWithTracks.filter((playlist) => playlist !== null);
    return NextResponse.json(validPlaylists);
  } catch (error) {
    console.error("Error fetching track:", error);
    return NextResponse.json({ error: "Failed to fetch playlists data" }, { status: 500 });
  }
};

4. 별도의 랜덤 플레이스트 생성하기

Spotify api에서 제공되는 인기 플레이리스트 데이터가 있었다. 기획 상 모든 플레이리스트를 보여줄 필요는 없었고, 대신 추천하는 한 가지만 보여주되 매번 다른 플레이리스트를 보여줘서 재미 요소를 더했다.

따라서 제공되는 대량의 트렌딩 플레이리스트 중 일부를 가져와 페이지를 새로고침할 때마다 random index를 골라 매번 다른 플레이리스트를 보여줄 수 있도록 했다.

const randomIndex = Math.floor(Math.random() * featuredPlaylists.length);
  const selectedPlaylist = featuredPlaylists[randomIndex];

🚀 기술적 한계 극복의 성과

성능 최적화

  • 네트워크 요청 속도 - 약 75% 감소(6550ms -> 1350ms)

사용자 경험 개선

  • 사용자는 매번 새로운 추천 플레이리스트를 확인할 수 있었고, 미리 듣기 음원이 없는 곡에 대해서도 명확한 안내를 제공받음으로써 혼란을 줄였다.

프로젝트의 목표 달성

  • Spotify API의 제한을 극복하면서도 기획 의도를 살린 결과, 기획 변경 없이 안정적인 기능 구현에 성공했다.
profile
웹 프론트엔드 UXUI

0개의 댓글