오늘은 JSP와 jQuery로
별점 리뷰 기능을 구현해 보았다.
클릭 가능한 별점(Star Rating) UI를 만들고,
리뷰 작성 시 별점과 내용이 함께 저장되며,
리스트에서 다시 별점이 표시되도록 설계했다.
1. 전체 기능 개요
구현한 기능은 다음과 같다.
- 클릭하면 별이 채워지는 별점 UI
- 별점을 선택하지 않으면 기본값 5점 자동 입력
- 리뷰 작성 폼과 연결되어 star_point 값이 서버로 전달
- DB에서 가져온 리뷰 리스트에 별점 이미지로 출력
- 리뷰 수정 시 리뷰 내용만 변경 (별점 변경 불가)
2. 별점 UI HTML 구조
별점은 span.star 다섯 개로 이루어진다.
<div class="star_rating">
<span class="star"></span>
<span class="star"></span>
<span class="star"></span>
<span class="star"></span>
<span class="star"></span>
</div>
리뷰 작성 시 별점을 선택하면
JS가 숨겨진 <input type="hidden">을
자동 생성해 서버로 점수를 넘길 예정이다.
3. jQuery로 별 선택 구현
별을 클릭하면 해당 별+이전 별까지
모두 .on 클래스를 적용해
채워진 이미지로 바꾼다.
단 별점을 선택하지 않을 경우
기본값으로 5점이 되도록 설정하였다.
(maxStars=5)
또한 별은 icon일 뿐 실제 값이 아니므로,
별에 값을 대입할 수 있는 input type=hidden을
별 클릭 시 생성해 주고, score 값을 대입시킨다.
이러한 과정을 통해 별점을 DB에 넘길 수 있게 된다.
// ★ 페이지가 로드되면 실행
$(function() {
const maxStars = 5; // 최대로 표시할 별 개수
// ⭐ 초기 세팅: 별점 전체 비우기
$('.star_rating .star').removeClass('on');
// ⭐ 기본값을 5점으로 설정 (모든 별 활성화)
$('.star_rating .star:lt(' + maxStars + ')').addClass('on');
// ⭐ 별 클릭 이벤트
$('.star_rating > .star').click(function() {
const index = $(this).index();
// 클릭한 요소의 0~4 인덱스
const score = index + 1;
// 별점은 1~5이므로 +1
// 1) 모든 별 비활성화
$(this).parent().children('span').removeClass('on');
// 2) 클릭한 별 + 이전 모든 별 채우기
$(this).addClass('on').prevAll('span').addClass('on');
// ⭐ hidden input 생성 또는 업데이트
// 별점을 서버에 전달하려면 form 안에 input이 반드시 있어야 함.
// <input type="hidden" id="star_point"> 이걸 매번 새로 만들 필요 없이
// 1번만 만들고 .val()로 값만 바꿔도 됨.
//id가 star_point인 요소가 존재하는지 체크
if ($('#star_point').length === 0) {
// 없으면 히든 인풋 추가
// attr()는 해당 태그에 여러 속성을 객체 형태로 넣을 수 있는 메서드
// type, id, name, value 값을 한 번에 추가 가능
$('<input>').attr({
type: 'hidden',
id: 'star_point',
name: 'star_point',
value: score // 선택한 별점
}).appendTo('#reviewForm');
} else {
$('#star_point').val(score); // 이미 있으면 값만 변경
}
});
// 폼 제출 시 별점을 선택하지 않았을 경우 기본값 5점 부여
$('#reviewForm').submit(function() {
if ($('#star_point').length === 0) {
$('<input>').attr({
type: 'hidden',
id: 'star_point',
name: 'star_point',
value: maxStars
}).appendTo('#reviewForm');
}
});
});
4. 리뷰 작성 폼
리뷰 작성 시 form타입 안에 있는 히든타입과
JS에 있는 히든타입(별점)이 함께 넘어간다.
즉 bid, writer, content, star_point
4가지 컬럼이 서버로 전달된다.
<!-- ⭐ 리뷰 작성 폼 -->
<c:if test="${not empty userInfo}">
<form id="reviewForm" action="reply.do" method="POST">
<input type="hidden" name="bid" value="${board.bid}">
<input type="hidden" name="writer" value="${userInfo}">
<!-- ⭐ 별점 선택 UI -->
<div class="star_rating">
<span class="star"></span>
<span class="star"></span>
<span class="star"></span>
<span class="star"></span>
<span class="star"></span>
</div>
<textarea class="star_box" name="content" placeholder="별점을 선택하지 않으면 5점으로 자동 등록됩니다."></textarea>
<input type="submit" class="replywrite_btn" value="리뷰 등록">
</form>
</c:if>
<c:if test="${empty userInfo}">
<input type="text" value="리뷰를 작성하려면 로그인이 필요합니다." disabled size="35">
</c:if>
5. CSS로 별 이미지 스타일 지정
별점과 관련된 디자인 영역
즉 CSS 부분은 플러그인을 사용했다.
이미 완성되어 올라온 별점 플러그인 중
CSS만 가져와 사용했으며, 내 코드에 맞게
매핑되는 부분의 클래스 네임만 통일하였다.
1. star_rating
: 별점을 묶는 최상위 컨테이너
2. star_rating .star
: 기본 상태로 비활성화된 별 아이콘
3. star_rating .star.on
: 클릭을 통해 활성화된 별 아이콘
4. star_box
: 리뷰 텍스트가 입력되는 텍스트 박스
5. replywrite_btn
: 리뷰 등록하기 버튼
6. star_icon
: 사용자 닉네임 옆에 표기될 별점 데이터
/* ⭐ 별점 컨테이너 */
.star_rating {
width: 100%;
box-sizing: border-box;
display: inline-flex;
float: left;
flex-direction: row;
justify-content: flex-start;
}
/* ⭐ 기본 비활성 별 (빈 별 이미지) */
.star_rating .star {
width: 25px;
height: 25px;
margin-right: 10px;
display: inline-block;
background: url('../images/star_empty.png') no-repeat;
background-size: 100%;
box-sizing: border-box;
}
/* ⭐ 클릭 시 활성화된 별 (채워진 이미지) */
.star_rating .star.on {
background: url('../images/star_fill.png') no-repeat;
background-size: 100%;
}
/* ⭐ 리뷰 내용 입력 박스 */
.star_box {
width: 400px;
box-sizing: border-box;
display: inline-block;
margin: 15px 0;
background: #F3F4F8;
border: 0;
border-radius: 10px;
height: 100px;
resize: none;
padding: 15px;
font-size: 13px;
font-family: sans-serif;
}
/* ⭐ 리뷰 등록 버튼 */
.replywrite_btn {
display: block;
width: 400px;
font-weight: bold;
border: 0;
border-radius: 10px;
max-height: 50px;
padding: 15px 0;
font-size: 1.1em;
text-align: center;
background: bisque;
}
/* ⭐ 리뷰 목록에서 별 아이콘 출력용 */
.star_icon {
width: 18px;
height: 18px;
vertical-align: middle;
}
6. 저장된 리뷰 별점 출력하기 (JSTL)
DB에서 조회한 ReplyDTO의
starPoint 값을 기반으로 별아이콘을 출력한다.
별점 3점이면 ★ ★ ★ ☆ ☆
이런 식으로 표시하기 위해
JSTL 반복문을 아래처럼 2개 사용한다:
▼ 채워진 별(점수만큼)
<!-- 채워진 별 표시 -->
<c:forEach var="i" begin="1" end="${data.starPoint}">
<!-- data.starPoint만큼 반복하면서 별을 채움 -->
<!-- 예: 별점이 3점이면 i=1,2,3 반복 → ★★★ -->
<img src="images/star_fill.png" class="star_icon">
<!-- 채워진 별 이미지를 표시, class=star_icon으로 CSS 스타일 적용 -->
</c:forEach>
▼ 빈 별(5 - 점수만큼)
<!-- 빈 별 표시 -->
<c:forEach var="i" begin="1" end="${5 - data.starPoint}">
<!-- 전체 별 5개 중에서 남은 별만큼 반복 -->
<!-- 예: 별점 3점이면 5-3=2 → 2개의 빈 별 표시 → ☆☆ -->
<img src="images/star_empty.png" class="star_icon">
<!-- 빈 별 이미지를 표시, 채워진 별과 동일하게 CSS 적용 -->
</c:forEach>
이렇게 하면 별점 개수만큼
자동으로 UI가 구성된다.
7. 리뷰 수정 기능
수정 시에는 별점 변경이 불가능하도록
텍스트 내용만 input으로 변환한다.
// 리뷰 수정 - 별점은 바꿀 수 없고 텍스트만 수정
function edit(rid,bid){
let elem = document.getElementById('reply_'+rid);
let apple = elem.innerText;
apple = apple.replace(" 댓글변경 댓글삭제","");
// 수정 폼 HTML을 문자열로 구성
let txt = '<form action="updateReply.do" method="POST">';
txt += '<input type="hidden" name="bid" value="'+bid+'">';
txt += '<input type="hidden" name="rid" value="'+rid+'">';
txt += '<input type="text" name="content" required value="'+apple+'">';
txt += '<input type="submit" value="댓글변경">';
txt += '</form>';
elem.innerHTML = txt;
}
8. 전체 흐름 요약
1. 사용자가 별 클릭 → hidden input에 점수 저장
2. 폼 제출 시 star_point 값 전달
3. ReplyAction에서 DTO로 데이터 저장
4. ReplyDAO → DB insert(replyDTO)
5. 다시 boardPage.do 이동
6. JSP에서 별점 숫자만큼 채워진 별 반복 출력
위와 같이 V에서 별점 기능이 구현되면
UI 변화뿐만 아니라 실제 유저의 입력값을
서버로 전달하게 되고 서버는 이를 저장한 후
다시 V에게 전달하여 변화된 데이터를 보여준다.
'개주 훈련일지 > 🏋️ 전집중 호흡 훈련' 카테고리의 다른 글
| 카카오페이 API 단건 결제 방법 정리 (0) | 2025.12.05 |
|---|---|
| 간단한 용어 정리 - 프론트 / 백 (0) | 2025.12.02 |
| 비밀번호 입력을 '토글 버튼'으로 보이기/숨기기 기능 추가하기 (0) | 2025.11.27 |
| 서블릿 호출을 커스텀하는 xml 설정 (0) | 2025.11.27 |
| 리다이렉트 vs 포워드 (0) | 2025.11.23 |