좋아요는 사용자와 상호작용하는 부분이기 때문에 use client
로 클라이언트 컴포넌트로 구성하였으며 연속 이벤트로 과부하가 발생할 가능성이 있기 때문에 lodash라이브러리를 사용하여 thorottling을 적용했다.
table에 해당 영화가 존재하는지, 로그인된 유저가 좋아요를 했는지에 따라 다른 로직이 적용된다.
const { data: currentMovieLikeData } = useQuery<any>({
queryKey: ['movieLikes', props.movieid],
queryFn: async () => {
const response = await supabase.from('movielikes').select('*').eq('movieid', props.movieid);
return response.data;
},
enabled: Boolean(props.movieid) // movieid가 있을 때만 요청
});
const { userInfo } = useUserInfoStore();
const mutation = useMutation(
async () => {
const { data: likesTable } = await supabase.from('movielikes').select('*').eq('movieid', props.movieid);
if (likesTable?.length) {
if (likesTable[0]?.user_id.includes(userInfo.id)) {
const { data: users } = await supabase.from('movielikes').select('user_id').eq('movieid', props.movieid);
const newUsers = users![0].user_id.filter((id: string) => id !== userInfo.id);
await supabase.from('movielikes').update({ user_id: newUsers }).eq('movieid', props.movieid);
} else {
const { data: users } = await supabase.from('movielikes').select('user_id').eq('movieid', props.movieid);
const newUsers = [...users![0].user_id, userInfo.id];
await supabase.from('movielikes').update({ user_id: newUsers }).eq('movieid', props.movieid);
}
} else {
const newUsers = { movieid: props.movieid, user_id: [userInfo.id] };
await supabase.from('movielikes').insert(newUsers);
}
},
{
// 성공 시에 캐시 업데이트
onSuccess: () => {
queryClient.invalidateQueries(['movieLikes', props.movieid]);
}
}
);
debounce vs thorottling
debounce는 이벤트가 끝날 때까지 기다렸다가 시작하고 throttling은 이벤트가 시작되면 일정 주기로 계속 실행한다.
성능 개선면에서는 debounce가 좋지만, 좋아요같이 즉시 결과를 요하는 기능에서는 throttling을 사용하는 것이 좋다.
thorottling 적용
mutate대신 mutateAsync를 적용하여 비동기 작업이 완료되어야 다음 버튼을 받을 수 있도록 만들었다.
const likeButtonHandler = throttle(
async () => {
if (userInfo.id) {
await mutation.mutateAsync();
} else {
alert('로그인 해주세요!');
}
},
1000, // 스로틀링 간격 (여기서는 1000ms)
{ trailing: false } // 마지막 호출 후 추가 호출 방지
);
다른 경로의 root layout을 만들고 싶을때에는 route에 영향을 받지않도록 폴더명을 (shop)
으로 만들고 그 아래에 원하는 경로로 만들면된다.
/new
경로로 만들 수 있고 new 경로의 root layout은 (shop)
의 layout.tsx
가 된다. mutate
는 비동기 작업이 완료되면 onSuccess or onError 콜백함수를 return하는데 mutateAsync
는 Promise를 반환하여 해당 비동기 작업이 끝날때까지 대기하고 결과를 받을 수 있다.