728x90
RESTful API를 만들 때 단건의 값을 반환하는 것이 아니라 여러 개의 값들을 반환할 상황이 있다. 이번 프로젝트의 경우 여러번 그런 상황이 있었는데 Spring으로 처음 프로젝트를 진행해보고, 특히, Page를 사용해서 반환하기 때문에 처음에는 약간 막막했다.
DTO와 Builder 패턴을 사용하여 어떻게 List로 반환하는 지 자세한 예시를 통해 알아보도록 하자.
ReviewController
@RestController
@RequiredArgsConstructor
@RequestMapping("/reviews")
public class ReviewController {
private final ReviewService reviewService;
@Operation(summary = "나의 리뷰 목록 조회")
@GetMapping("/myreview")
public Page<MyReviewsRes> getMyReviews(PageReq pageReq) {
Member member = MemberUtils.getAuthMember().getUser(); //로그인 한 회원
Pageable pageable = PageRequest.of(pageReq.getPageNumber(), pageReq.getPageSize()); // 각각 Default 값은 0, 20
MyReviewServiceReq request = MyReviewServiceReq.builder() // MyReviewServiceReq 객체 생성
.memberId(member.getId())
.pageable(pageable)
.build();
return reviewService.getMyReviewsRes(request);
}
}
회원은 여러 이벤트에 대한 리뷰를 작성할 수 있다. 마이페이지에서 회원이 작성한 리뷰 목록을 조회하는 API를 구현할 때 앞서 말한 여러개의 값을 반환해야 했다. 그래서 아래와 같이 구현하였다. 차근 차근 알아보자!
ReviewEventDto
@Getter
public class ReviewEventDto {
private Review review;
private Long eventId;
private String eventStoreName; //Event가 열리는 장소의 이름이다.
@QueryProjection
public ReviewEventDto(Review review, Long eventId, String eventStoreName) {
this.review = review;
this.eventId = eventId;
this.eventStoreName = eventStoreName;
}
}
ReviewRepositoryCustom
public interface ReviewRepositoryCustom {
Page<ReviewEventDto> findWithEventByMemberId(Long memberId, Pageable pageable);
}
ReviewRepositoryImpl
public class ReviewRepositoryImpl implements ReviewRepositoryCustom {
private final JPAQueryFactory queryFactory;
public ReviewRepositoryImpl(EntityManager em) {
this.queryFactory = new JPAQueryFactory(em);
}
@Override
public Page<ReviewEventDto> findWithEventByMemberId(Long memberId, Pageable pageable) {
List<ReviewEventDto> reviews = queryFactory.select(
new QReviewEventDto(
review,
event.id,
event.storeName
))
.from(review)
.leftJoin(review.event, event).fetchJoin()
.where(review.member.id.eq(memberId))
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
JPAQuery<Long> countQuery = queryFactory.select(review.count())
.from(review)
.leftJoin(review.event, event)
.where(review.member.id.eq(memberId));
return PageableExecutionUtils.getPage(reviews, pageable, countQuery::fetchOne);
}
}
ReviewService
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class ReviewService {
private final ReviewRepository reviewRepository;
public Page<MyReviewsRes> getMyReviewsRes(MyReviewServiceReq request) {
Page<ReviewEventDto> myEvents = reviewRepository.findWithEventByMemberId(request.getMemberId(),
request.getPageable());
return myEvents.map(MyReviewsRes::of);
}
}
- 회원 아이디로 Review 엔티티와 Event 일부 정보를 조회
- 반환 값이 Page이기 때문에 ReviewEventDto도 Page로 생성
- myEvents를 MyReviewsRes::of라는 메서드를 이용해 MyReviewsRes 형식으로 매핑
- 아래와 같이 Result 클래스를 별도 선언하여 반환해주면 코드 유연성 ⬆
@Getter
@Builder
public class MyReviewsRes {
private Long id;
private Long eventId;
private String eventStoreName;
private LocalDateTime createdAt;
private int score;
private String reviewImage;
private String content;
public static MyReviewsRes of(ReviewEventDto reviewEventDto) {
Review review = reviewEventDto.getReview();
Long eventId = reviewEventDto.getEventId();
String eventStoreName = reviewEventDto.getEventStoreName();
return MyReviewsRes.builder()
.id(review.getId())
.eventId(eventId)
.eventStoreName(eventStoreName)
.createdAt(review.getCreatedAt())
.score(review.getScore())
.reviewImage(
review.getReviewImages().stream()
.map(ReviewImage::getImage)
.findFirst()
.map(image -> ApiUrl.IMAGE + image)
.orElse(null)
)
.content(review.getContent())
.build();
}
}
728x90
'💻dev > 🌱Java+Spring' 카테고리의 다른 글
Spring Security | PasswordEncoder를 사용하여 회원가입시 비밀번호 암호화 하기 (0) | 2023.08.11 |
---|---|
Spring | @ConfigurationProperties와 @ConfigurationPropertiesScan (0) | 2023.08.11 |
Spring Security | Spring Security의 구조를 이해해보자! (0) | 2023.08.10 |
Spring | @Builder, @NoArgsConstructor 그리고 푸른 수염의 @AllArgsConstructor (0) | 2023.08.09 |
Java | Lombok의 @Builder와 @Builder.Default 알아보기 (0) | 2023.08.09 |