til

최종 프로젝트 10일 차 @헤더 필터(시청중/모집중), zustand

fpzmfks 2024. 10. 31. 23:20

오늘은 시청중/모집중 필터 구현과 필터와 검색 기능을 헤더로 옮기는 작업을 하였다. 

 

페이지네이션과 데이터 요청

필터나 정렬, 검색, 페이지네이션 같은 기능들은 하나하나 놓고 본다면 별 것 아니지만, 이번 프로젝트와 같이 데이터베이스 구조가 복잡할 때에는 이런 기능들을 합치는 것이 가장 큰 이슈 중 하나인 것 같다. 

 

이전의 정렬, 필터, 검색 기능들도 supabase 메서드에 대해 조사하고 잘 활용해야만 페이지네이션과 결합할 수 있었는데 

데이터 요청/응답 이슈로 인해 이번에 기어코 시청중/모집중 필터 기능을 구현하는 데에 실패했다. 정확히는 필터 기능 구현 자체는 쉽게 성공했으나 이를 페이지네이션과 결합하는 것이 불가능했다. 이 글의 가장 하단에 노력했지만 실패한 코드가 남아있다. 

 

문제의 핵심은 '시청중' 필터에 있다. '모집중' 필터도 현재로써는 완벽하게 구현되지는 않지만, 시청중 필터는 그것보다 더 까다로운데, 시작시간뿐만 아니라 종료시간까지 고려해야 하기 때문이다. 

 

기존의 데이터테이블은 

 

시청날짜, 시작시간, 영상시간

 

의 구조로 이루어져 있었다. 이러한 데이터가 있으면 다른 기능들을 구현하는 데에 크게 어려움은 없다. 종료시간 같은 것이야 함수를 만들어서 계산하면 되기 때문이다. 

 

하지만 데이터를 요청하면서 동시에 종료시간을 고려해야하는 현재 상황에서는 데이터를 요청하면서 동시에 그 안의 값들을 계산해서 받아와야 하는 상황인 것이다. 이러한 기능을 supabase에서 자체적으로 제공하지 않는 이상에야 불가능한 방법이다. 혹시나 해서 supabase 기능들을 차근히 살펴보았으나 어디에도 이런 것을 계산해주는 메서드는 없었다. 단, 아마 SQL Editor를 사용하면 이러한 계산이 가능할 것도 같은데, 가뜩이나 복잡한 날짜/시간 관련 데이터를 한 번도 제대로 사용해본 적이 없는 sql로 다룰 수 있을 것 같지는 않다.

 

그래서 결국 해결책은 데이터베이스에 애초부터 시작시간과 종료시간을 넣어주는 것이다. 그러면 supabase 메서드인 gte같은 것으로 쉽게 필터를 걸어줄 수 있기 때문에 시청중, 모집중과 같은 데이터를 요청할 수 있고, 데이터를 요청하면서 동시에 그 안의 값들을 계산해달라고 할 필요도 없다

 

그래서 데이터베이스에 담긴 시간 관련 테이블이 아래와 같아졌다.

 

시청날짜, 시작시간, 영상시간   +   시작시간, 종료시간

 

기존에 있던 값들과 겹치는 부분이 있지만, 프로젝트 마무리가 얼마 남지 않았기 때문에 일단 데이터를 추가만 하는 방향성으로 정했다. 이렇게 겹치는 부분은 향후 리팩토링을 해나갈 수 있으면 좋을 것이다. 

 

 

zustand

그리고 또 오늘 한 일이 zustand를 사용하여 전역상태관리를 해서 헤더의 input 값이 메인페이지로 전달되도록 하는 것이었다. 이번에 검색 기능을 위해서 zustand를 좀 복잡한 방법으로 사용했는데, 다른 페이지로 이동하면 input창의 값이 초기화되고 검색할 수 없게 되는 등의 기능이 들어갈 것이라고 예상했기 때문이었다. 내가 생각하기로는 아마 middleware 처리 같은 것을 해야하지 않을까 싶다. 

 

 

 

막간으로 오늘 내가 작업한 코드들이나 첫번째 코드는 다른 분이 만든 코드를 수정해서 만든 시청여부 판별 함수이고, 두번째는 시청중/모집중 필터를 구현하기 위한 노력이 담긴 코드이다. 

// 현재시간에 따라 파티상태를 판별하는 함수
export const getViewStatus = (party: Party) => {
  const currentDate = new Date();
  const watchDate = new Date(party.watch_date + 'T' + party.start_time.split('.')[0]);
  const endDate = new Date(party.watch_date + 'T' + party.start_time.split('.')[0]);
  endDate.setMinutes(endDate.getMinutes() + party.duration_time);

  
  if (currentDate < watchDate) {
    return '모집중';
  } else if (currentDate > watchDate && currentDate < endDate) {
    return '시청중';
  } else if (currentDate > endDate) {
    return '시청완료';
  }
};

 

아래는 내가 오늘 어떻게든 시청중/모집중 필터를 구현하기 위해 노력한 흔적이다. 하지만 아래의 함수는 페이지네이션과 시청중/모집중 필터가 결합되지 않아 데이터가 이상하게 읽히는 문제가 있다. 사실 이런 로직을 작성하기 이전부터 그러한 문제를 인지하고 있었으나, 날짜를 조정하는 데에는 아직 잘 모르는 부분이 많아서 일단 시도해보았다. 

// 데이터 불러오기
  const { data, isLoading } = useQuery({
    queryKey: ['recruitList'],
    queryFn: async () => {
      const response: PostgrestSingleResponse<partyInfo[]> =
        // 검색을 안하는 경우
        wordConversion === '+'
          ? // 모집중을 택할 때 watch_date가 오늘 이상인 데이터 불러오기
            partySituation === '모집중'
            ? await browserClient
                .from('party_info')
                .select('*')
                .range(start, end)
                .order('watch_date', { ascending: false })
                .order('start_time', { ascending: false })
                .order(order, { ascending: false })
                .gte('watch_date', nowDate)
                .textSearch('video_platform', bull)
            : partySituation === '시청중'
            ? await browserClient
                .from('party_info')
                .select('*')
                .range(start, end)
                .order('watch_date', { ascending: false })
                .order('start_time', { ascending: false })
                .order(order, { ascending: false })
                .eq('watch_date', nowDate)
                .textSearch('video_platform', bull)
            : await browserClient
                .from('party_info')
                .select('*')
                .range(start, end)
                .order('watch_date', { ascending: false })
                .order('start_time', { ascending: false })
                .order(order, { ascending: false })
                .textSearch('video_platform', bull)
          : //검색을 하는 경우
          partySituation === '모집중'
          ? await browserClient
              .from('party_info')
              .select('*')
              .range(start, end)
              .order('watch_date', { ascending: false })
              .order('start_time', { ascending: false })
              .order(order, { ascending: false })
              .textSearch('video_platform', bull)
              .gte('watch_date', nowDate)
              .textSearch('video_name', wordConversion)
          : partySituation === '시청중'
          ? await browserClient
              .from('party_info')
              .select('*')
              .range(start, end)
              .order('watch_date', { ascending: false })
              .order('start_time', { ascending: false })
              .order(order, { ascending: false })
              .eq('watch_date', nowDate)
              .textSearch('video_platform', bull)
              .textSearch('video_name', wordConversion)
          : await browserClient
              .from('party_info')
              .select('*')
              .range(start, end)
              .order('watch_date', { ascending: false })
              .order('start_time', { ascending: false })
              .order(order, { ascending: false })
              .textSearch('video_platform', bull)
              .textSearch('video_name', wordConversion);

      if (response.error) {
        console.log(response.error.message);
      }
      // 시청중, 모집중
      if (response.data && response.data.length > 0 && partySituation === '시청중') {
        // 시청중인 경우
        const nowWatchingParties = response.data.filter((n) => {
          return getViewStatus(n) === '시청중';
        });
        return nowWatchingParties;
      } else if (response.data && response.data.length > 0 && partySituation === '모집중') {
        // 모집 중인 경우
        const nowWatchingParties = response.data.filter((n) => {
          return getViewStatus(n) === '모집중' && n.situation === '모집중';
        });
        return nowWatchingParties;
      }
      return response.data;
    }
  });