react useState와 정렬 함수를 이용한 가격별 정렬 기능 구현

Suxxzzy.log·2021년 12월 25일
0

이번에는 실제 쇼핑몰처럼 가격별 정렬 기능을 구현해보았다.
낮은 가격순, 높은 가격순, 그리고 필터링 해제를 했을 때, 다르게 렌더링되도록 구현하였다. 상태관리와 정렬 알고리즘이 둘 다 필요했기 때문에 단계별로 나누어서 구현을 하였다.

1단계 : 사용자가 변화시킬 수 있는 대상 파악하기

사용자는 옵션 태그에서 여러 가지 필터링 조건들을 고를 수 있다. 이에 따라 화면에 출력되는 아이템들의 순서가 달라지기 때문에, 아이템들의 순서를 상태로 관리하여야 한다고 판단했다.
예를 들어 사용자가 높은가격순 옵션을 선택한 경우 화면에는 아이템들이 높은 가격부터 낮은 가격 순으로 노출되어야 할 것이다. 그렇지 않은 경우 다른 순서로 정렬이 일어나야 한다.

2단계: 이벤트 핸들러 작성

옵션 선택은 select 태그와 option태그를 이용하였고,
이에 따라 value속성값이 달라진다는 것을 이용하였다.
select 태그에서 onChange이벤트가 일어나므로, 이벤트 핸들러를 할당해준다.

 <select  onChange={handleChange}>
        <option value="noPriceFilter">--정렬 기준 선택--</option>
        <option value="sortByLowPrice">낮은가격순</option>
        <option value="sortByHighPrice">높은가격순</option>
 </select>

3단계: useState 기본 템플릿 작성

필터링 상태를 나타내는 변수 priceFilter와 이를 갱신할 수 있는 함수 setPriceFilter를 작성하였다.
onChange이벤트가 발생함에 따라 priceFilter가 바뀌어야 하므로, setPriceFilter를 이용하여 상태를 갱신하도록 하였다.
이벤트의 타겟은 select element이므로 setPriceFilter에는 인자로 event.target.value를 전달해주었다.

const [priceFilter,setPriceFilter] = useState("noPriceFilter")
  function handleChange(event){
    setPriceFilter(event.target.value);
  }

4단계: 렌더링.

삼항연산자를 이용하여 priceFilter가 낮은가격순이면 아이템을 낮은가격순으로 정렬하여 화면에 렌더링하고, 높은가격순이면 높은가격순으로 전렬하여 화면에 렌더링되도록 하였다. 필터링을 적용하지 않을 경우, 정렬되지 않은 아이템 배열을 렌더링하도록 하였다.

필터링 조건에 맞지 않을 경우 null로 처리하였다. 이는 화면에 아무것도 보여주지 않겠다는 건데, 실제로 웹사이트에서는 아무것도 안보이는 경우는 없다. 왜냐하면 사용자가 선택한 옵션과 삼항연산자 판별식에 작성된 값이 무조건 일치할 수 밖에 없기 때문이다.

<div className="itemlist">
      {priceFilter === "noPriceFilter" ? dummyItems.map(items => <Item items={items} key={items.id}/>):null}
      {priceFilter === "sortByLowPrice" ? sortedByLowPrice.map(items => <Item items={items} key={items.id}/>):null}
      {priceFilter === "sortByHighPrice" ? sortedByHighPrice.map(items => <Item items={items} key={items.id}/>):null}
</div>

렌더링 전 정렬하기.

정렬부터 먼저 하고 map을 이용하여 아이템들을 렌더링해주었다.
아이템 데이터들은 여러 개의 객체로 이루어진 배열이므로,(아래 데이터 참조)
객체의 키 값 중에서 itemPrice를 기준으로 정렬이 필요하였다.
이때 가격은 숫자와 콤마가 섞인 문자열이었기 때문에, 콤마를 제거하고, Number메서드를 사용하여 숫자로의 형 변환이 필요했다.

const dummyItems =[
    {
        id: 1,
        src: `https://picsum.photos/id/${getRandomNumber(1,99)}/300/300`,
        itemName: "루미니 카라 bl",
        itemPrice: "35,600",
    }...후략
];

가격데이터 형 변환하여 비교하기

숫자로의 형 변환에 사용한 함수는 다음과 같다.
정렬 알고리즘을 작성할 때 코드를 깔끔하게 쓰기 위해 별도의 함수로 작성을 하는 것이 좋았다.
뒷 인덱스부터 문자열의 일부를 추출하도록 음수 인덱스를 사용하였기 때문에 1000원부터 10만원대의 상품에 대해서 커버가 가능하였다!
이렇게 작성하면 추후 더 비싸거나 싼 상품이 들어와도 처리가 가능할 것이다.

function getPrice(priceStr){
    return Number(priceStr.slice(-priceStr.length,-4) + priceStr.slice(-3))
  }

드디어 정렬 알고리즘 이용하기

인터넷을 검색해보니 버블정렬, 삽입정렬, 합병정렬 등 여러 가지가 있었으나, 나는 버블 정렬을 선택했다. 버블 정렬이 데이터가 더 많아질 경우에는 효율적이지 못하다고 하는데, 다른 알고리즘에 비해 그나마 조금이라도? 알고 있는 게 있어서 사용을 했다.ㅠㅠ(그래도 구현했다는 것에 재미를 붙일 수 있었다.)
내가 이해한 바로는, 버블정렬은 인접한 두 요소를 대소비교하여, 그 위치를 swap시켜주는 과정을 반복함으로서 정렬해준다.
내부의 반복문에서 조건문을 왜 저렇게 작성을 한 건지는 잘 모르겠는데, 일단 구글링 중 찾은 코드가 이거여서 이 코드에 변수명만 바꾸어 사용하였다.
아래 코드는 낮은 가격순으로 정렬하는 코드이고, 조건문에서 부등호 방향만 바꿔주면 높은 가격순으로 정렬하는 코드가 된다.

function sortByLowPrice(arr){
    const sortedArr = arr.slice();
    for(let i = 0 ; i < sortedArr.length ; i++){
      let swap;
      for(let j = 0; j < sortedArr.length - 1 - i ; j++){
        if(getPrice(sortedArr[j].itemPrice) > getPrice(sortedArr[j + 1].itemPrice)){
          swap = sortedArr[j];
          sortedArr[j] = sortedArr[j + 1];
          sortedArr[j + 1] = swap;
        }
      }
      if(!swap){
        break;
      }
    }
    return sortedArr;
  }

이를 이용하여 배열을 정렬해주고, map을 이용하여 아이템들을 렌더링한다.

아이템 렌더링 시, 같은 양식이 반복되어 컴포넌트 파일을 따로 작성하였고, 그 컴포넌트에 props로 item배열의 요소 하나하나를 전달해주었다.

<div className="itemlist">
      {priceFilter === "noPriceFilter" ? dummyItems.map(items => <Item items={items} key={items.id}/>):null}
      {priceFilter === "sortByLowPrice" ? sortedByLowPrice.map(items => <Item items={items} key={items.id}/>):null}
      {priceFilter === "sortByHighPrice" ? sortedByHighPrice.map(items => <Item items={items} key={items.id}/>):null}
</div>
profile
몫을 다하는 사람

0개의 댓글