안녕하세요, hr0513입니다! 😊
앞서 블로그에 훅에 대해 정리한 내용을 바탕으로, 해당 훅을 활용한 별점 구현 방법을 공유하려고 합니다.
구현 방식에 대해 의견이나 수정할 부분이 있다면 댓글로 남겨주세요!
1. 필요한 별점 이미지 제작
별점 구현을 시작하기 전에 필요한 별점 이미지를 먼저 제작합니다.
2. StarRating 컴포넌트
먼저 StarRating 컴포넌트를 아래와 같이 작성합니다.
count와 isFixed는 props로 전달됩니다.
<StarRating
count={3.5}
isFixed={true}
/>
- count: 별점 값
- isFixed: 별점이 고정되었는지 여부 (true 일 경우 변경 불가)
3. SCSS 스타일 작성
별점 스타일을 scss로 작성합니다.
.star {
&Icon {
position: relative;
width: 120px;
height: 24px;
background: url('../../assets/images/icon/star.png') no-repeat;
background-position-y: top;
&.cursorDefault {
cursor: default;
}
&Filled {
position: absolute;
top: 0;
left: 0;
height: 100%;
background: url('../../assets/images/icon/star.png') no-repeat;
background-position-y: bottom;
transition: width 0.2s ease;
}
}
}
transition: width 0.2s ease; 를 사용하여 별점의 width가 변경될 때 부드럽게 애니메이션 효과를 주었습니다.
4. useState와 useEffect로 별점 상태 관리
useState 훅을 사용하여 별점 값과 hover 상태의 별점 값을 관리하고
useEffect 훅을 사용하여 props로 전달받은 count 값을 별점 값에 초기화합니다.
const [rating, setRating] = useState(0);
const [hoverRating, setHoverRating] = useState(null);
useEffect(() => {
setRating(count);
}, [count]);
5. 별점 넓이 계산 함수
별점의 넓이를 계산하는 함수입니다.
각 별의 넓이는 20%로 계산하여 0~5 사이의 별점에 맞게 반환됩니다.
const calculateWidth = (rating) => {
return (rating * 20);
};
const filledWidth = `${calculateWidth(hoverRating ?? rating)}%`;
rating과 hoverRating은 1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5 값을 가질 수 있습니다.
6. 이벤트 헨들러
별점이 클릭되거나 마우스를 이동시킬 때의 이벤트 헨들러입니다.
// 클릭 시 별점 계산
const handleClick = (event) => {
const newRating = calculateRating(event);
setRating(newRating);
};
// 마우스가 영역 내에서 이동할 때 별점 계산
const handleMouseMove = (event) => {
const newRating = calculateRating(event);
setHoverRating(newRating);
};
// 마우스가 벗어날 때 hover 상태 해제
const handleMouseLeave = () => {
setHoverRating(null);
};
7. 별점 계산 함수
getBoundingClientRect() 메서드를 사용하여 클릭된 위치에 맞는 별점을 계산합니다.
const calculateRating = (event) => {
if (isFixed) return;
const rect = event.currentTarget.getBoundingClientRect();
const clickX = event.clientX - rect.left;
let newRating = (clickX / rect.width) * 5;
newRating = Math.ceil(newRating);
return newRating;
}
- getBoundingclientRect() 란?
이 메서드는 현재 HTML 요소의 크기와 위치를 반환합니다.
이를 통해 해당 요소의 top, left, width, height를 알 수 있습니다.
- clickX 계산
event.clientX는 브라우저 화면에서 마우스 커서의 X 좌표입니다.
rect.left는 요소의 왼쪽 경계가 화면 왼쪽에서 몇 픽셀 떨어져 있는지 나타냅니다.
event.clientX - rect.left는 클릭한 위치가 요소의 왼쪽 경계로부터 얼마나 떨어져 있는지 계산합니다.
- 별점 비율 계산
clickX / rect.width는 클릭한 위치가 요소의 너비에서 차지하는 비율을 계산합니다.
그 비율에 5를 곱하면 0~5 범위로 별점 값을 계산할 수 있습니다.
마지막으로 Math.ceil()을 사용하여 소수점을 반올림하여 정수 값을 얻습니다.
8. 전체 코드
import { useState, useEffect } from 'react';
import styles from './StarRating.module.scss';
const StarRating = ({ count = 0, isFixed = true }) => {
const [rating, setRating] = useState(count);
const [hoverRating, setHoverRating] = useState(null);
// 별점 너비 계산
const calculateWidth = (rating) => {
return (rating * 20);
};
const filledWidth = `${calculateWidth(hoverRating ?? rating)}%`;
// 별점 계산
const calculateRating = (event) => {
if (isFixed) return;
const rect = event.currentTarget.getBoundingClientRect();
const clickX = event.clientX - rect.left;
let newRating = (clickX / rect.width) * 5;
newRating = Math.ceil(newRating);
return newRating;
}
// 클릭 시 별점 계산
const handleClick = (event) => {
const newRating = calculateRating(event);
setRating(newRating);
};
// 마우스가 영역 내에서 이동할 때 별점 계산
const handleMouseMove = (event) => {
const newRating = calculateRating(event);
setHoverRating(newRating);
};
// 마우스가 벗어날 때 hover 상태 해제
const handleMouseLeave = () => {
setHoverRating(null);
};
useEffect(() => {
setRating(count);
}, [count]);
return (
<div
className={styles.starIcon}
onMouseMove={handleMouseMove}
onClick={handleClick}
onMouseLeave={handleMouseLeave}
>
<div
className={styles.starIconFilled}
style={{ width: filledWidth }}
></div>
</div>
);
};
export default StarRating;
9. 실행 화면
isFixed가 false일 경우
- 클릭이나 마우스 이동 시 별점 계산이 이루어지며, 계산된 별점은 정수로 표시됩니다.
isFixed가 true일 경우
- 클릭이나 마우스 이동 시 별점 계산이 이루어지지 않습니다.
- 별점은 고정되어 표시되며 소수점도 표시됩니다.
'개발 > React' 카테고리의 다른 글
[React] Next.js에서 GSAP(ScrollTrigger) 사용 시 발생하는 Hydration 오류 해결하기 - Hydration failed because the server rendered HTML didn't match the client. (0) | 2024.11.13 |
---|---|
[React] 요소 나타날 때 통통 튀는 애니메이션 효과 주기 (1) | 2024.11.13 |
[React] 카테고리 기능 만들기 (0) | 2024.11.11 |
[React] 탭 기능 만들기 (0) | 2024.11.09 |
[React] 리액트 훅 살펴보기 - 1 (2) | 2024.11.07 |