많은 리팩토링과 다양한 기능을 추가해 포스트맨으로 시험해 봤습니다.
우선적으로 이번 프로젝트를 통해서 게시판 기능을 이용한 CRUD에 대해 다잡을 수 있었고,
시큐리티, jwt token에 대해서도 조금 생각해볼 수 있는 계기가 되었습니다.
팀마다 기능을 나누어 구현했기에, 팀과의 협력과 공유하는 것이 얼마나 중요한지에 대해 깨닫게 되었습니다.
리팩토링 전 코드
Entity
Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class Review {
// 아이디
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// 상품 매핑
@JoinColumn(name = "Product_id")
@ManyToOne(fetch = FetchType.LAZY)
@OnDelete(action = OnDeleteAction.CASCADE)
private Product product;
// 평점
@Column(nullable = false)
@Max(100)
@Min(0)
private int rate;
// 리뷰 코멘트
@Column(nullable = false)
private String comment;
// 리뷰생성날짜
@Column(nullable = false)
@CreatedDate
@DateTimeFormat(pattern = "yyyy-mm-dd")
private LocalDate createDate;
@PrePersist
public void createDate() {
this.createDate = LocalDate.now();
}
// 유저 매핑
@JoinColumn(name = "User_id")
@ManyToOne(fetch = FetchType.LAZY)
@OnDelete(action = OnDeleteAction.NO_ACTION)
private User user;
@Builder
public Review(Product product, int rate, LocalDate createDate, String comment,User user)
{
this.product = product;
this.rate = rate;
this.createDate = createDate;
this.comment = comment;
this.user = user;
}
}
ReviewService
@Service
@RequiredArgsConstructor
public class ReviewService {
private ReviewRepository reviewRepository;
private ProductRepository productRepository;
// Read All
@Transactional(readOnly = true)
public List<ReviewResponseDto> getReviews() {
List<Review> reviews = reviewRepository.findAll();
List<ReviewResponseDto> reviewResponseDtos = new ArrayList<>();
for(Review review : reviews) {
reviewResponseDtos.add(ReviewResponseDto.toDto(review));
}
return reviewResponseDtos;
}
@Transactional(readOnly = true)
public Review getReview(Long id){
Review review = reviewRepository.findById(id).orElseThrow(ReviewNotFoundException::new);
return review;
}
// Create
@Transactional
public Review saveReview(ReviewRequestDto reviewRequestDto,User writer) {
Review review = Review.builder()
.comment(reviewRequestDto.getComment())
.createDate(reviewRequestDto.getDate())
.product(reviewRequestDto.getProduct())
.rate(reviewRequestDto.getRate())
.user(writer)
.build();
return reviewRepository.save(review);
}
// Update
@Transactional
public Review updateReview(Long id, Review review) {
// 원래 있던 review 객체 불러옴
Review originalReview = reviewRepository.findById(id).orElseThrow(ReviewNotFoundException::new);
// 오리지날리뷰에 새 리뷰객체의 정보 덮어쓰기
originalReview.setRate(review.getRate());
originalReview.setComment(review.getComment());
originalReview.setCreateDate(review.getCreateDate());
// 저장 후 리턴
return reviewRepository.save(originalReview);
}
// Delete
@Transactional
public void deleteReview(Long id) {
reviewRepository.findById(id).orElseThrow(ReviewNotFoundException::new);
//리뷰 못 찾으면 예외처리
reviewRepository.deleteById(id);
}
}
ReviewController
@RequiredArgsConstructor
@RestController
@RequestMapping("/api")
public class ReviewController {
private ReviewService reviewService;
private UserRepository userRepository;
private ReviewRepository reviewRepository;
@ApiOperation(value = "전체 리뷰 게시글 보기", notes = "전체 리뷰 게시글을 조회합니다.")
@ResponseStatus(HttpStatus.OK)
@GetMapping("/reviews")
public Response findAll() {
return success(reviewService.getReviews());
}
@ApiOperation(value = "리뷰 게시글 일부 보기",notes = "리뷰 게시글 일부를 조회합니다.")
@ResponseStatus(HttpStatus.OK)
@GetMapping("/reviews/find/{id}")
public Response findReview(Long id){
return success(reviewService.getReview(id));
}
@ApiOperation(value = "리뷰 게시글 작성", notes = "리뷰 게시글을 작성합니다.")
@ResponseStatus(HttpStatus.CREATED)
@PostMapping("/reviews/write")
public Response saveReview(@RequestBody ReviewRequestDto reviewRequestDto,Review review) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
User loginUser = userRepository.findByUsername(authentication.getName()).orElseThrow(UserNotFoundException::new);
return success(reviewService.saveReview(reviewRequestDto, loginUser));
}
@ApiOperation(value = "리뷰 게시글 수정", notes = "리뷰 게시글을 수정합니다.")
@ResponseStatus(HttpStatus.OK)
@PutMapping("/reviews/update/{id}")
public Response updateReview(@PathVariable("id") Long id, Review newReview) {
Review oldReview = reviewRepository.findById(id).orElseThrow(ReviewNotFoundException::new);
User writer = oldReview.getUser();
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
User loginUser = userRepository.findByUsername(authentication.getName()).orElseThrow(UserNotFoundException::new);
if(loginUser.equals(writer)){
return success(reviewService.updateReview(id, newReview));
}
else{
throw new UserNotEqualsException();
}
}
@ApiOperation(value = "리뷰 게시글 삭제", notes = "리뷰 게시글을 삭제합니다.")
@ResponseStatus(HttpStatus.OK)
@DeleteMapping("/reviews/delete/{id}")
public Response deleteReview(@PathVariable("id") Long id) {
Review oldReview = reviewRepository.findById(id).orElseThrow(ReviewNotFoundException::new);
User writer = reviewRepository.findByUserId(id).getUser();
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
User loginUser = userRepository.findByUsername(authentication.getName()).orElseThrow(UserNotFoundException::new);
if(loginUser.equals(writer)){
reviewService.deleteReview(id);
return success("삭제 완료");
}
else{
throw new UserNotEqualsException();
}
}
}
ReviewRequestDto
@Data
@AllArgsConstructor
public class ReviewRequestDto {
@NotNull(message = "평점을 입력해주세요")
private int rate;
@NotBlank(message = "리뷰를 입력해주세요.")
private String comment;
}
ReviewResponseDto
Data
public class ReviewResponseDto {
@NotBlank
private String buyerName;
@NotNull
private int rate;
@NotBlank
private String sellerName;
@NotBlank
private LocalDate date;
@NotBlank(message = "제품명을 입력해주세요.")
private String productName;
@NotBlank(message = "리뷰를 입력해주세요.")
private String comment;
// Constructor
public ReviewResponseDto(String buyerName, int rate, String sellerName, LocalDate date, String productName, String comment) {
this.buyerName = buyerName;
this.rate = rate;
this.sellerName = sellerName;
this.date = date;
this.productName = productName;
this.comment = comment;
}
// toDto
public static ReviewResponseDto toDto(Review review) {
return new ReviewResponseDto(
review.getUser().getUsername(),
review.getRate(),
review.getProduct().getSeller().getUsername(),
review.getCreateDate(),
review.getProduct().getProductName(),
review.getComment()
);
}
}
리뷰 기능들에 대한 url
/join => 회원가입
/login => 로그인
/users/myPage =>마이페이지
전체 리뷰 조회:/api/reviews/
일부 리뷰 조회:/api/reviews/find/{id}
리뷰 작성 :/api/reviews/write
리뷰 수정:/api/reviews/update/{id}
리뷰 삭제:/api/reviews/delete/{id}
이와 같이, 코드를 작성해보았는데 한 가지 간과한 사실을 발견했습니다.
어떤 제품에 대한 리뷰인가에 대해서는 전혀 생각하지 않고, 단편적으로 리뷰를 작성하고, 수정하고 조회하는 기능만 구현했습니다. 그래서 추가된 방법이 제품에 대한 리뷰를 작성, 수정 및 조회하는 기능을 구현하는데 초점을 두었습니다.
수정된 url코드
회원가입:/join
로그인:/login
마이페이지:/users/myPage
전체 리뷰 조회:/product/{productId}/reviews
단건 리뷰조회:/product/{productId}/reviews/{reviewId}
리뷰 작성:/product/{productId}/reviews/write
리뷰 수정:/product/{productId}/reviews/update/{reviewId}
리뷰 삭제:/product/{productId}/reviews/delete/{reviewId}
사용자명을 통한 리뷰 조회 : /products/{productId}/reviews/byUser/{username}
리뷰내용을 통한 리뷰 조회 : /products/{productId}/reviews/byComment/{Comment}
'Java > Spring' 카테고리의 다른 글
스프링 프로젝트 코드 리뷰 (1) | 2022.08.23 |
---|---|
스프링부트 프로젝트 최종 결과물 (0) | 2022.08.22 |
Junit5을 이용한 테스트코드 작성(단위 테스트 코드) (0) | 2022.08.15 |
SpringBoot JPA(Java Persistence API) 사용 목적 (0) | 2022.08.05 |
DTO, DAO, Repository, Entity 개념 (0) | 2022.08.04 |