[Week2] HelperList Filtering

tia·2022년 2월 19일
0

파이널프로젝트

목록 보기
6/9

🌞 HelperList Filtering

Donorticon 헤더의 기부하기를 클릭하면
로그인을 하지 않은 상태에서도, 아래 사진처럼
등록되어 있는 전체 Helper 목록을 조회 할 수 있다.

이번 포스팅은 Helper 카테고리에 따른 filtering 기능 구현에 관련된 것이다.

1. filtering 기능 구현전의 코드

팀원들과 상의를 해서 만든 API documentation을 보면,
Helper 전체 목록을 불러올 때에는 아래와 같이 결정을 했었다.

filtering 기능이 구현되기 전까지만해도,
해당 API를 이용해서 Helper 전체 목록을 조회하는데에는 문제가 없었다.

하지만, filtering 기능을 구현하다 보니,
내가 만약 아동청소년을 클릭해서 해당 Helper 목록을 조회한 후,
다시 전체보기를 클릭해서 Helper 전체 목록을 불러올 때, 에러가 발행했다.

그래서 클릭한 Helper 카테고리에 따라서
API 주소를 변경해볼까 하는 생각에 API 주소를 수정하게 되었다.

사실 HelperList filtering 디자인은 해피빈을 레퍼런스 삼아서 디자인하게 되었기에,
API 주소도 해피빈을 참고 삼아 해보기로 했다.

2. API 변경

해피빈 사이트을 보니, 처음 사이트에 접속하면 주소는 아래와 같았다.

https://happybean.naver.com/donation/DonateHomeMain

위의 주소로 접속 후, 카테고릴 선택하게 되면, 카테코리의 종류에 따라
아래와 같이 주소가 변경되었다.

https://happybean.naver.com/donation/DonateHomeMain#theme=2
https://happybean.naver.com/donation/DonateHomeMain#theme=3
https://happybean.naver.com/donation/DonateHomeMain#theme=4

이 부분을 모티브 삼아서,
HelperList filtering 주소도 아래와 같이 변경해 보았다.
(아래 코드는 서버에서, HelperList 정보를 filtering해서 제대로 보내주었을 때, 클라이언트 쪽에서 실행되는 코드이다)

navigate(`/helperlist/category/${id}?page=${currentPage}&limit=9`);

해피빈의 경우, 처음 접속했을때 주소가 카테코리를 선택했을 때와 달랐지만,
일단 기능구현이 먼저였기에, 헤더의 기부하기를 클릭하면 아래와 같은 주소로 이동할 수 있게 설정하였다.

<StyledLink to="/helperlist/category/0?page=1&limit=9">
	<ListItem>기부하기</ListItem>
</StyledLink>

Helper 카테고리를 먼저 설명하자면, 아래와 같다.
src 부분에는 해당 아이콘의 디렉토리 주소가 해당 파일 상단 부분에 설정되어 있다.

3. client 설정

일단 코드 구현에 앞서, Helper 카테고리를 아래와 같의 변수에 담아 주었다.

const helperCategory = [
  { id: 0, name: '전체보기', src: all },
  { id: 1, name: '아동청소년', src: child },
  { id: 2, name: '어르신', src: old },
  { id: 3, name: '장애인', src: disable },
  { id: 4, name: '다문화', src: global },
  { id: 5, name: '가족/여성', src: women },
  { id: 6, name: '정신질환자', src: mental },
  { id: 7, name: '그 외', src: etc },
];

3-1. 아이콘/카테고리 이름 설정

(아이콘을 손수 만들어준 내 동생에게 무한 감사드린다 🙏)

우선 useState()를 사용해서 Helper 카테고리 디폴드 값은 전체보기인 0을 설정해 주었다.
원하는 카테고리를 클릭하면, 카테고리 이름과 아이콘에 따라서 해당 id값을 SetHelperCategoryId(id)를 사용해서 helperCategoryId에 저장해준다.

그리고 map() 함수 내부에 name이라는 변수를 따로 만들어서
카테고리 이름을 클릭하던, 아이콘을 선택하던 동일한 id 값을 가져올 수 있도록 설정했다.
(이부분의 아이디어를 제공해주신 팀원분께 무한 감사드린다 🙏)

const [helperCategoryId, SetHelperCategoryId] = useState(0);

const handleNameClick = async (name) => {
    const filtered = helperCategory.filter((x) => x.name === name);
    const id = filtered[0].id;
    setCurrentPage(1);
    SetHelperCategoryId(id);
};

return(
<CategoryContainer>
	<CategoryBox>
		{helperCategory.map((x, idx) => {
            const name = x.name;
            return (
              <CategoryContent key={idx}>
                <CategoryImageBg onClick={() => handleNameClick(name)}>
                  <CategoryImage src={x.src} />
                </CategoryImageBg>
                <CategoryName onClick={() => handleNameClick(name)}>
                  {name}
                </CategoryName>
              </CategoryContent>
			);
		})}
	</CategoryBox>
</CategoryContainer>
);

3-2. axios 설정

db 구조상, 전체보기를 다른 카테고리와 별도로 취급해주어야만 했다.
전체보기의 경우, Helper db에서 모든 리스트를 불러올 수 있었지만,
아래 사진과 같이 Helper 카테고리를 별도의 db인 vulnerable 테이블로 관리하고 있다.

그래서 일달 HelperList를 불러오는 경우를 2가지로 나누어 주었다.

 useEffect(() => {
    if (helperCategoryId === 0) {
      getList(helperCategoryId);
    } else {
      getFilteredList(helperCategoryId);
    }
  }, [currentPage, helperCategoryId]);

전체보기

const getList = async (id) => {
	try {
		const { data } = await axios.get(
			`/helperlist/category/${id}?page=${currentPage}&limit=9`,
      	);
      	const { list: helperList, maxPage } = data;
      	setList(helperList);
      	setMaxPage(maxPage);
      	navigate(`/helperlist/category/${id}?page=${currentPage}&limit=9`);
    } catch (e) {
      	console.log(e);
    }
};

그 외

const getFilteredList = async (id) => {
	setCurrentPage(1); // 다른 카테고리에서 페이지가 1인 아닌경우가 있을 수 있기에 설정
    try {
      const { data } = await axios.get(
        `/helperlist/category/${id}?page=${currentPage}&limit=9`,
      );
      const { list, maxPage } = data;
      const filteredList = list.map((x) => x.helper);
      setList(filteredList);
      setMaxPage(maxPage);
      navigate(`/helperlist/category/${id}?page=${currentPage}&limit=9`);
    } catch (e) {
      console.log(e);
    }
  };

3-3. 해야할일

  • 카테고리 선택시, 카테고리 아이콘 배경 색깔 흰색으로 변경
  • 카테고리 선택시, 카테고리 이름 볼드 처리
  • getList()getFilteredList() 함수 구조가 거의 비슷한데, 하나의 함수로 코드 단순화

4. server 설정

일단 라우터 설정부터 해주었다.

router.get('/helperlist/category/:id?', helperList.filterList.getFilteredList);

filterList.js 설정은 아래와 같다.

설명 1
pagination 관련 코드인데, 이부분은 추후 따로 포스팅을 할 예정이다.

const { helper, helper_vulnerable } = require('../../models');

module.exports = {
  getFilteredList: async (req, res) => {
    const id = parseInt(req.params.id);

    // 설명 1
    let page = Math.max(parseInt(req.query.page));
    let limit = Math.max(parseInt(req.query.limit));

    page = !isNaN(page) ? page : 1;
    limit = !isNaN(limit) ? limit : 9;

    const skip = (page - 1) * limit;

    if (id === 0) {
      try {
        const allList = await helper.findAndCountAll({
          limit: 9,
          offset: skip,
          where: {},
          attributes: { exclude: ['password', 'createdAt', 'updatedAt'] },
        });
        const { count, rows: list } = allList;
        const maxPage = Math.ceil(count / limit);
        res.send({ list, maxPage });
      } catch (e) {
        console.log(e);
      }
    } else {
      try {
        const filteredList = await helper_vulnerable.findAndCountAll({
          limit: 9,
          offset: skip,
          where: { vulnerable_id: id },
          include: {
            model: helper,
            required: true,
            attributes: ['id', 'name', 'slogan', 'img'],
          },
        });
        const { count, rows: list } = filteredList;
        const maxPage = Math.ceil(count / limit);
        res.send({ list, maxPage });
      } catch (e) {
        console.log(e);
      }
    }
  },
};

4-1. 해야할일

  • req.params 정보가 넘어오지 않았을때 에러 처리
  • id값이 0~7 사이가 아닌 경우 에러 처리
  • gallery 모델과 helper 모델 id로 연결해서 이미지 한개 끌어와야함

0개의 댓글