지금 개발이 남아있는 기능은 검색바에서 실시간으로 곡제목, 아티스트명으로 곡을 검색하고 선택되어있는 내 플레이리스트에 항목을 추가하는 것이다.
// 항목 검색 (곡 & 아티스트)
export const searchMenu = async (query: string) => {
const accessToken = await getPrivateAccessToken();
const res = await axios.get(`${BASEURL}/search`, {
headers: {
Authorization: `Bearer ${accessToken}`
},
params: {
q: query,
type: "track,artist",
limit: 10
}
});
console.log(res.data);
return res.data.tracks.items;
};
const SearchSong = () => {
const [song, setSong] = useState<string | null>("");
const {
data: searchResult,
isLoading,
error,
refetch
} = useQuery<SearchTrack[]>({
queryKey: ["searchSong", song],
queryFn: () => searchMenu(song),
enabled: !!song
});
if (isLoading) {
return <div>Loading...</div>;
}
if (error) {
return <div>곡을 찾아 오는 도중 에러가 발생했습니다.</div>;
}
return (
<div className="flex flex-col gap-8 pt-4 bg-blue-50 p-4 rounded h-full">
<h3>SearchSong</h3>
<input
type="text"
placeholder="곡을 입력해주세요"
value={song}
onChange={(e) => {
setSong(e.target.value);
refetch();
}}
className="w-[20rem] h-[2rem]"
/>
<div>
// Throttle 함수
const useThrottle = <T,>(value: T, limit: number): T => {
const [throttledValue, setThrottledValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setThrottledValue(value);
}, limit);
return () => {
clearTimeout(handler);
};
}, [value, limit]);
return throttledValue;
};
const SearchSong: React.FC<{ playlistId: string }> = ({ playlistId }) => {
const [song, setSong] = useState<string>("");
const throttledSong = useThrottle(song, 300);
const queryClient = useQueryClient();
const { data: searchResult = [], error } = useQuery<SongMenu[], Error>({
queryKey: ["searchSong", throttledSong],
queryFn: () => searchMenu(throttledSong),
enabled: !!throttledSong,
staleTime: 5000
});
props함수명을 잘못 받았다.
나는 props-drilling을 활용해 클릭된 재생목록의 id를 받아왔는데 작업을 하며 부모컴포넌트에서 자식컴포넌트로 받을 때 보냈던 명칭과 다르게 받아 값이 들어오고 있지 않은 것이었다.
무한 console.log를 찍으면서 발견...
내가 사용하는 api들은 내가 작성한 privateaccessToken을 가져와서 사용하게 되는 것이었다.
오류메세지를 확인해보니 계속해서 403에러가 났다.
어떤 이유인지 몰라 다시한번 spotify developer docs를 하나하나 정독했다. 잘 찾아보니 이렇게 나와있었다.
supabase로 로그인을 하는 로직에서 미리 scope를 정해주어야하는 것이었다.
playlist-modify-private playlist-modify-public"
를 추가한 이후에 바로 반영이 되었다...
// Login handler
const handleSpotifyLogin = async () => {
const { error } = await supabase.auth.signInWithOAuth({
provider: "spotify",
options: {
redirectTo: "http://localhost:3000/auth/callback",
scopes:
"user-read-private playlist-read-private playlist-read-collaborative playlist-modify-private playlist-modify-public"
}
});
로직을 작성하기 이전에 API를 제공해주는 곳에서 걸어놓은 조건들에 조금 더 집중해야겠다는 생각을 많이 했다. 그래도 해결했을 때의 그 기쁨이 힘듦을 씻어주는 것 같다..!!!
ex) 스크롤 이벤트 - 일정시간동안 스크롤 이벤트가 여러번 발생하더라도 마지막 스크롤 위치만 사용하여 처리됨 / 자동완성 기능
나는 useQuery와 useMutation invalidateQueries (tanstackQuery)를 활용해 데이터를 불러왔는데 곡을 검색하고 추가버튼을 눌렀을 때 실시간으로 반영되게 하는 과정에서 내가 다른 tsx파일에서 선언한 queryKey를 이용해 invalidateQueries 실시간으로 처리되게 할 수 있다는 것을 알았다. 생각해보면 전체를 아우르고 있는데 잘 쓰지 않아 잊고 있었다.
// 곡 추가 뮤테이션
const addTrackMutation = useMutation({
mutationFn: (uri: string) => addPlaylist(playlistId, uri),
onSuccess: () => {
queryClient.invalidateQueries(["getPlaylistTracks", playlistId]);
setSong("");
}
});