til

최종 프로젝트 16일 차 @메인 페이지 리팩토링

fpzmfks 2024. 11. 8. 22:22

오늘은 ut(유저 테스트) 관련 회의를 하고 먼저 메인 페이지 리팩토링을 진행했다. 

 

기능적으로 문제는 없지만 메인페이지에는 코드적으로 결함이 있었기 때문인데, 첫째가 가장 하단에 있는 엄청 길고 쓸데없이 복잡한 데이터 요청 코드이고, 둘째가 useEffect를 2개 사용해서 불필요한 렌더링을 유발한다는 것이다. 

 

첫째는 내가 최근에 작성한 코드이니만큼 그냥 대충 분리하면 딱히 문제될 것도 없어보이지만, 문제는 두번째이다.

 

내가 useEffect를 두 번 쓴 이유는 페이지네이션 이슈 때문인데, 데이터 필터링으로 인해 페이지 수가 줄어들 경우 현재 머물고 있는 페이지가 없는 페이지가 될 경우가 발생할 수 있다. 때문에 필터 값 변경이 발생할 때마다 setPageNumber(1)을 통해 페이지를 1로 리셋하는 방법을 취했는데 이를 위해서 useEffect를 사용했더니 아래와 같이 불필요한 리렌더링이 일어나는 코드가 되어버렸다. setPageNumber(1)을 useEffect가 아니라 각 필터링 트리거 onClick에 넣으면 해결될 문제이긴 하지만 이 방법에서는 또 쿼리 스트링으로 관리하는 필터링은 어떻게 할지가 걸린다. 

  // 필터 누르면 데이터가 바로 바뀌도록
  useEffect(() => {
    queryClient.invalidateQueries({ queryKey: ['recruitList'] });
    queryClient.invalidateQueries({ queryKey: ['recruitListPages'] });
  }, [order, filter, pageNumber, searchWord, partySituation, queryClient]);

  // 페이지 리셋
  // 추후 리펙토링 필요
  // 페이지를 query로 관리하는 등의
  useEffect(() => {
    setPageNumber(1);
  }, [filter, searchWord, partySituation, queryClient]);

 

그래서 나는 모든 것의 해결책으로써 필터링을 모두 쿼리스트링으로 관리한다는 방법을 쓰기로 했다. 헤더가 아닌 메인 페이지에 있는 필터 트리거를 button이 아니라 Link 태그로 변경시켜 버리는 것이다. 

 

하지만 이 방법에도 또 문제가 있었으니, Link 태그가 페이지를 이동시키는 것이기 때문에 필터를 누르면 스크롤이 상단으로 올라가 버린다는 것이었다. useRef를 사용해서 내가 원하는 영역으로 스크롤을 끌어내려주는 것은 가능했지만, 한 번 스크롤이 상단으로 올라가는 것까지는 막지 못했다. 

 

이를 튜터님께 상담해보니 현재 구현한 필터링 상태값들을 모두 쿼리 스트링으로 변환하는 것은 적절하지 않은 것 같고 페이지네이션 이슈를 해결하고 싶은 거라면 컴포넌트들을 나누고 페이지네이션 상태값 또한 같이 나누는 것이 좋겠다고 하셨다. 

 

일단 이렇게 컴포넌트들을 나누어보았는데... 이렇게 글을 쓰며 정리하면서 생각해보니 이렇게 재사용성이 떨어지는 컴포넌트들을 늘리는 것은 별로 좋은 생각이 아닌 것 같고 특정 props 조합을 내려주면 그에 따라 데이터를 요청하고 받아와서 그려주는 재사용성이 있는 컴포넌트를 만드는 것이 좋을 것이라는 생각이 든다. 아마 이렇게 나누는 것이 썩 좋은 생각이 아닌 것 같고, 튜터님의 의도도 이런 구조가 아니었던 것 같다. 

일단 컴포넌트를 3개 정도 만들어 보았는데 아래 두 사진을 보면 알겠지만 구조 상 차이가 없다. 데이터만 좀 다르게 받아올 뿐이지 애초에 같은 컴포넌트였으니 그럴만도 했다. 주말 동안에는 이 컴포넌트를 재사용성이 높게 만드는 연습을 해야할 것이다. 

const response: PostgrestSingleResponse<partyInfo[]> =
    // 검색을 안하는 경우
    wordConversion === '+'
      ? partySituation === 'recruiting'
        ? await browserClient
            .from('party_info')
            .select('*')
            .range(start, end)
            .order(order, { ascending: false })
            .gte('start_date_time', now) // 현재시간보다 이후에 시작하는  아직 모집중인 데이터
            .eq('situation', '모집중') // 모집 마감이 아닌 경우
            .textSearch('video_platform', bull)
        : partySituation === 'current'
        ? await browserClient
            .from('party_info')
            .select('*')
            .range(start, end)
            .order(order, { ascending: false })
            .lte('start_date_time', now) // 시작 시간이 지났고
            .gte('end_time', now) // 시청 종료 시간이 지나지 않은 경우
            .textSearch('video_platform', bull)
        : await browserClient
            .from('party_info')
            .select('*')
            .range(start, end)
            .order(order, { ascending: false })
            .gte('end_time', now) // 종료 시간이 지나지 않은 경우만
            .textSearch('video_platform', bull)
      : //검색을 하는 경우
      partySituation === 'recruiting'
      ? await browserClient
          .from('party_info')
          .select('*')
          .range(start, end)
          .order(order, { ascending: false })
          .gte('start_date_time', now)
          .eq('situation', '모집중')
          .textSearch('video_platform', bull)
          .textSearch('video_name', wordConversion) // 검색어
      : partySituation === 'current'
      ? await browserClient
          .from('party_info')
          .select('*')
          .range(start, end)
          .order(order, { ascending: false })
          .lte('start_date_time', now)
          .gte('end_time', now)
          .textSearch('video_platform', bull)
          .textSearch('video_name', wordConversion) // 검색어
      : await browserClient
          .from('party_info')
          .select('*')
          .range(start, end)
          .order(order, { ascending: false })
          .gte('end_time', now) // 종료 시간이 지나지 않은 경우만
          .textSearch('video_platform', bull)
          .textSearch('video_name', wordConversion); // 검색어

  if (response.error) {
    return [];
  }

  return response.data;