navigate('?category=${*category*}');
⇒ 해당 url로 이동하여 쿼리 파라미터 값을 searchParam를 이용해 변수에 담고, 이 변수를 API 엔드포인트에 넣어줬다. ⇒ 결국, 카테고리 별로 다른 엔드포인트를 갖게된다. 백엔드에서는 엔드포인트에 해당하는 데이터리스트를 보내줄 수 있게된다.prodList.slice(offset, limit * page).map
과 같이 구현하였다.limit은 페이지에서 보여줄 상품의 개수이다. 우리는 5개씩 보여줄 예정이므로 5.
page는 페이지네이션에서 현재 페이지를 의미한다.
offset은 slice()의 시작위치로, (page-1)*limit의 공식(?)으로 산출할 수 있다.
⇒ 이렇게 상품리스트 배열을 가공하면 데이터가 몇개가 들어오던지 5개씩 보여주는 구현이 가능하다.
위 과정을 통해 상품을 특정 개수만 화면에 보여지게 구현하였다. 이제 다음페이지로 갔을때 다음 데이터가 불러와지도록 페이지네이션 기능을 구현해야한다.
⇒ 상품 총 갯수를 알아야 몇 개의 페이지가 필요한지 알 수 있고, 그 값을 기준으로 상품 리스트 하단에 페이지네이션을 구현할 수 있다.
⇒ 페이지네이션을 구현할 때, 어떻게 map()메서드로 사용할 수 있을지에 대한 고민이 많았다. 왜냐하면 상품 리스트의 경우 상품에 대한 정보가 담긴 배열에 대해서 map()를 적용하면 되지만 페이지네이션의 경우 map함수를 적용할 대상 배열이 없기 때문,
⇒ 구글링을 통해 알게된 방법으로 배열을 만들고 map함수를 돌려 페이지네이션 기능을 구현할 수 있었다. 그 방법은 다음과 같다.
⇒ Array(pageNum).fill().map()
⇒ 그 이유는, 사이트 규모가 커질수록 서버에서 모든 데이터를 보내고 프론트단에서 그 데이터를 가공하는 것은 효율적이지 못하기 때문.
목데이터를 프론트 단에서 가공하여 ui에 상품리스트를 보여줬었는데, 실제로 백엔드로부터 데이터를 받아서 보여주는 과정에서 많은 착오가 발생했다.
첫째로는 데이터가 배열자체로 오는 것이 아니라 response라는 객체안에 result라는 키값에 접근해야 배열에 접근할 수 있었다.
⇒ .then(result => productList(result))
<< 이것을
⇒ .then(result => productList(response.result))
<< 로 수정하여 해결하였다.
⇒ 즉, json()통해 js 언어로 가공한 응답은 생각했던대로 배열로 오는 것이 아니라 response라는 객체형식으로 오고, result 키 값에 원하는 배열 값이 있었던 것임.
response = {result : [원하는 배열]}
두번째는 백엔드의 상품리스트 데이터가 몇개인지 정보를 받아야 페이지네이션이 가능했으므로, 상품의 총개수 값을 백엔드개발자에게 요청하였다. 그 결과 데이터를 가져와서 가공하는 과정이 필요했다. (상품총개수 데이터를 제외한 나머지 상품정보만 productList state에 추가)
⇒ API로 부터 상품정보를 받아서 map함수를 돌리기 위해서는 배열에 상품 정보 객체만 들어있어야 한다.
⇒ 그러나, 추가적으로 페이지네이션 기능을 구현하기 위해서는 해당 카테고리의 상품수를 미리 알아야하고 이 값은 배열에 같이 담겨져온다.
⇒ 즉, fetch함수의 마지막 부분(.then)단에서 이 배열을 잘라서 나눠놔야 원하는 값을 가져와서 쓸 수 있다.
.then(res => res.json())
.then(res => {
const prodNum = res.result.length - 1;
const dataList = res.result.slice(0, prodNum);
const total = res.result[prodNum].category_total;
setTotal(total);
setProdList(dataList);
.then(res => res.json())
.then(res => {
setTotal(res.result[0].total_count);
setProdList(res.result[0].products);
});
기존에는 상품 리스트 페이지에서 데이터를 백엔드에 요청할 때, 카데고리 넘버와 페이지수 2개의 변수를 엔드포인트로 하여 요청하였다. (페이지당 상품 갯수는 5개로 고정)
그러나, 5일차 스탠드업 미팅과정에서 리스트 페이지에서 페이지당 갯수와 솔팅버튼을 추가하자는 결과가 나왔고, 백엔드 개발자와 미팅해 본 결과 엔드포인트를 총 4개로 하여 요청하기로 했다.
⇒ 카테고리넘버, 페이지당 상품갯수(limit), 페이지당 첫 상품의 시작위치(offset), 솔팅기준값(최신순, 가격순 등)
imit버튼과 sort버튼은 select와 option 태그를 사용하였고, 이 값이 변화하면 그 값을 fetch로 백엔드 API에 호출하는 로직을 구현하였다.
⇒ 처음 구현할 때는 값이 바뀌면 url에 추가하고 그 url의 값을 searchparameter로 받아와서 보내는 로직을 생각했다.
⇒ 그러나, 굳이 url에 넘겨서 그 값을 searchparameter로 받는 로직을 구현하지 않고도 구할 수 있는 값이라면 searchParams를 사용하지 않았다. (limit, offset, sort값에 해당)
⇒ category 값만 searchParams를 이용해 fetch 함수의 url에 넣는다.
⇒ 여기서 fetch함수는 4가지 엔드포인드가 변화하면 작동하는 형식으로 구현하였다.
const category = searchParams.get('category');
const getProductList = () => {
fetch(
`http://10.58.3.49:8000/products?category=${parseInt(
category
)}&offset=${offset}&limit=${limit}&sort_by=${sort}`
)
.then(res => res.json())
.then(res => {
setTotal(res.result[0].total_count);
setProdList(res.result[0].products);
});
};
5개의 카테고리 중, 특정 카테고리를 클릭하면 해당 카테고리를 제외한 나머지 카테고리는 흐려지도록 기능을 구현하고자 했다.
const MenuTap = ({ menu, isClickedList, idx }) => {
const focusOnMenuTap = target => {
isClickedList.forEach((menu, i) => {
if (i !== target) {
isClickedList[i] = false;
} else {
isClickedList[i] = true;
}
});
};
return (
<div
className={isClickedList[idx] ? 'menuTapOn' : 'menuTapOff'}
onClick={() => {
focusOnMenuTap(idx);
}}
>
{menu.cate_name}
</div>
);
};