학회 플젝에서 진행했었던 프로젝트 내의 Spotify 소셜 로그인 기능에 대해 작성해보려고 합니당~.~
당시 플젝 기간이 일주일도 채 안됐었어서 대강 굴러가는 대로 코드를 짰었는데, 현재 리팩토링 작업을 하고 있어 기록 겸 쓰는 글입니다.
저희는 백엔드와 프론트엔드가 함께 로그인 기능을 구현했는데, NextJS 쓰는 분이라면 그냥 Next-Auth를 쓰십시오... 그때 10분만에 만들 수 있었어요
앱 내에 만든 스포티파이 플레이어에서 노래를 재생하기 위해서는, 우리 어플리케이션에 로그인할 때 스포티파이 프리미엄 계정으로 소셜 로그인을 해야 한다! 소셜 로그인을 하면 access_token을 주는데, 이 토큰을 Spotify Web API의 인자로 넣어야 노래 재생이 가능하기 때문.
또 유의해야 할 점은 토큰의 유효 시간이다. 1시간이 지나면 토큰이 expired 상태가 되어 노래 재생이 불가능해진다. 이때 다시 새로운 토큰을 요청해야 한다.
아래 이미지는 공식문서에 나와 있는 authorization code flow다.
백엔드 프론트엔드 둘 다 이 과정을 제대로 이해 못해서 한참 헤맸다 ㅋㅋ 😞
https://developer.spotify.com/documentation/general/guides/authorization/code-flow/
위 이미지의에서
APPLICATION = 우리가 지금 만들고 있는 앱을 의미한다
USER = 우리 앱에 로그인하려는 유저를 의미한다.
보면 access_token을 얻기 위해서 두 번의 요청이 필요하다.
❶번에서 유저가 로그인을 하면 바로 토큰을 주는 게 아니라 토큰을 얻을 수 있는 code를 리턴한다. 이 code를 가지고 스포티파이 측에 한 번 더 요청을 해야(❷번) 응답으로 토큰을 받을 수 있다.
토큰은 참고로 이렇게 생겼다.
BQCEB5lEagZLI5J9ims5z_aVbOGLYY5S_zSysgLqiUQtfiSAE8QO8doBwS55oL69RJK3qaN89hClJzInfEPVAnd9I0SyVOmy7Y0XomvHu8QE3DML8ic_o7sOUuY0TyLApQ7MEi--X_0eT0NSQlYvGOea3gwww-bh-KjRFrPEeTrtqSj8oWMGMz6Jq4LWMIdsnV-Z5YqqCe-No8IFKoVk6rqWJioTubOHVfo
우리가 구현한 방식은 다음과 같다.
- Front: 유저가 로그인 버튼을 클릭하면 Back에
requestLogin
API를 호출한다.- Back: Spotify에 로그인을 요청한다.
- Spotify: 유저가 로그인할 수 있는 스포티파이 로그인 페이지 url을 리턴한다.
Back은 응답으로 받은 이 url을 Front에 전달한다.(requestLogin
의 응답으로)- Front: 해당 url로 유저를 이동시킨다.
- User: 해당 페이지 내에서 로그인 버튼을 클릭
- Spotify: 우리가 미리 등록해둔 Redirect URL로 유저를 리다이렉션 시킨다.
이때 url의 query string에 access_token을 받을 수 있는 code를 포함해 리다이렉트 한다.- Front: url을 파싱해 code를 얻어낸다. 얻어낸 code를 query string에 담아
getToken
API를 호출한다.- Back: 프론트가 보내준 코드를 사용해 스포티파이에 acess_token을 요청. 응답으로 받은 access_token을 Front에 전달한다.(
getToken
의 응답으로)
더럽게 복잡하네요
Redirect URL등록은 여기다 하시면 돼요. 👇
백엔드랑 상의해서 작성한 API 명세다. 이 명세에 따라 함수를 작성했다.
토큰을 여러 곳에서 사용하기 때문에 토큰을 저장하는 Context를 생성했다.
// context/TokenContext.tsx
import { createContext, useContext, useEffect, useState } from "react";
const TokenContext = createContext<string | null>(null);
const SetTokenContext = createContext<(token: string | null) => void>(() => {});
export const TokenProvider = ({ children }: { children: React.ReactNode }) => {
const [token, setToken] = useState<string | null>(null);
return (
<TokenContext.Provider value={token}>
<SetTokenContext.Provider value={setToken}>
{children}
</SetTokenContext.Provider>
</TokenContext.Provider>
);
};
export const useToken = () => {
const token = useContext(TokenContext);
return token;
};
export const useSetToken = () => {
const setToken = useContext(SetTokenContext);
return setToken;
};
useToken과 useSetToken Hook을 활용하면 어디서든 토큰에 접근하고 토큰을 수정할 수 있다.
❶ 유저가 앱에 접속
👉 index.tsx의 useCheckToken이 실행된다.
const useCheckToken = () => {
const token = useToken();
const setToken = useSetToken();
const router = useRouter();
useEffect(() => {
if (token) {
isTokenValid(token).then((isValid) => {
if (!isValid) {
setToken(null);
router.push("/login");
}
});
} else {
router.push("/login");
}
}, [router, setToken, token]);
};
useToken에 토큰 있으면 isTokenValid
로 토큰의 유효성을 검사한다.
useToken에 토큰 없으면 바로 로그인 페이지로 리다이렉트 한다.
❷ login.tsx
로그인 버튼이 있는 페이지. 유저가 버튼을 클릭하면 requestLogin
API를 호출한다.
❸ loading.tsx
Spotify App에 등록해놓은 Redirect URL이다.
스포티파이 로그인 페이지에서 유저가 스포티파이로 로그인 하면 해당 페이지로 리다이렉트 된다.
해당 페이지의 useEffect에서 query string을 파싱하고, getToken
API를 호출한다.
getToken
의 응답으로 받은 access_token을 useSetToken을 활용해 저장한다.
이게 프로젝트 마지막 날까지의 진행 상황이다. 근데 문제가 많아서 혼자 리팩토링 중이다.
우선 가장 큰 문제
새로고침 하면 로그인이 풀린다
다음 포스팅에선 새로고침 시에도 로그인이 유지되는 기능을 구현해 보겠다.