트러블 슈팅: Spotify API를 활용한 음악 데이터 입력 자동화 - minumsa/carver-music GitHub Wiki
문제 상황
카버뮤직에서는 관리자 아이디로 로그인한 후, 업로드 페이지에서 글을 작성할 수 있습니다. 하지만 카버뮤직은 음반 리뷰에 특화된 블로그이므로, 글을 작성할 때 제목과 내용뿐만 아니라 음반 관련 데이터(앨범 제목, 가수 이름, 발매일, 레이블, 앨범아트 이미지 링크 등)도 함께 입력해야 합니다.
초기에는 각각의 데이터를 위한 입력란을 만들어 일일이 검색해 입력했지만, 시간을 절약하기 위해 이 작업을 자동화할 필요가 있었습니다.
해결 과정
Spotify API 도입
그래서 음악 데이터를 제공하는 API를 찾던 중, Spotify API를 선택했습니다. 이 API는 카버뮤직에 필요한 다양한 음악 데이터를 무료로 제공하며, 사용법도 매우 간단했습니다. API를 사용할 때는 사이트에서 발급받은 전용 액세스 토큰을 생성한 후, 데이터를 요청해 가져오면 됩니다.
Spotify API를 활용해 업로드 페이지의 앨범 제목 입력란에 키워드를 입력하면, 관련된 앨범이 상위 5개까지 모달 창에 표시되도록 구현했습니다. 사용자가 원하는 앨범을 클릭하면, 관련 데이터가 자동으로 모두 입력되도록 처리했죠. 이제 사용자는 원하는 앨범을 검색해 클릭하고, 내용을 입력하기만 하면 글쓰기를 완료할 수 있습니다.
Spotify API |
---|
Spotify API 데이터 보안 관리
앞서 언급했듯이, Spotify API를 호출하려면 액세스 토큰이 필요하고, 이 토큰을 생성하기 위해서는 사용자 ID와 암호가 필요합니다. 그런데 잘 작동하던 이 코드가 어느 순간부터 오류를 발생시키기 시작했습니다.
Spotify 관련 에러 |
---|
Spotify API 관련 코드를 전체적으로 점검한 후, 다시 튜토리얼을 꼼꼼히 살펴보던 중, 토큰의 유효 기간이 1시간이라는 문구를 발견했습니다.
Spotify API 튜토리얼 |
---|
생성된 토큰을 콘솔에 출력해보니, 실제로 토큰 인증과 관련된 에러 메시지가 나타났습니다. 이는 만료된 토큰으로 Spotify API를 호출하려 했기 때문에 발생한 문제였습니다.
Spotify API 에러 콘솔 출력 |
---|
Next.js의 fetch 함수 반환값 자동 캐싱
한편, 토큰을 생성한 후 따로 저장 기능을 구현하지 않았는데도 토큰이 자동으로 캐싱되는 이유가 궁금했습니다. 그러던 중, Next.js가 fetch 함수의 반환값을 자동으로 캐시한다는 사실을 알게 되었습니다.
By default, Next.js automatically caches the returned values of fetch in the Data Cache on the server. This means that the data can be fetched at build time or request time, cached, and reused on each data request.
토큰이 1시간 동안 유효하다는 점을 고려해, 그동안은 토큰을 재사용하고, 시간이 만료되면 새로운 토큰을 발급하는 방식으로 코드를 수정했습니다. 또한, 토큰 생성 시 Spotify API를 호출할 때, Next.js의 fetch 함수에 대한 자동 캐싱 기능을 비활성화했습니다. 한편 보안을 위해 CLIENT_ID와 CLIENT_SECRET 같은 중요한 정보는 .env 파일에 저장하여 관리하고, dotenv를 사용해 이를 코드에 안전하게 불러오도록 처리했습니다.
let cachedAccessToken: string | null = null;
let tokenExpirationTime: number = 0;
async function getToken() {
try {
const tokenNotExpired = cachedAccessToken < tokenExpirationTime;
if (tokenNotExpired) {
return cachedAccessToken;
}
require("dotenv").config();
const url = "https://accounts.spotify.com/api/token";
const clientId = process.env.CLIENT_ID;
const clientSecret = process.env.CLIENT_SECRET;
// ...
const result = "grant_type=client_credentials";
const accessTokenResponse = await fetch(url, {
method: "POST",
headers,
body: result,
cache: "no-store",
});
if (!accessTokenResponse.ok) {
console.error("Error: Access token fetch failed");
}
const response = await accessTokenResponse.json();
cachedAccessToken = response.access_token;
tokenExpirationTime = Date.now() + 3600 * 1000;
return cachedAccessToken;
} catch (error) {
console.error(error);
}
}
배운 점
- 액세스 토큰의 보안 및 만료 기간 문제를 처리해보았습니다.
- Next.js의 fetch 함수 반환값 자동 캐싱 기능에 대해 알게 되었습니다.
- 기술 문서는 가급적 꼼꼼히 읽어야겠다는 교훈을 얻었습니다.