Java/Spring

스프링 프로젝트 리팩토링 전 코드(review)

SeungbeomKim 2022. 8. 22. 00:28

많은 리팩토링과 다양한 기능을 추가해 포스트맨으로 시험해 봤습니다.

우선적으로 이번 프로젝트를 통해서 게시판 기능을 이용한 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}