Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

경매 신고 등록 및 조회 api 추가 #238

Merged
merged 27 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
71304bf
feat: 경매 신고 엔티티 추가
JJ503 Aug 8, 2023
fd691e4
feat: 경매 신고 레포지토리 추가
JJ503 Aug 8, 2023
2f8e0c4
feat: 경매 신고 등록 서비스 추가
JJ503 Aug 8, 2023
0f701f3
feat: 경매 신고 등록 api 추가
JJ503 Aug 8, 2023
30b8d4a
feat: 경매 신고 중복 등록 검증 추가
JJ503 Aug 8, 2023
9f6e38d
refactor: 메서드 호출 순서로 변경
JJ503 Aug 8, 2023
7eb3c1d
feat: 경매 신고 전체 조회 레포지토리 추가
JJ503 Aug 8, 2023
356a320
feat: 경매 신고 전체 조회 서비스 추가
JJ503 Aug 8, 2023
f86ee9a
feat: 경매 신고 전체 조회 api 추가
JJ503 Aug 8, 2023
2a8ea44
feat: 경매 신고 테이블 생성에 대한 flyway 스크립트 추가
JJ503 Aug 8, 2023
b99dafe
Merge remote-tracking branch 'origin/develop-be' into feature/226
JJ503 Aug 8, 2023
4c3797a
refator: toString 항목 추가
JJ503 Aug 8, 2023
5b44a01
rename: flyway 스크립트 네이밍 수정
JJ503 Aug 9, 2023
e3fb3a3
Merge remote-tracking branch 'origin/develop-be' into feature/226
JJ503 Aug 9, 2023
aec9a3a
test: merge에 따른 테스트 코드 수정
JJ503 Aug 9, 2023
84c67b1
refactor: querydsl을 EntityGraph로 수정
JJ503 Aug 9, 2023
9e8de4b
test: import 와일드카드 제거
JJ503 Aug 9, 2023
2730b0f
refactor: final 추가
JJ503 Aug 9, 2023
0ad9e12
style: 개행 추가
JJ503 Aug 9, 2023
c7b2b86
test: given-when-then 주석 수정
JJ503 Aug 9, 2023
27940a5
refactor: 메서드명 수정
JJ503 Aug 9, 2023
8e99dbb
refactor: transactional 어노테이션 추가
JJ503 Aug 9, 2023
68d9765
Merge branch 'develop-be' into feature/226
JJ503 Aug 9, 2023
14d08b8
rename: 동일 클래스가 존재해 헷갈리는 dto 네이밍 수정
JJ503 Aug 10, 2023
7105a4f
rename: dto 패키지 분리
JJ503 Aug 10, 2023
3039ebe
Merge remote-tracking branch 'origin/develop-be' into feature/226
JJ503 Aug 10, 2023
7c4212f
refactor: 머지에 따른 인증 관련 코드 수정
JJ503 Aug 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,28 +33,18 @@ public class BidService {

@Transactional
public Long create(final LoginUserDto userDto, final CreateBidDto bidDto) {
final Auction auction = auctionRepository.findById(bidDto.auctionId())
.orElseThrow(() -> new AuctionNotFoundException("해당 경매를 찾을 수 없습니다."));
checkInvalidAuction(auction);

// TODO: 2023/07/28 추후 User 패키지 내에 UserNotFoundException이 생긴다면 해당 예외를 사용하도록 수정 하겠습니다.
final User bidder = userRepository.findById(userDto.usedId())
.orElseThrow(() -> new UserNotFoundException("해당 사용자를 찾을 수 없습니다."));
final Auction auction = auctionRepository.findById(bidDto.auctionId())
.orElseThrow(() -> new AuctionNotFoundException("해당 경매를 찾을 수 없습니다."));
checkInvalidAuction(auction);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 로직과 동일한 로직이 chatRoomService에도 존재합니다!
이 메서드를 Auction으로 옮기면 어떤지에 대해 엔초 PR에 코멘트를 남겼었는데,
Auction에 해당 메서드를 옮기고 bidService와 chatRoomService에서 Auction의 메서드를 사용하는 것은 어떤지 건의드려봅니다~!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

도메인으로 넣자는 이야기가 맞을까요?
맞다면 개인적으로 도메인에서 예외를 관리하는 게 좋을지 아직은 잘 판단이 되지 않습니다.
또한, 항상 검증에 대한 로직이 동일할 것이라 생각하지 않습니다.
예를 들어 AuctionReportService에서는 삭제된 경매에 대해서만 예외를 발생시키고 종료된 경매에 대해서는 정상 처리하게 됩니다. (물론 종료에 대해서도 예외가 필요하단 의견이 있다면 수정되겠지만요!)
그러면 어떤 건 auction에서 어떤 건 각 서비스에서 검증을 하게 되는데 그때의 기준이 모호해지지 않을까 하는 걱정이 있습니다.
이에 대한 메리 혹은 다른 분들의 의견이 궁금합니다!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저는 해당 분기문이 경매에 대한 상태를 조회한다고 생각했습니다
그리고 언제 경매의 상태가 변경되는지는 도메인 로직이라고 생각해서 도메인에 들어가면 자연스러울 것 같다고 생각했어요
그런데 말씀해주신 것처럼 서비스와 도메인으로 예외 검증 위치가 분산된다는 점이 가독성을 해칠 수도 있을 것 같다는 의견에 동의합니다!
다른 분들의 의견도 궁금합니당

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

서비스마다 검증 내부 로직이 다를 수 있기 때문에 검증에 대한 처리를 auction에 넣는 것에 대해서는 지금처럼 서비스에서 하는 것이 좋다고 생각합니다.
메리가 저한테 리뷰 남겨주신 것은 경매의 상태를 판단해서 enum으로 반환하는 메서드를 경매 엔티티에 넣자는 의견이었던 것 같은데 그 부분에 대해서 다른 분들(@apptie @JJ503)은 어떻게 생각하시나요?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kwonyj1022 그거 관련 이슈..가 있기는 했는데 까먹고...방치한지 오래되기는 했네요..
enum으로 반환해주기는 해야하는데...

checkInvalidBid(auction, bidder, bidDto);

final Bid saveBid = saveBid(bidDto, auction, bidder);
return saveBid.getId();
}

private Bid saveBid(final CreateBidDto bidDto, final Auction auction, final User bidder) {
final Bid createBid = bidDto.toEntity(auction, bidder);
final Bid saveBid = bidRepository.save(createBid);

auction.updateLastBid(saveBid);

return saveBid;
}

private void checkInvalidAuction(final Auction auction) {
final LocalDateTime now = LocalDateTime.now();
if (auction.isClosed(now)) {
Expand All @@ -80,6 +70,14 @@ private void checkInvalidBid(final Auction auction, final User bidder, final Cre
checkInvalidBidPrice(lastBid, bidPrice);
}

private BidPrice processBidPrice(final int value) {
try {
return new BidPrice(value);
} catch (final InvalidBidPriceException ex) {
throw new InvalidBidPriceException("입찰 금액이 잘못되었습니다");
}
}

private void checkIsSeller(final Auction auction, final User bidder) {
if (auction.isOwner(bidder)) {
throw new InvalidBidderException("판매자는 입찰할 수 없습니다");
Expand All @@ -104,12 +102,13 @@ private void checkInvalidBidPrice(final Bid lastBid, final BidPrice bidPrice) {
}
}

private BidPrice processBidPrice(final int value) {
try {
return new BidPrice(value);
} catch (final InvalidBidPriceException ex) {
throw new InvalidBidPriceException("입찰 금액이 잘못되었습니다");
}
private Bid saveBid(final CreateBidDto bidDto, final Auction auction, final User bidder) {
final Bid createBid = bidDto.toEntity(auction, bidder);
final Bid saveBid = bidRepository.save(createBid);

auction.updateLastBid(saveBid);

return saveBid;
}

public List<ReadBidDto> readAllByAuctionId(final Long auctionId) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
import com.ddang.ddang.image.infrastructure.local.exception.StoreImageFailureException;
import com.ddang.ddang.image.infrastructure.local.exception.UnsupportedImageFileExtensionException;
import com.ddang.ddang.region.application.exception.RegionNotFoundException;
import com.ddang.ddang.report.application.exception.AlreadyReportAuctionException;
import com.ddang.ddang.report.application.exception.InvalidReportAuctionException;
import com.ddang.ddang.report.application.exception.InvalidReporterToAuctionException;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
Expand Down Expand Up @@ -175,6 +178,30 @@ public ResponseEntity<ExceptionResponse> handleUserNotAccessibleException(
.body(new ExceptionResponse(ex.getMessage()));
}

@ExceptionHandler(InvalidReporterToAuctionException.class)
public ResponseEntity<ExceptionResponse> handleInvalidReporterToAuctionException(final InvalidReporterToAuctionException ex) {
logger.warn(String.format(EXCEPTION_FORMAT, InvalidReporterToAuctionException.class), ex);

return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ExceptionResponse(ex.getMessage()));
}

@ExceptionHandler(InvalidReportAuctionException.class)
public ResponseEntity<ExceptionResponse> handleInvalidReportAuctionException(final InvalidReportAuctionException ex) {
logger.warn(String.format(EXCEPTION_FORMAT, InvalidReportAuctionException.class), ex);

return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ExceptionResponse(ex.getMessage()));
}

@ExceptionHandler(AlreadyReportAuctionException.class)
public ResponseEntity<ExceptionResponse> handleAlreadyReportAuctionException(final AlreadyReportAuctionException ex) {
logger.warn(String.format(EXCEPTION_FORMAT, AlreadyReportAuctionException.class), ex);

return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body(new ExceptionResponse(ex.getMessage()));
}

@Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(
final MethodArgumentNotValidException ex,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.ddang.ddang.report.application;

import com.ddang.ddang.auction.application.exception.AuctionNotFoundException;
import com.ddang.ddang.auction.domain.Auction;
import com.ddang.ddang.auction.infrastructure.persistence.JpaAuctionRepository;
import com.ddang.ddang.bid.application.dto.LoginUserDto;
import com.ddang.ddang.bid.application.exception.UserNotFoundException;
import com.ddang.ddang.report.application.dto.CreateAuctionReportDto;
import com.ddang.ddang.report.application.dto.ReadAuctionReportDto;
import com.ddang.ddang.report.application.exception.AlreadyReportAuctionException;
import com.ddang.ddang.report.application.exception.InvalidReportAuctionException;
import com.ddang.ddang.report.application.exception.InvalidReporterToAuctionException;
import com.ddang.ddang.report.domain.AuctionReport;
import com.ddang.ddang.report.infrastructure.persistence.JpaAuctionReportRepository;
import com.ddang.ddang.user.domain.User;
import com.ddang.ddang.user.infrastructure.persistence.JpaUserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class AuctionReportService {

private final JpaAuctionRepository auctionRepository;
private final JpaUserRepository userRepository;
private final JpaAuctionReportRepository auctionReportRepository;

public Long create(final LoginUserDto userDto, final CreateAuctionReportDto auctionReportDto) {
// TODO: 2023/08/08 추후 User 패키지 내에 UserNotFoundException이 생긴다면 해당 예외를 사용하도록 수정 하겠습니다.
final User reporter = userRepository.findById(userDto.usedId())
.orElseThrow(() -> new UserNotFoundException("해당 사용자를 찾을 수 없습니다."));
final Auction auction = auctionRepository.findById(auctionReportDto.auctionId())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

지토, 엔초 코드에서는 레포지토리 조회 로직을 findXX()와 같은 형태로 분리해주었는데, 이 부분도 고려해보시면 좋을 것 같습니다!
이 부분은 제이미 판단에 맡기겠습니다~!

Copy link
Member Author

@JJ503 JJ503 Aug 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

find~()의 경우 코드가 중복되면 메서드로 분리하는 것으로 이해해 저는 굳이 분리하지 않았습니다.
아마 신고 관련 코드가 추가되어 repoterauctionfind()해야 하는 경우가 늘어나면 분리할 것 같습니다.

.orElseThrow(() -> new AuctionNotFoundException("해당 경매를 찾을 수 없습니다."));
invalidReportAuction(reporter, auction);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

entity 이름이 AuctionReport이니까 메서드 이름도 invalidAuctionReport로 하는건 어떤가요?
또 예외 케이스를 체크하는 것이므로 "check" 를 앞에 붙여주는 것도 좋을 것 같습니다. checkInvalidAuctionReport 이런 식으로요.
메서드가 길어서 별로라면 validateAuctionReport 같은 것도 제안드립니다.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

통일성을 위해 checkInvalid~로 수정하도록 하겠습니다!


final AuctionReport auctionReport = auctionReportDto.toEntity(reporter, auction);
return auctionReportRepository.save(auctionReport)
.getId();
}

private void invalidReportAuction(final User reporter, final Auction auction) {
if (auction.isOwner(reporter)) {
throw new InvalidReporterToAuctionException("본인 경매글입니다.");
}
if (auction.isDeleted()) {
throw new InvalidReportAuctionException("이미 삭제된 경매입니다.");
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위에서 건의드린 내용과 비슷한 맥락으로 위의 두 분기문을 Auction으로 옮겨서
isAbailableToCreateReportAuction()으로 하면 어떤지 의견 남겨봅니다!
이것도 제이미의 판단에 맡기고싶고, 제이미의 의견도 궁금합니다~!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 의견에 대해 위 코멘트에 댓글 달아두었습니다!

if (auctionReportRepository.existsByAuctionIdAndReporterId(auction.getId(), reporter.getId())) {
throw new AlreadyReportAuctionException("이미 신고한 경매입니다.");
}
}

public List<ReadAuctionReportDto> readAll() {
final List<AuctionReport> auctionReports = auctionReportRepository.findAll();
return auctionReports.stream()
.map(ReadAuctionReportDto::from)
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.ddang.ddang.report.application.dto;

import com.ddang.ddang.auction.domain.Auction;
import com.ddang.ddang.report.domain.AuctionReport;
import com.ddang.ddang.report.presentation.dto.CreateAuctionReportRequest;
import com.ddang.ddang.user.domain.User;

public record CreateAuctionReportDto(Long auctionId, String description) {

public static CreateAuctionReportDto from(final CreateAuctionReportRequest auctionReportRequest) {
return new CreateAuctionReportDto(auctionReportRequest.auctionId(), auctionReportRequest.description());
}

public AuctionReport toEntity(final User reporter, final Auction auction) {
return new AuctionReport(reporter, auction, this.description);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.ddang.ddang.report.application.dto;

import com.ddang.ddang.auction.domain.Auction;

import java.time.LocalDateTime;

public record ReadAuctionDto(
Long id,
String title,
String description,
int bidUnit,
int startPrice,
boolean deleted,
LocalDateTime closingTime,
int auctioneerCount
) {

public static ReadAuctionDto from(final Auction auction) {
return new ReadAuctionDto(
auction.getId(),
auction.getTitle(),
auction.getDescription(),
auction.getBidUnit().getValue(),
auction.getStartPrice().getValue(),
auction.isDeleted(),
auction.getClosingTime(),
auction.getAuctioneerCount()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.ddang.ddang.report.application.dto;

import com.ddang.ddang.report.domain.AuctionReport;

import java.time.LocalDateTime;

public record ReadAuctionReportDto(
Long id,
ReadReporterDto reporterDto,
LocalDateTime createdTime,
ReadAuctionDto auctionDto,
String description
) {

// TODO: 2023/08/08 [고민] ReadAuctionDto 클래스가 같은 이름으로 여러 패키지에 존재함. 이때, import를 할 때 헷갈리거나 실수하지는 않을까요?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 부분은 충분히 혼동될 수 있을 것 같습니다
특정 패키지 내부에 있는 ReadAuctionDto는 좀 특화해서 네이밍을 짓던가 해야겠네요

public static ReadAuctionReportDto from(AuctionReport auctionReport) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ㅍ..파이널을 발견해버렸습니다..

Copy link
Member Author

@JJ503 JJ503 Aug 9, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

으아니...! final 실수가 있었을 줄이야... (이마짚)
메리짱👍

return new ReadAuctionReportDto(
auctionReport.getId(),
ReadReporterDto.from(auctionReport.getReporter()),
auctionReport.getCreatedTime(),
ReadAuctionDto.from(auctionReport.getAuction()),
auctionReport.getDescription()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.ddang.ddang.report.application.dto;

import com.ddang.ddang.user.domain.User;

public record ReadReporterDto(Long id, String name, String profileImage, double reliability) {

public static ReadReporterDto from(final User reporter) {
return new ReadReporterDto(
reporter.getId(),
reporter.getName(),
reporter.getProfileImage(),
reporter.getReliability()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.ddang.ddang.report.application.exception;

public class AlreadyReportAuctionException extends IllegalArgumentException {

public AlreadyReportAuctionException(final String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.ddang.ddang.report.application.exception;

public class InvalidReportAuctionException extends IllegalArgumentException {
public InvalidReportAuctionException(final String message) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

클래스와 생성자 사이 개행 추가 부탁드립니당

super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.ddang.ddang.report.application.exception;

public class InvalidReporterToAuctionException extends IllegalArgumentException {
public InvalidReporterToAuctionException(final String message) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

클래스와 생성자 사이 개행 추가 부탁드립니당 22

super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.ddang.ddang.report.domain;

import com.ddang.ddang.auction.domain.Auction;
import com.ddang.ddang.common.entity.BaseCreateTimeEntity;
import com.ddang.ddang.user.domain.User;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.ForeignKey;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@EqualsAndHashCode(of = "id")
@ToString(of = {"id"})
public class AuctionReport extends BaseCreateTimeEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "reporter_id", foreignKey = @ForeignKey(name = "fk_auction_report_reporter"))
private User reporter;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "auction_id", foreignKey = @ForeignKey(name = "fk_auction_report_auction"))
private Auction auction;

@Column(columnDefinition = "text")
private String description;

public AuctionReport(final User reporter, final Auction auction, final String description) {
this.reporter = reporter;
this.auction = auction;
this.description = description;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.ddang.ddang.report.infrastructure.persistence;

import com.ddang.ddang.report.domain.AuctionReport;
import org.springframework.data.jpa.repository.JpaRepository;

public interface JpaAuctionReportRepository extends JpaRepository<AuctionReport, Long>, QuerydslAuctionReportRepository {

boolean existsByAuctionIdAndReporterId(final Long auctionId, final Long reporterId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.ddang.ddang.report.infrastructure.persistence;

import com.ddang.ddang.report.domain.AuctionReport;

import java.util.List;

public interface QuerydslAuctionReportRepository {

List<AuctionReport> findAll();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.ddang.ddang.report.infrastructure.persistence;

import com.ddang.ddang.report.domain.AuctionReport;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

import java.util.List;

import static com.ddang.ddang.report.domain.QAuctionReport.auctionReport;

@Repository
@RequiredArgsConstructor
public class QuerydslAuctionReportRepositoryImpl implements QuerydslAuctionReportRepository {

private final JPAQueryFactory queryFactory;

@Override
public List<AuctionReport> findAll() {
return queryFactory.selectFrom(auctionReport)
.leftJoin(auctionReport.reporter).fetchJoin()
.leftJoin(auctionReport.auction).fetchJoin()
.fetch();
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 메서드는 그렇게 복잡하지 않아 EntityGraph나 JPQL로도 처리할 수 있을 것 같은데 Querydsl로 하신 의도가 무엇인지 궁금합니다

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 궁금합니다!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

개인적으로 JPQL은 join이 두 개이상부 터는 보기 싫더라고요...
그래서 JPQL은 사용하지 않았습니다.
그리고 EntityGraph의 경우 아직 사용해 본 적이 없어 고려하지 못했네요.
해당 부분은 EntityGraph로 수정했습니다.
좋은 의견 감사합니다!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

보기 싫은건 공감이 되기는 하네요..

}
Loading