ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [개발일지03/🏔️오름마켓 React] 정렬 버튼 active 상태 유지하기 - useLocation, useSearchParams
    프로젝트 기록/오름마켓 2024. 3. 29. 22:29
    ✅ 오늘의 작업 내역
         1. 정렬 메뉴 버튼 active 상태 변경(100%)
         👾 (이슈 처리) 새로고침 후에 바로 로고를 클릭하여 "/" 경로로 이동하는 경우 변경된 active 상태가 반영되지 않는 이슈
         👾 (이슈 처리) 모바일 뷰에서 sortPath가 없는 경우 셀렉트 박스 label값이 null이 되는 이슈

     

    💡 구현 결과

    (왼) 데스크탑 뷰 / (오) 모바일 뷰

    1. 정렬 메뉴 버튼 active 상태 변경

    기존 정렬 메뉴 버튼의 active 상태는 NavigationBar 컴포넌트 내의 상태값을 통해 관리되고 있었다. 이를 쿼리 스트링 값에 따라 actvie 값이 적용될 수 있도록 변경이 필요했다. 처음에는 active 상태를 관리할 상태값을 useSortFilter 커스텀 훅에 넣어야 하나 고민했다가 해당 커스텀 훅이 처리하는 기능의 경계가 모호해지는 느낌이라 원래 active 상태를 관리해 줬던 NavigationBar 컴포넌트 내에서 useSearchParams 훅으로 처리해 주기로 결정했다.

     

    NavigationBar.tsx

    메뉴에 대한 페이지였다면 useLocation의 pathname으로 처리했겠지만 쿼리 스트링의 경우 search로 ?부터 시작되는 파라미터 값을 받아와야한다. 받아온 값을 split를 활용하여 원하는 key, value값을 받아와 처리해주는 방법을 사용해볼까 하다가 filter 쿼리와 sort 쿼리의 위치가 정해져있는게 아니여서 useSearchParams의 메서드를 활용하여 value값만 받아오는 것이 좋을 것 같다는 생각이 들었다.

    const locationPath = useLocation().search;
    const [searchParams, setSearchParams] = useSearchParams(locationPath);
    
    const sortPath = searchParams.get('sort');

     

    그리고 받아온 sort key의 value값을 담은 sortPath을 사용하여 현재 클릭된 버튼의 active 상태를 처리해 줄 isSortSelected 함수를 생성해주자. isSortSelected 함수는 active 상태를 표현해주는 css 속성에 bool값을 리턴해주는 함수이다.

    const isSortSelected = (sortOrder: string) => {
      if (!sortPath && sortOrder === 'latest') {
        return true;
      }
      return sortPath === sortOrder;
    };

     

     

    정렬 버튼을 클릭하면 상수로 지정해놨던 SORT_OPTIONS 배열 안에 들어있던 latest, oldest, maxPrice, minPrice가 전부 매개변수로 들어오게 된다. 이때 현재 페이지의 쿼리 스트링 value값과 동일한 값만 true값을 반환해주고, 나머지는 false를 반환해준다. 만약 쿼리 스트링에 정렬에 대한 key값이 없다면 기본값인 최신순이 active 되도록 true값을 반환하는 조건도 추가해주었다.

     

    <Box sx={{ marginLeft: 'auto' }}>
        {SORT_OPTIONS.map((option) => (
          <Button
            key={option.value}
            value={option.value}
            color="inherit"
            onClick={() => onSortChange('sort', option.value)}
            sx={{
              fontWeight: isSortSelected(option.value) ? '700' : '300',
            }}
          >
            {option.label}
      </Button>
    ))}

     

    👾 오늘의 트러블슈팅

    1) 새로고침 후에 바로 로고를 클릭하여 "/" 경로로 이동하는 경우 변경된 active 상태가 반영되지 않는 이슈

    문제 상황

    쿼리 스트링이 없을 경우 최신순이 활성화 되야하는데 높은가격순이 활성화되어 있는 상황

     

    searchParams로 현재 페이지의 쿼리 스트링 값을 바로 받아와서 active 상태를 반영해주기 때문에 새로고침을 해도 상태가 유지된다. 하지만 새로고침 후 바로 ORUM 로고를 클릭하여 "/" 경로로 이동하는 경우 active 상태가 새로고침 이전으로 지정되어 있는 것을 발견했다. searchParams 값을 확인해봤을 때도 새로고침하기 이전 값이 저장되어 있는 것을 확인할 수 있었다.

     

     

    문제 원인

    여러 가능성을 가지고 console.log를 찍어보며 원인을 확인해보았다. 처음에는 새로고침이 되면 useSearchParams의 값이 초기화되서 그런가?라는 생각을 했다가 그렇게되면 location.search의 현재 값이 들어가서 최신순으로 표기가 되야했다. 혹시나해서 sortPath값과 locationPath값을 찍어보니 locationPath값은 제대로 반영이 되는 것을 확인했다.

     

    그러다 문득 Link 태그의 특징이 떠올랐다. Link 태그는 브라우저의 주소만 바꿀 뿐 페이지 자체를 새로고침 하지는 않는 다는 사실! ORUM 로고 또한 Link 태그로 주소를 변경하고 있었고, 새로고침이 되면서 location.search와 useSearchParams 모두 변경된 값을 다시 받아온다. 하지만 useSearchParams는 초기값으로 location.search의 값을 받아올 뿐, searchParams의 값을 변경해주지 않는 이상 계속 기존 값을 유지하고 있는 상태였다.

     

    useLocation의 경우 URL의 변경 사항을 감지해서 Link를 사용하여 URL을 변경한다면, 해당 사항을 확인하여 location.search값을 다시 업데이트 해주지만 useSearchParams는 페이지가 새로고침이 된 상태가 아니기 때문에 변경 사항을 확인하지 못하고 기존 값을 그대로 유지하고 있는 것이 문제의 원인이었다!

     

    해결 방법

    useSearchParams의 값을 업데이트 해주려면 수동으로 변경사항을 알려줘야한다. 현재 location.search의 값을 초기값으로 받고있기 때문에 useEffect의 의존성 배열에 location.search 값을 걸어주고, sortPath값이 존재한다면 해당 값이 searchParams에 반영되도록 처리해주면 해결!

     useEffect(() => {
        if (sortPath) {
          setSearchParams(sortPath);
        }
      }, [locationPath]);

    결과 화면

     

    2) 모바일 뷰에서 sortPath가 없는 경우 셀렉트 박스 label값이 null이 되는 이슈

    문제 상황

    마지막으로 정렬 부분 테스트를 해보다가 console에 경고 메세지가 떠서 알게 된 이슈였다. 모바일 뷰에서 URL에 쿼리 스트링값이 없는 경우 null값이 넘어오면서 셀렉트 박스 label의 이름이 null로 렌더링 되고 있었다.

     

    문제 원인

    셀렉트 박스의 value 속성에 연결된 값은 searchParams에서 쿼리 스트링에 sort key값이 있으면 가져오는 value값인 sortPath였는데, 해당하는 쿼리 스트링의 값이 없는 경우 null로 값이 넘어오게 되는 것이 문제였다.

     

    해결 방법

    조건식으로 sortPath값이 없다면 초기값인 latest를 default로 설정해 주는 것으로 해결!

     <Select
        labelId="filter-select-label"
        id="filter-select"
        value={!sortPath ? 'latest' : sortPath}
        sx={{ fontSize: '0.9rem', borderRadius: 0 }}
        onChange={(event) =>
          onSortChange('sort', event.target.value as string)
        }
        >
        {SORT_OPTIONS.map((option) => (
          <MenuItem key={option.value} value={option.value}>
            {option.label}
          </MenuItem>
        ))}
    </Select>

    결과 화면

     


     

    💬 본 포스팅은 공부 기록용으로 정확하지 않은 정보가 존재할 수 있습니다. 발견하신다면 알려주세요!

     

Designed by Tistory.