[ 작업한 내용 ]
< 사용자: 책 리뷰 작성 >
1. Review
- Review 엔티티 작성
- id, user, book, content, createdDAte, updatedDate 필드
- user와 book 필드는 각각 User, Book 엔티티와 다대일(@ManyToOne) 연관관계를 가진다.
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "review")
public class Review {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(optional = false)
@JoinColumn(name = "user_id")
private User user;
@ManyToOne(optional = false)
@JoinColumn(name = "book_id")
private Book book;
private String content;
private LocalDateTime createdDate;
private LocalDateTime updatedDate;
@PrePersist
public void prePersist() {
this.createdDate = LocalDateTime.now();
}
}
2. UserReviewController
- POST /api/v1/user/books/{bookId}/reviews 로 요청받는다.
- 파라미터는 bookId, UserReviewCreateReqDTO, @AuthenticationPrincipal User 이다.
- Service 계층으로 bookId, UserReviewCreateReqDTO, user.getId()를 전달한다.
- 반환받은 결과는 UserReviewCreateResDTO 타입으로 저장한다.
- 클라이언트 측으로 반환은 성공 메시지와 결과를 ApiResponse.success()로 감싸어 반환한다.
// 사용자: 책 리뷰 작성
@PostMapping("/books/{bookId}/reviews")
public ResponseEntity<?> createReview(
@PathVariable("bookId") Long bookId,
@RequestBody UserReviewCreateReqDTO requestDTO,
@AuthenticationPrincipal User user) {
// 서비스로직
UserReviewCreateResDTO responseDTO = userReviewService.createReview(bookId, requestDTO, user.getId());
// 성공메시지
String message = messageProvider.getMessage(ReviewSuccessCode.REVIEW_CREATED.getMessage());
// 응답
return ResponseEntity
.status(ReviewSuccessCode.REVIEW_CREATED.getHttpStatus())
.body(ApiResponse.success(
ReviewSuccessCode.REVIEW_CREATED,
message,
responseDTO));
}
3. UserReviewService
- userRepository.findById(userId)로 사용자 유효 검사를 하고 유효하지 않은 사용자면 UserErrorCode.USER_NOT_FOUND 예외를 던진다.
- bookRepository.findById(bookId)로 도서 유효 검사를 하고 유효하지 않은 책이라면 BookErrorCode.BOOK_NOT_FOUND 예외를 던진다.
- 사용자는 도서 하나에 리뷰 하나만 작성이 가능하기 때문에 userId와 bookId로 리뷰 유효 검사를 하고, boolean 형태로 결과를 받는다. 만약에 해당 도서에 리뷰를 이미 작성했다면 ReviewErrorCode.REVIEW_ALREADY_CREATED 예외를 발생시킨다.
// 사용자: 책 리뷰 작성
@Transactional
public UserReviewCreateResDTO createReview(Long bookId, UserReviewCreateReqDTO requestDTO, Long userId) {
// 사용자 유효 검사 + 예외
User user = userRepository.findById(userId)
.orElseThrow(() -> new BaseException(UserErrorCode.USER_NOT_FOUND));
// 책 유효 검사 + 예외
Book book = bookRepository.findById(bookId)
.orElseThrow(() -> new BaseException(BookErrorCode.BOOK_NOT_FOUND));
// 리뷰 유효 검사
// 사용자는 도서 하나에 리뷰 하나만 작성 가능
if (reviewRepository.existsByUser_IdAndBook_Id(user.getId(), book.getId())) {
throw new BaseException(ReviewErrorCode.REVIEW_ALREADY_CREATED);
}
// Review 엔티티 객체 생성
Review review = Review.builder()
.user(user)
.book(book)
.content(requestDTO.getContent())
.build();
// DB에 저장
reviewRepository.save(review);
// 반환
return new UserReviewCreateResDTO(review);
}
4. ReviewRepository
- JpaRepository를 상속받는 인터페이스입니다.
- JpaRepository<Review, Long> 형태로 선언하여 Review 엔티티를 관리하며, PK 타입은 Long입니다.
- userId와 bookId로 조회하여 boolean타입으로 값을 받는다.
public interface ReviewRepository extends JpaRepository<Review, Long> {
boolean existsByUser_IdAndBook_Id(Long userId, Long bookId);
}
5. messages.properties
- 책 리뷰 관련 성공/에러 메시지
error.review.already_created=이미 해당 도서에 리뷰를 작성하셨습니다.
success.review.created=리뷰가 성공적으로 작성되었습니다.
6. ReviewSuccessCode
- 책 리뷰 작성 성공 코드 정의
REVIEW_CREATED(HttpStatus.CREATED, "REVIEW_200", "success.review.created");
7. ReviewErrorCode
- 책 리뷰 작성 에러 코드 정의
REVIEW_ALREADY_CREATED(HttpStatus.BAD_REQUEST, "REVIEW_401", "error.review.already_created");
8. UserReviewCreateReqDTO
- 책 리뷰 작성 요청 DTO
- content 필드
@Getter
public class UserReviewCreateReqDTO {
private String content;
}
9. UserReviewCreateResDTO
- 책 리뷰 작성 응답 DTO
- id, bookId, bookTitle, userId, username, createdDate 필드
- Review 엔티티를 파라미터로 받는 UserReviewCreateResDTO 생성자함수 정의
@Getter
public class UserReviewCreateResDTO {
private Long id;
private Long bookId;
private String bookTitle;
private Long userId;
private String username;
private LocalDateTime createdDate;
public UserReviewCreateResDTO(Review review) {
this.id = review.getId();
this.bookId = review.getBook().getId();
this.bookTitle = review.getBook().getTitle();
this.userId = review.getUser().getId();
this.username = review.getUser().getUsername();
this.createdDate = review.getCreatedDate();
}
}
10. SecurityConfig
- 원활한 개발을 위해 .permitAll() 설정에 책 리뷰 작성 uri 추가
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/v1/user/books/**").permitAll()
.anyRequest().authenticated())
'개발 기록 > 도서관 관리 시스템' 카테고리의 다른 글
[Library Management System] 25.09.18 (49일) (0) | 2025.09.18 |
---|---|
[Library Management System] 25.09.17 (48일) (0) | 2025.09.17 |
[Library Management System] 25.09.12 (45일) (0) | 2025.09.12 |
[Library Management System] 25.09.11 (44일) (0) | 2025.09.11 |
[Library Management System] 25.09.10 (43일) (0) | 2025.09.10 |