리뷰 등록 순서
1. 버튼을 클릭해 리뷰 입력할 모달창을 띄운다.
2. 모달창에 별점주는 화면을 구성
3. 회원 아이디, 리뷰 점수, 내용 입력해 리뷰 등록
4. 리뷰 개수와 평균이 변경되었으므로, URL 재호출해서 갱신
진행 순서
- ReviewDTO 구성
: Movie와 Member 참조하므로 단순 문자열 즉, 회원 mid와 mno를 참조하는 형태로 변경 - 작성한 리뷰의 평점과 내용을 변경할 수 있도록 Review 엔티티에 수정 메서드 추가
- changeGrade()
- changeText()
- ReviewService의 entityToDto()와 dtoToEntity() 메서드 정의 후, 추가 기능 작성
- 특정한 영화의 모든 리뷰를 가져오는 기능
- 새로운 영화 리뷰를 등록하는 기능
- 특정 영화 리뷰를 수정하는 기능
[리뷰 엔티티 클래스를 수정한다.] - 특정 영화 리뷰를 삭제하는 기능
- ReviewServiceImpl에서 ReviewService 메서드 구현
- RestController로 ReviewController 작성
- ReviewDTO는 JSON 형태로 변환되어 처리
- 새로운 영화 리뷰 등록도 JSON 폼새으로 전송 처리
- Read.html의 리뷰 모달창과 이미지 모달창 작성
- 별점 처리 라이브러리 적용
1. ReviewDTO 구성
package com.movie.boot4.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ReviewDTO {
//리뷰 번호
private Long reviewnum;
//영화 번호
private Long mno;
//회원 번호
private Long mid;
//회원 정보
private Long id;
private String nickname;
private String email;
//리뷰 정보
private int grade;
private String text;
private LocalDateTime regDate, modDate;
}
2. Review 엔티티 클래스 메서드 추가
package com.movie.boot4.entity;
import lombok.*;
import javax.persistence.*;
@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
//연관 관계의 엔티티 사용하지 않도록 설정
@ToString(exclude = {"movie", "member"})
public class Review extends BaseEntity{
@Id
@GeneratedValue (strategy = GenerationType.IDENTITY)
private Long reviewnum;
//다대일 관계에서는 항상 기본값이 즉시 로딩이므로, 지연 로딩으로 설정하는 작업 필요
//-> 지연로딩을 사용하므로, 테스트시에 @Transactional 필요
@ManyToOne (fetch = FetchType.LAZY)
private Movie movie;
@ManyToOne (fetch = FetchType.LAZY)
private Member member;
private int grade;
private String text;
//리뷰 수정을 위한 메서드 작성
public void changeGrade(int grade){
this.grade=grade;
}
public void changeText(String text){
this.text=text;
}
}
3. ReviewService에 메서드 정의
package com.movie.boot4.service;
import com.movie.boot4.dto.ReviewDTO;
import com.movie.boot4.entity.Member;
import com.movie.boot4.entity.Movie;
import com.movie.boot4.entity.Review;
import java.util.List;
public interface ReviewService {
//특정 영화의 모든 리뷰를 가저온다.
List<ReviewDTO> getListOfMovie(Long mno);
//영화 리뷰를 추가
Long register(ReviewDTO movieReviewDTO);
//특정한 영화리뷰 수정
void modify(ReviewDTO movieReviewDTO);
//영화 리뷰 삭제
void remove(Long reviewnum);
default Review dtoToEntity(ReviewDTO movieReviewDTO){
//영화 객체와 멤버 객체가 필요하다.
//해당 영화객체는 영화리뷰DTO에서 mno을 이용해 객체 생성
//해당 회원객체는 영화리뷰DTO에서 mid를 이용해 객체 생성
Review movieReview = Review.builder()
.reviewnum(movieReviewDTO.getReviewnum())
.movie(Movie.builder().mno(movieReviewDTO.getMno()).build())
.member(Member.builder().mid(movieReviewDTO.getMid()).build())
.grade(movieReviewDTO.getGrade())
.text(movieReviewDTO.getText())
.build();
return movieReview;
}
default ReviewDTO entityToDTO(Review review){
ReviewDTO reviewDTO= ReviewDTO.builder()
.reviewnum(review.getReviewnum())
.mno(review.getMovie().getMno())
.mid(review.getMember().getMid())
.nickname(review.getMember().getNickname())
.email(review.getMember().getEmail())
.grade(review.getGrade())
.text(review.getText())
.regDate(review.getRegDate())
.modDate(review.getModDate())
.build();
return reviewDTO;
}
}
4.ReviewServiceImpl에서 메서드 구현
package com.movie.boot4.service;
import com.movie.boot4.dto.ReviewDTO;
import com.movie.boot4.entity.Movie;
import com.movie.boot4.entity.Review;
import com.movie.boot4.repository.ReviewRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Service
@Log4j2
@RequiredArgsConstructor
public class ReviewServiceImpl implements ReviewService{
//interface와 달리 다른 곳에서 호출을 할 메서드이므로 public으로 선언
//(1) 엔티티를 DTO로 변환
//(2) DTO를 엔티티로 변환
private final ReviewRepository reviewRepository;
//특정 영화의 모든 리뷰를 가저온다. -> 엔티티로 리포지토리에서 원하는 리뷰를 찾아 DTO로 변환해 리스트로 반환
@Override
public List<ReviewDTO> getListOfMovie(Long mno){
Movie movie=Movie.builder()
.mno(mno)
.build();
List<Review> result=reviewRepository.findByMovie(movie);
return result.stream()
.map(movieReview -> entityToDTO(movieReview))
.collect(Collectors.toList());
}
@Override
//영화 리뷰를 추가 -> DTO를 엔티티로 변환해 리포지토리에 저장 후, DTO의 리뷰번호 반환
public Long register(ReviewDTO movieReviewDTO){
Review review=dtoToEntity(movieReviewDTO);
reviewRepository.save(review);
return movieReviewDTO.getReviewnum();
}
@Override
//특정한 영화리뷰 수정 -> null일 수 있으므로 Optional 사용해, DTO로 리뷰를 찾고, 있으면 엔티티에서 수정 후, 리포지토리에 저장
public void modify(ReviewDTO movieReviewDTO){
Optional<Review> result=reviewRepository.findById(movieReviewDTO.getReviewnum());
if(result.isPresent()){
Review review = result.get();
review.changeGrade(movieReviewDTO.getGrade());
review.changeText(movieReviewDTO.getText());
reviewRepository.save(review);
}
}
@Override
//영화 리뷰 삭제 -> 리포지토리에서 리뷰번호로 제거
public void remove(Long reviewnum){
reviewRepository.deleteById(reviewnum);
}
}
5.ReviewController
- RestController로 설계해 '/reviews/'로 시작되는 경로 처리
- ReviewDTO는 JSON으로 변환되어 처리
- 새로운 영화 리뷰 등록도 JSON으로 변환되어 전송 처리
방식 | URL | 결과 데이터 | 설명 |
GET | /reviews/영화번호/all | ReviewDTO 리스트 | 해당 영화의 모든 리뷰들을 반환 |
POST | /reviews/영화번호 | 생성된 리뷰 번호 | 새로운 리뷰를 등록 |
PUT | /reveiews/영화번호/영화리뷰번호 | 리뷰의 수정 성공 여부 | 리뷰 수정 |
DELETE | /reveiews/영화번호/영화리뷰번호 | 리뷰 삭제 | 리뷰 삭제 |
package com.movie.boot4.controller;
import com.movie.boot4.dto.ReviewDTO;
import com.movie.boot4.service.ReviewService;
import lombok.RequiredArgsConstructor;
import lombok.extern.java.Log;
import lombok.extern.log4j.Log4j2;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
//JSON 처리를 위한 RestController 설계
@RestController
//객체를 JSON으로 : RequestBody
//JSON을 객체로 : ResponseBody
//경로 처리를 위한 어노테이션
@RequestMapping("/reviews")
@Log4j2
@RequiredArgsConstructor
public class ReviewController {
private final ReviewService reviewService;
// 해당 영화 모든 리뷰 반환, 새로운 리뷰 등록
// RestController에서 수정 : PutMapping 사용
// RestController에서 삭제 : DeleteMapping 사용
// @GetMapping("/{mno}/all")
// @PostMapping("/{mno}")
// @PutMapping("/{mno}/{reviewnum}")
// @DeleteMapping("/{mno}/{reviewnum}")
//해당 영화 모든 리뷰 반환 -> mno을 getListOfMovie 메서드에 파라미터로 전달
@GetMapping("/{mno}/all")
//@PathVariable은 URL 경로에 변수를 넣는다.
public ResponseEntity<List<ReviewDTO>> getList(@PathVariable("mno") Long mno){
log.info("-------list-----------");
log.info("MNO : "+mno);
//getListOfMovie : 특정 영화의 모든 리뷰를 가저오는 메서드
List<ReviewDTO> reviewDTOlist = reviewService.getListOfMovie(mno);
return new ResponseEntity<>(reviewDTOlist, HttpStatus.OK);
}
//새로운 리뷰 등록 후 해당 리뷰 번호 반환 -> 요청 본문에 리뷰DTO를 register메서드의 파라미터로 전달
@PostMapping("/{mno}")
//스프링에서의 비동기 처리를 위한 어노테이션
//요청본문 requestBody, 응답본문 responseBody
public ResponseEntity<Long> addReview(@RequestBody ReviewDTO reviewDTO){
log.info("-----------add MovieReview-------------");
log.info("reviewDTO: "+ reviewDTO);
Long reviewnum = reviewService.register(reviewDTO);
return new ResponseEntity<>(reviewnum, HttpStatus.OK);
}
//RestController에서 수정 : PutMapping 사용
//RestController에서 삭제 : DeleteMapping 사용
//리뷰 수정 -> 경로에 변수를 넣고, 요청 본문을 전달하는데, 리뷰DTO를 modify 메서드에 파라미터로 전달
@PutMapping("/{mno}/{reviewnum}")
public ResponseEntity<Long> modifyReview(@PathVariable Long reviewnum, @RequestBody ReviewDTO reviewDTO){
log.info("----------------modify MovieReview-------------"+ reviewnum);
log.info("reviewDTO : "+ reviewDTO);
reviewService.modify(reviewDTO);
return new ResponseEntity<>(reviewnum, HttpStatus.OK);
}
//리뷰 삭제 -> 경로에 변수를 넣고, 리뷰번호를 remove메서드의 파라미터로 전달
@DeleteMapping("/{mno}/{reviewnum}")
public ResponseEntity<Long> removeReview(@PathVariable Long reviewnum){
log.info("--------------modify removeReview--------------");
log.info("reviewnum: "+ reviewnum);
reviewService.remove(reviewnum);
return new ResponseEntity<>(reviewnum, HttpStatus.OK);
}
}
6. Read.html의 리뷰 모달창과 이미지 모달창
- 리뷰 모달창 - 실제 영화 리뷰 처리
- 회원 아이디
- 별점 내용
- 리뷰 내용
- 여러 개의 버튼 존재하고 상황에 따라서 보이거나 사라짐
- 이미지 모달창 - 단순 이미지 확인
- 닫기 버튼 하나 존재
<!-- 리뷰 모달창과 이미지 모달창-->
<div class="reviewModal modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Movie Review</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label >Reviewer ID</label>
<input type="text" class="form-control" name="mid" >
</div>
<div class="form-group">
<label >Grade <span class="grade"></span></label>
<div class='starrr'></div>
</div>
<div class="form-group">
<label >Review Text</label>
<input type="text" class="form-control" name="text" placeholder="Good Movie!" >
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary reviewSaveBtn">Save changes</button>
<button type="button" class="btn btn-warning modifyBtn">Modify </button>
<button type="button" class="btn btn-danger removeBtn">Remove </button>
</div>
</div>
</div>
</div>
<div class="imageModal modal " tabindex="-2" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Picture</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
7. 별점 처리 라이브러리
https://github.com/dobtco/starrr
GitHub - dobtco/starrr: 1-5 star rating, in jQuery.
1-5 star rating, in jQuery. Contribute to dobtco/starrr development by creating an account on GitHub.
github.com
static 폴더에 starrr.js
css 폴더에 starrr.css 파일 추가
파일 링크 추가
<script th:src="@{/starrr.js}"></script>
<link th:href="@{/css/starrr.css}" rel="stylesheet">
<link rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.2.0/css/font-awesome.min.css">
강제로 reviewModal을 화면에 띄우도록 수정
<script>
$(document).ready(function(e) {
var grade = 0;
var mno = [[${dto.mno}]];
$('.starrr').starrr({
rating: grade,
change: function(e, value){
if (value) {
console.log(value);
grade = value;
}
}
});
$(".reviewModal").modal("show"); //미리 보기용
});
</script>
: starrr 라이브러리는 jQuery 플러그인으로, 별점의 값이 변하는 이벤트 처리 가능
-> grade가 해당 별점의 변수로 조회 화면에서 리뷰 입력용 모달창 확인 가능
-> Reviewer ID에 숫자로된 사용자 번호 입력
'Server Programming > Spring Boot Backend Programming' 카테고리의 다른 글
[Spring 부트 - 운동 클럽 프로젝트] 1. 스프링 시큐리티 연동 (1) 기본 설정 (0) | 2022.10.19 |
---|---|
[Spring 부트 - 영화 리뷰 프로젝트] 6. Ajax로 영화 리뷰 처리 (2) 리뷰 등록 / 수정 /삭제 (0) | 2022.10.18 |
[Spring 부트 - 영화 리뷰 프로젝트] 5. 영화 조회 처리 (0) | 2022.10.18 |
[Spring 부트 - 영화 리뷰 프로젝트] 4. 영화 목록 처리 (0) | 2022.10.18 |
[Spring 부트 - 영화 리뷰 프로젝트] 3. 영화 등록 처리 (0) | 2022.10.18 |