프로젝트 진행중 별점과 리뷰를 남기는 기능이 필요했고, 이를 구현한것을 기억하기 위해 이 글을 작성한다.
필자는 react-hook-form을 같이 사용하여 유효성 검사까지 추가하였다.
const arrayIndex = [5, 4, 3, 2, 1];
const [rate, setRate] = useState<number>(1);
<fieldset className={styles['stars-wrapper']}>
{arrayIndex.map((index) => (
<React.Fragment key={index}>
<input
type='radio'
id={`${index}`}
value={arrayIndex[index]}
{...register('rating', { required: true })}
></input>
<label
htmlFor={`${index}`}
className={styles['stars']}
onClick={() => setRatingIndex(index)}
>
⭐️
</label>
</React.Fragment>
))}
</fieldset>
fieldset으로 input을 그룹화를 시켜준다.
5개의 별이 필요하므로 배열을 선언후 map을 실행시킨다.
배열의 값을 5부터 둔 이유는 뒤에서 설명할 것이다.
.stars-container {
display: flex;
align-items: center;
gap:8px;
}
.stars-container > fieldset {
display: inline-block;
gap: 8px;
direction: rtl;
}
.stars {
font-size: 40px;
color: transparent;
text-shadow: 0 0 0 #f0f0f0;
cursor: pointer;
}
.stars:hover{
text-shadow: 0 0 0 #FBB900; /* 마우스 호버 */
}
.stars:hover ~ label{
text-shadow: 0 0 0 #FBB900; /* 마우스 호버 뒤에오는 이모지들 */
}
.stars-wrapper input[type=radio]{
display: none;
}
.stars-wrapper input[type=radio]:checked ~ label{
text-shadow: 0 0 0 #FBB900;
}
label로 지정한 .stars에 color: transparent 로 주고 text-shadow를 통해 새로운 색을 부여한다.

별점의 경우 5점별에 마우스를 올리면 뒤에 있는 별들이 같이 hover 되어야 한다.
.stars:hover ~ label{
text-shadow: 0 0 0 #FBB900; /* 마우스 호버 뒤에오는 이모지들 */
}
1,2,3,4,5번 중에 3번을 호버 한다고 하면, 1번과 2번의 색상이 같이 채워져야한다.
일반 형제 결합자는 A ~ B{} 라고 하면, A 뒤에 위치하는 요소들만 적용된다.
하지만 HTML 상의 요소의 위치는 1번과 2번이 3번의 앞에 존재한다.
따라서 3번 ~ 1번 {} 이 가능할 수 없다.
direction: rtl을 쓰지 않으면 원래의 방향은 ltr이므로 오른쪽 별부터 하나씩 채워지게 된다.
direction을 통해 방향을 바꾸어준다. rtl을 사용하면 하위 요소들이 오른쪽부터 채워지게 된다.
여기서 배열의 점수를 5,4,3,2,1 로 설정한 이유가 나온다.
ltr로 설정했다면 5 4 3 2 1 이 되어 오른쪽 별 부터 채워지고 이것을 뒤집어 1 2 3 4 5 로 만든다.
원래의 요소가 5 4 3 2 1 이었으므로 별을 클릭했을때 state를 5부터 시작해주었다.