til

최종 프로젝트 9일 차 @메인 페이지 정렬/필터, 페이지네이션, 검색 + 모달 창 조정

fpzmfks 2024. 10. 30. 23:22

오늘은 어제 붙잡고 있던 것들을 마무리 했고, 내일 남은 헤더 기능만 구현하면 나의 기능 구현은 끝이 난다. 페이지 2개를 구현하려니 너무 힘이 드는데, 그래도 그 어느 때보다도 배우는 게 많았다는 점이 위안이 된다. 

 

먼저 메인 페이지 기능들을 짚어보자면

정렬 / 필터

정렬은 supabase order를 통해, 필터는 supabase textSearch를 통해 구현했다. 

 

이 중 정렬의 인기순 기능은 어제 붙잡고 한참을 고민하던 것이었다.

 

문제의 원인은 인기순 정렬을 위한 count값이 배열로 들어오는 것을 곧장 order로 정렬할 수 없었기 때문에 sql editer를 사용해야 했다는 것이다. 하지만 sql에서 사용되는 언어는 내가 모르는 영역이기 때문에 한정된 프로젝트 시간 내에 할 수 있을 지 장담할 수 없었다. 

 

다행히 오늘 아침 회의에서 기존의 인기순 정렬 기준이 합리적이지 못하다는 의견이 나와서 정렬 기준을 변경하여 select count를 쓰지 않게 되었고, 자연히 sql도 쓰지 않게 되었다. 대신 모집 하기 시에 시청할 영상의 인기도(popularity) 정보를 넣어 이를 기준으로 인기도 정렬을 쉽게 할 수 있었다. 

 

페이지네이션

그리고 supabase 메서드를 이용하여 정렬과 필터를 구현한 이 모든 과정이 바로 페이지네이션을 위한 것이었다.

 

올바른 페이지네이션을 구현하기 위해서는 요청을 할 때부터 데이터의 길이, 정렬, 필터 등의 조건들을 명시해서 이러한 조건에 맞는 데이터들을 각 페이지에 넣어야 한다. 그렇지 않으면 각 페이지마다 들어가는 정보의 순서나 갯수가 이상해지기 때문이다.

 

다른 방법으로는 모든 데이터를 불러왔다가 직접 정렬과 필터를 먹여주는 방법이 있는데, 이러한 방법은 비동기 통신을 최적화하고자 하는 페이지네이션을 구현하는 의미가 없기 때문에 지양해야 한다. 

 

앞선 정렬/필터 기능을 페이지네이션 용으로 잘 구성해두었기 때문에, 곧장 페이지네이션에 성공했다. 

 

검색

supabase의 textSearch 기능은 단순 필터뿐만 아니라 검색 기능도 가지고 있다. 다만 주의할 점은 띄어쓰기 단위로 검색하기 때문에 띄어쓰기가 아닌 검색어로는 검색이 되지 않는다는 것이다.

 

textSearch가 작동하는 모습을 보기 전에는 디바운싱 같은 것을 적용할 생각을 했었는데 이러한 띄어쓰기 단위의 검색에서 디바운싱을 적용하는 것은 적절하지 않을 것 같아 아쉽다.

 

전에 비슷한 supabase 메서드를 찾아보기로는 ilke라는 비슷한 함수로도 검색 기능이 가능하지 않을까 하는데, 이전에 그 함수를 쓰려다가 크게 실패했기 때문에 당장에는 적용하지 않아도 될 것 같다. 

 

모달 창 조정

어제 모달 창 띄우기 트리거에 대해 여러가지 고려해보고 오늘 모달에서 선언한 open 상태값을 트리거 버튼에 props로 내려주는 방법을 튜터님께 물어보았는데, 애초에 내가 생각한 방법은 불가능하다고 한다. 

 

대신에 모달을 사용하는 컴포넌트에서부터 모달 컴포넌트에 props를 내려줘서 컨트롤 하는 것이 더 좋겠다는 조언을 들었다. 

 

내가 기존에 생각한 방법은 모달의 상태값을 트리거에 넘겨주는 것이었는데

 

사실은 컴포넌트의 상태값을 모달에 넘겨줘도 트리거로 사용할 수 있는 것이었다. 

 

그렇게 생각의 전환을 이루고 완성한 것이 아래의 코드이다. 핵심은

 

open={open || openControl}

 

로 좀 더 세세하게 컨트롤 하고 싶은 곳에서는 상태값을 선언해서 openControl에 props를 내려주고

세세한 컨트롤이 필요하지 않은 곳에서는 openControl={false} 를 props로 지정하면 된다. 

 

내가 받아오는 값이든 주는 값이든 하나만 true 값이면 모달이 열리도록 한 것이다. 

 

다만 이 경우 openControl로 세세한 컨트롤을 하는 경우에서는 쉽게 모달 창을 닫을 수 없는데, 이번에 이는 의도적인 기능으로 모집하기 시 반드시 참가하기 과정을 거쳐야하기 때문에 이러한 구조를 선택했다. 

const ParticipationButton = ({openControl,children,party_id}: {
  openControl: boolean;children: React.ReactNode;party_id: string;}) => {
  
  const [open, setOpen] = useState(false);
  console.log('파티아이디', party_id);
  
  return (
    <>
      <Dialog open={open || openControl} onOpenChange={setOpen}>
        <DialogTrigger asChild>{children}</DialogTrigger>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>참가하기</DialogTitle>
          </DialogHeader>
          <ParticipationForm party_id={party_id} closeHandler={setOpen} />
          <DialogDescription></DialogDescription>
        </DialogContent>
      </Dialog>
    </>
  );
};