이번 프로젝트에서 기능을 좀 더 고도화를 해볼까 하고 도전한 것이 별점이다. 처음에는 그냥 숫자로 나타내려고 하다가 아이콘도 써보고 싶고 별점을 구현해보고 싶어서 고민을 하다가 도전을 해보았다. 사실 그냥 숫자보다 별모양이 더 이쁘니까 ㅎㅎ
//custom hook 및 style이라고 적혀있는 코드 외에는 모두 별점이 있는 컴포넌트에서 작업했다.
아이콘 선택
svg를 쓸지 뭘 쓸지 고민을 하다가 폰트어썸을 쓰기로 결정을 했고 그 중 fastar, solid를 기본으로 진행하였다. 사실 초기엔 빈 별에서 꽉 찬 별로 진행하고 싶었는데 두개를 import 해오는게 안돼서 꽉찬별의 색을 바꿔주기로 했다.
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faStar } from '@fortawesome/free-solid-svg-icons';
custom hook 만들고 다른 컴포넌트에서 import 해오기
그리고 별점은 후기가 있는 프로젝트라면 어디서나 쓰일 것 같아서 재사용을 위해 custom hook을 만들어보기로 했다. 유저가 클릭한 별아이콘의 id값을 가지고 와 rate라는 state값을 변경 시켜주는 reactionStar라는 함수를 만들어준다. 그리고 기본 값인 5개의 회색별을 보여주기 위해 배열의 길이가 5인 starArr를 만들어준다. 또 전체리뷰의 평균 평점을 계산하기 위해 totalRating 함수를 만들었다. totalRating는 리뷰의 전체 데이터를 받아와서 평점을 모두 더한 뒤 리뷰의 갯수로 나누어 준다. makeZero 함수는 리뷰 등록 후 클릭되어있던 별 아이콘의 값을 초기화 시켜주는 함수이다. 마지막으로 필요한 값들을 return 해주어 다른 컴포넌트에서도 값을 불러와서 사용할 수 있게 만들어준다.
//custom hook
const useStarRating = () => {
const [rate, setRate] = useState(0);
const starArr = [1, 2, 3, 4, 5];
const reactionStar = id => {
setRate(id);
};
const totalRating = score => {
let allStarRating = 0;
let plusRating = 0;
for (let i = 0; i < score?.length; i++) {
allStarRating += Number(score[i].rating);
plusRating = allStarRating / score.length;
}
return plusRating;
};
const makeZero = () => {
setRate(0);
};
return { rate, starArr, reactionStar, totalRating, makeZero };
};
만들어둔 custom hook을 import 해온 뒤 구조분해할당해준다.
import useStarRating from '../../hooks/useStarRating.js';
const { rate, starArr, reactionStar, totalRating, makeZero } = useStarRating();
totalRating 함수 사용
유저의 리뷰의 전체 평균 평점을 나타내기 위해 import로 가져온 것 중 평점에 관련된 함수인 totalRating을 불러온 뒤 데이터를 인자로 보내준다. 전체 댓글의 별점을 갯수로 나눠야해서 댓글의 전체 데이터를 인자로 보내주었다. 이제 totalRating함수에서 계산된 값이 화면에 출력될 것이다.
<Style.ProfileBold>평점</Style.ProfileBold>
<div>{totalRating(reviewData)}</div>
reactionStar 함수 사용
1,2,3,4,5라는 5개의 요소를 가진 배열인 starArr를 map을 돌려서 별 아이콘을 5개를 만들어준다. 그리고 custom hook에서 가져온 reactionStar라는 함수에 인자로 유저가 클릭한 별 아이콘의 값을 보내준다. 상태값인 rate와 아이콘 값인 star를 비교해 상태값이 더 높을때 true가 되고 boolean 값을 스타일컴포넌트로 보내주어 동적으로 색을 변경할 수 있도록 만들었다. 이제 유저가 3번째 별을 클릭하면 3개의 별이 노란색으로 바뀌게 된다. (회색 별을 5개 밑에 깔아주고 유저가 별을 클릭하면 그만큼 노란별이 회색별을 덮을수 있도록 구현하였다.)
작업을 할 때엔 star와 idx를 따로 생각했는데 지금보니 star가 idx+1인 값이어서 idx가 필요가 없다는 생각이 들었다. 더군다나 배열의 값이 추가 또는 삭제가 될 때 index값이 변경되어 원하지 않는 결과값이 나올 수도 있기 때문에 map에서 key값에는 index를 넣는걸 지양하라고 되어있어서 빼는 것이 좋을 것 같다. 다만 starArr이 하드코딩된 배열이라 이걸 재활용하기 쉽게 만드는 방법을 다시 생각해보는게 좋을 것 같다.
그리고 추천받은 방법은 array.from이나 array.fill을 사용해보라는 거였는데 조금 어려워서 생각을 많이 해봐야할 것 같다. 하지만 포기하지 않고 도전해봐야지!
{starArr?.map((star, idx) => {
return (
<Style.AllStar star={star <= rate} key={idx}>
<FontAwesomeIcon
icon={faStar}
onClick={() => reactionStar(star)}
/>
</Style.AllStar>
);
})}
styled-components로 값을 넘겨준 star 라는 props는 star 값이 유저가 클릭한 rate라는 값보다 작거나 같을 때 노란색, 크면 회색으로 바뀌도록 구현이 되어있다. 이때 아이콘의 색을 변경해야해서 조금 어려웠었지만 동기의 도움으로 금방 해결할 수 있었다.
//style.js
AllStar: styled.div`
display: flex;
svg {
font-size: 1.3em;
path {
color: ${props => (props.star ? props.theme.mainColor : 'lightgray')};
cursor: pointer;
}
}
`,
코드리뷰를 받으면서 hook을 더 재활용할 수 있게 만드는게 좋을 것 같다는 말을 들었다. 가령 최대 점수를 4점, 5점, 3점 이런식으로 유동적으로 변경할 수 있으면 더 좋지 않겠냐는 의견이었고 사실 최대점수는 홈페이지 당 1개겠지만 여러 프로젝트에서 사용할 수 있도록 만들려면 바꿔두는것도 좋겠다는 생각이 들어서 리팩토링을 하면서 해봐야겠다.
'🍀오늘도 삽질 중🍀 > React.js' 카테고리의 다른 글
CRA로 구축한 프로젝트에 TS 적용하기 (0) | 2023.09.29 |
---|---|
Array.from 이용해서 새로운 배열로 별점 생성하기 (0) | 2023.09.24 |
여러개의 모달을 하나의 state로 관리하기 (0) | 2023.08.16 |
쿼리스트링과 useParams 사용해 원하는 정보만 받아오기 (0) | 2023.08.11 |
버튼에 따라 다른 UI 구현하기 (0) | 2023.08.10 |