티스토리 뷰

사용자 페이지에 접속 시 사용자가 작성한 Comments와 좋아요를 누른 Liked Place를 표시한다.

각각의 데이터는 모두 페이지네이션을 사용해 표시했다.

 


Comments 페이지네이션

comments 데이터를 가져올 때 offset(from)에서 시작해 offset(from+to)에서 끝나는 방식으로 쿼리 결과를 제한한다.

from및 to 값은 0부터 시작하며 이 범위 내의 레코드만 반환한다.

이때 쿼리 순서를 존중하며 order 절이 없으면 범위가 예상치 못하게 동작할 수 있다.

export const getCommentsById = async(user_id:string,page:number=1, pageSize:number = 10)=>{
    const supabase =await  createServerSideClient();
    const result = await supabase.from('comments')
    .select('*',{count: 'exact'}) // count를 추가로 전달->정확한 행 수를 리턴
    .is('deleted_at',null)
    .eq('user_id',user_id)
    .order('created_at',{
        ascending:false // 내림차순 정렬
    })
    .range((page-1)*pageSize, page*pageSize-1); // 10개씩 가져오기

    return {data:result.data , count:result.count};
};

 

comments controller 훅에서 api를 요청할 때 page가 변화될 때마다 요청을 보낼 수 있도록 구현했다.

export const useCommentsById = (user_id:string, pageSize:number=10) =>{
    const [loading, setLoading] = useState(false);
    const [comments, setComments] = useState<TypeComments[]>([]);
    const [total, setTotal] = useState(0);
    const [page, setPage] = useState(1);

    const onGetCommentsById =useCallback(
        async(page:number) =>{
            setLoading(true);
            try{
                const result= await getCommentsById(user_id,page,pageSize);
                if(result?.data){
                    setComments(result.data);
                    setTotal(result.count || 0);
                }
            } catch(error){
                console.log(error);
            } finally{
                setLoading(false);
            };
        },[user_id, pageSize]) 

    useEffect(()=>{
        onGetCommentsById(page);
    },[user_id, page, onGetCommentsById]);

   
    return {loading, comments, total, page, pageSize, setPage}
}

 

 

+ 페이지 버튼 표시하기

comments 데이터를 불러와 데이터가 존재하는 페이지 수 만큼의 버튼을 표시하고 버튼을 클릭하면 해당 범위에 따른 데이터를 가져오도록 구현했다.

useCommentsById를 호출해 받아온 total과 pageSize를 사용해 총 페이지 수를 구한다.

const totalPages = Math.ceil(total / pageSize);

 

총 페이지 수 만큼 요소가 들어있는 배열을 만든다. (ex. totalPages =2 -> [1,2])

Array.from 메소드를 사용해 구현했다.

첫 번째 인자로 배열로 변환할 순회 가능 또는 유사 배열 객체를 전달하고 두 번째 인자로 맵핑 함수를 전달한다.

{Array.from({ length: totalPages }, (_, idx) => (
  <button key={idx} onClick={() => setPage(idx + 1)}>
    {idx + 1}
  </button>
))}

버튼  클릭 이벤트로 page를 변화시켜 api 요청이 이루어지도록 구현했다.

 

 


Liked Place 페이지네이션

liked-place의 경우 comments와 동일한 코드로 작성할 시 제대로 작동하지 않는다.

rang 메소드는 테이블의 각 행을 기준으로 범위를 설정하기 때문이다!

liked-place 데이터는 liked_place 필드 내에 존재하기 때문에 요소의 수에는 영향을 줄 수 없다.

 

⇨ liked-place 필드 데이터를 그대로 가져와 커스텀 훅에서 페이지네이션을 진행했다.

export const useLikedPlacePagination = (userId:string, pageSize:number=6) =>{
    const [loading, setLoading] = useState(false);
    const [likedPlace, setLikedPlace] = useState<TypePlaceLike[]>([]);
    const [total, setTotal] = useState(0);
    const [page, setPage] = useState(1);

   const onGetLikedPlacePagination =useCallback(
    async(page:number) => {
    setLoading(true);
    try {
        const result = await getLikedPlacePagination(userId);
        if(result.data){
            const allLikedPlaces =result.data[0].liked_place as TypePlaceLike[];
            setTotal(allLikedPlaces.length);

            const paginatedPlaces = allLikedPlaces.slice((page - 1) * pageSize, page * pageSize);
            setLikedPlace(paginatedPlaces);
        }
    } catch (error) {
       console.log(error) 
    } finally {
        setLoading(false);
    }
   },[userId, pageSize]) 
   
   useEffect(()=>{
    onGetLikedPlacePagination(page);
   },[userId, page, onGetLikedPlacePagination]);

  return {loading, likedPlace, total, page, pageSize, setPage}
}

전체 데이터를 가져와 slice 메소드를 사용해 데이터를 잘라 요청한 페이지의 수 만큼만 전달한다.

 

 

 


참고 사이트: https://supabase.com/docs/reference/javascript/range

728x90