From d7fd616cd7fd8b48445ada5832c064b9bd5d083c Mon Sep 17 00:00:00 2001 From: SCY Date: Thu, 25 Jul 2024 16:55:50 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=A0=84=EB=B0=98=EC=A0=81=EC=9D=B8=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC=20(#103)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 예외 처리 핸들러 추가 * feat: Offering 예외 처리 코드 추가 * feat: Comment 예외 처리 코드 추가 * feat: Member 예외 처리 코드 추가 * feat: OfferingMember 예외 처리 코드 추가 * feat: Offering 예외 처리 상세 코드 추가 * feat: 에러 코드 적용 * feat: 도메인 검증 로직 * feat: DTO 검증 로직 --------- Co-authored-by: masonkimseoul --- .../comment/exception/CommentErrorCode.java | 24 +++++++++++++++++ .../repository/entity/CommentEntity.java | 2 ++ .../comment/service/CommentService.java | 16 ++++++----- .../service/dto/CommentSaveRequest.java | 14 +++++++++- .../global/exception/ErrorMessage.java | 4 +++ .../global/exception/ErrorResponse.java | 10 +++++++ .../exception/GlobalExceptionHandler.java | 17 ++++++++++++ .../global/exception/MarketException.java | 11 ++++++++ .../member/exception/MemberErrorCode.java | 24 +++++++++++++++++ .../repository/entity/MemberEntity.java | 2 +- .../offering/exception/OfferingErrorCode.java | 27 +++++++++++++++++++ .../repository/entity/OfferingEntity.java | 5 ++++ .../offering/service/OfferingService.java | 9 ++++--- .../exception/OfferingMemberErrorCode.java | 24 +++++++++++++++++ .../service/OfferingMemberService.java | 12 ++++++--- .../service/dto/ParticipationRequest.java | 8 +++++- 16 files changed, 193 insertions(+), 16 deletions(-) create mode 100644 backend/src/main/java/com/zzang/chongdae/comment/exception/CommentErrorCode.java create mode 100644 backend/src/main/java/com/zzang/chongdae/global/exception/ErrorMessage.java create mode 100644 backend/src/main/java/com/zzang/chongdae/global/exception/ErrorResponse.java create mode 100644 backend/src/main/java/com/zzang/chongdae/global/exception/GlobalExceptionHandler.java create mode 100644 backend/src/main/java/com/zzang/chongdae/global/exception/MarketException.java create mode 100644 backend/src/main/java/com/zzang/chongdae/member/exception/MemberErrorCode.java create mode 100644 backend/src/main/java/com/zzang/chongdae/offering/exception/OfferingErrorCode.java create mode 100644 backend/src/main/java/com/zzang/chongdae/offeringmember/exception/OfferingMemberErrorCode.java diff --git a/backend/src/main/java/com/zzang/chongdae/comment/exception/CommentErrorCode.java b/backend/src/main/java/com/zzang/chongdae/comment/exception/CommentErrorCode.java new file mode 100644 index 000000000..7d7bfe23f --- /dev/null +++ b/backend/src/main/java/com/zzang/chongdae/comment/exception/CommentErrorCode.java @@ -0,0 +1,24 @@ +package com.zzang.chongdae.comment.exception; + +import static org.springframework.http.HttpStatus.BAD_REQUEST; + +import com.zzang.chongdae.global.exception.ErrorMessage; +import com.zzang.chongdae.global.exception.ErrorResponse; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum CommentErrorCode implements ErrorResponse { + + NOT_FOUND(BAD_REQUEST, "해당 댓글이 존재하지 않습니다."); + + private final HttpStatus status; + private final String message; + + @Override + public ErrorMessage getErrorMessage() { + return new ErrorMessage(this.message); + } +} diff --git a/backend/src/main/java/com/zzang/chongdae/comment/repository/entity/CommentEntity.java b/backend/src/main/java/com/zzang/chongdae/comment/repository/entity/CommentEntity.java index c51a4326a..28d48244b 100644 --- a/backend/src/main/java/com/zzang/chongdae/comment/repository/entity/CommentEntity.java +++ b/backend/src/main/java/com/zzang/chongdae/comment/repository/entity/CommentEntity.java @@ -10,6 +10,7 @@ import jakarta.persistence.Id; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; +import jakarta.validation.constraints.NotNull; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; @@ -34,6 +35,7 @@ public class CommentEntity extends BaseTimeEntity { @ManyToOne private OfferingEntity offering; + @NotNull @Column(length = 80) private String content; diff --git a/backend/src/main/java/com/zzang/chongdae/comment/service/CommentService.java b/backend/src/main/java/com/zzang/chongdae/comment/service/CommentService.java index c15621b06..0ceba30fb 100644 --- a/backend/src/main/java/com/zzang/chongdae/comment/service/CommentService.java +++ b/backend/src/main/java/com/zzang/chongdae/comment/service/CommentService.java @@ -1,6 +1,7 @@ package com.zzang.chongdae.comment.service; import com.zzang.chongdae.comment.domain.CommentWithRole; +import com.zzang.chongdae.comment.exception.CommentErrorCode; import com.zzang.chongdae.comment.repository.CommentRepository; import com.zzang.chongdae.comment.repository.entity.CommentEntity; import com.zzang.chongdae.comment.service.dto.CommentAllResponse; @@ -10,9 +11,12 @@ import com.zzang.chongdae.comment.service.dto.CommentRoomAllResponseItem; import com.zzang.chongdae.comment.service.dto.CommentSaveRequest; import com.zzang.chongdae.comment.service.dto.CommentLatestResponse; +import com.zzang.chongdae.global.exception.MarketException; +import com.zzang.chongdae.member.exception.MemberErrorCode; import com.zzang.chongdae.member.repository.MemberRepository; import com.zzang.chongdae.member.repository.entity.MemberEntity; import com.zzang.chongdae.offering.domain.OfferingWithRole; +import com.zzang.chongdae.offering.exception.OfferingErrorCode; import com.zzang.chongdae.offering.repository.OfferingRepository; import com.zzang.chongdae.offering.repository.entity.OfferingEntity; import com.zzang.chongdae.offeringmember.domain.OfferingMemberRole; @@ -30,9 +34,9 @@ public class CommentService { public void saveComment(CommentSaveRequest request) { MemberEntity loginMember = memberRepository.findById(request.memberId()) - .orElseThrow(); // TODO: 예외처리 하기 + .orElseThrow(() -> new MarketException(MemberErrorCode.NOT_FOUND)); OfferingEntity offering = offeringRepository.findById(request.offeringId()) - .orElseThrow();// TODO: 예외처리 하기 + .orElseThrow(() -> new MarketException(OfferingErrorCode.NOT_FOUND)); CommentEntity comment = new CommentEntity(loginMember, offering, request.content()); commentRepository.save(comment); @@ -40,7 +44,7 @@ public void saveComment(CommentSaveRequest request) { public CommentRoomAllResponse getAllCommentRoom(Long loginMemberId) { MemberEntity loginMember = memberRepository.findById(loginMemberId) - .orElseThrow(); // TODO: 예외처리 하기 + .orElseThrow(() -> new MarketException(MemberErrorCode.NOT_FOUND)); List offeringsWithRole = offeringRepository.findAllWithRoleByMember(loginMember); List responseItems = offeringsWithRole.stream() @@ -59,7 +63,7 @@ private CommentRoomAllResponseItem toCommentRoomAllResponseItem(OfferingWithRole OfferingEntity offering = offeringWithRole.getOffering(); OfferingMemberRole role = offeringWithRole.getRole(); CommentEntity latestComment = commentRepository.findTopByOfferingOrderByCreatedAtDesc(offering) - .orElseThrow(); // TODO: 예외처리 하기 + .orElseThrow(() -> new MarketException(CommentErrorCode.NOT_FOUND)); return new CommentRoomAllResponseItem( offering.getId(), offering.getTitle(), @@ -70,7 +74,7 @@ private CommentRoomAllResponseItem toCommentRoomAllResponseItem(OfferingWithRole public CommentAllResponse getAllComment(Long offeringId, Long loginMemberId) { validateMemberExistence(loginMemberId); OfferingEntity offering = offeringRepository.findById(offeringId) - .orElseThrow(); // TODO: 예외 처리하기 + .orElseThrow(() -> new MarketException(OfferingErrorCode.NOT_FOUND)); List commentsWithRole = commentRepository.findAllWithRoleByOffering(offering); List responseItems = commentsWithRole.stream() @@ -81,7 +85,7 @@ public CommentAllResponse getAllComment(Long offeringId, Long loginMemberId) { private void validateMemberExistence(Long memberId) { if (!memberRepository.existsById(memberId)) { - throw new IllegalArgumentException("존재하지 않는 사용자가 로그인을 했네요"); // TODO: 예외 처리하기 + throw new MarketException(MemberErrorCode.NOT_FOUND); } } diff --git a/backend/src/main/java/com/zzang/chongdae/comment/service/dto/CommentSaveRequest.java b/backend/src/main/java/com/zzang/chongdae/comment/service/dto/CommentSaveRequest.java index 4677e8b3d..48af53c2d 100644 --- a/backend/src/main/java/com/zzang/chongdae/comment/service/dto/CommentSaveRequest.java +++ b/backend/src/main/java/com/zzang/chongdae/comment/service/dto/CommentSaveRequest.java @@ -1,4 +1,16 @@ package com.zzang.chongdae.comment.service.dto; -public record CommentSaveRequest(Long memberId, Long offeringId, String content) { +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; + +public record CommentSaveRequest(@NotNull + Long memberId, + + @NotNull + Long offeringId, + + @NotBlank(message = "댓글 내용을 입력해주세요.") + @Size(max = 80, message = "댓글 내용의 길이를 확인해주세요.") + String content) { } diff --git a/backend/src/main/java/com/zzang/chongdae/global/exception/ErrorMessage.java b/backend/src/main/java/com/zzang/chongdae/global/exception/ErrorMessage.java new file mode 100644 index 000000000..4b7202e7e --- /dev/null +++ b/backend/src/main/java/com/zzang/chongdae/global/exception/ErrorMessage.java @@ -0,0 +1,4 @@ +package com.zzang.chongdae.global.exception; + +public record ErrorMessage(String value) { +} diff --git a/backend/src/main/java/com/zzang/chongdae/global/exception/ErrorResponse.java b/backend/src/main/java/com/zzang/chongdae/global/exception/ErrorResponse.java new file mode 100644 index 000000000..0dbe44dc5 --- /dev/null +++ b/backend/src/main/java/com/zzang/chongdae/global/exception/ErrorResponse.java @@ -0,0 +1,10 @@ +package com.zzang.chongdae.global.exception; + +import org.springframework.http.HttpStatus; + +public interface ErrorResponse { + + HttpStatus getStatus(); + + ErrorMessage getErrorMessage(); +} diff --git a/backend/src/main/java/com/zzang/chongdae/global/exception/GlobalExceptionHandler.java b/backend/src/main/java/com/zzang/chongdae/global/exception/GlobalExceptionHandler.java new file mode 100644 index 000000000..79ee44027 --- /dev/null +++ b/backend/src/main/java/com/zzang/chongdae/global/exception/GlobalExceptionHandler.java @@ -0,0 +1,17 @@ +package com.zzang.chongdae.global.exception; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler + public ResponseEntity handle(MarketException e) { + ErrorResponse errorResponse = e.getErrorResponse(); + return ResponseEntity + .status(errorResponse.getStatus()) + .body(errorResponse.getErrorMessage()); + } +} diff --git a/backend/src/main/java/com/zzang/chongdae/global/exception/MarketException.java b/backend/src/main/java/com/zzang/chongdae/global/exception/MarketException.java new file mode 100644 index 000000000..ed66eb62d --- /dev/null +++ b/backend/src/main/java/com/zzang/chongdae/global/exception/MarketException.java @@ -0,0 +1,11 @@ +package com.zzang.chongdae.global.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class MarketException extends RuntimeException { + + private final ErrorResponse errorResponse; +} diff --git a/backend/src/main/java/com/zzang/chongdae/member/exception/MemberErrorCode.java b/backend/src/main/java/com/zzang/chongdae/member/exception/MemberErrorCode.java new file mode 100644 index 000000000..b972a8f32 --- /dev/null +++ b/backend/src/main/java/com/zzang/chongdae/member/exception/MemberErrorCode.java @@ -0,0 +1,24 @@ +package com.zzang.chongdae.member.exception; + +import static org.springframework.http.HttpStatus.BAD_REQUEST; + +import com.zzang.chongdae.global.exception.ErrorMessage; +import com.zzang.chongdae.global.exception.ErrorResponse; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum MemberErrorCode implements ErrorResponse { + + NOT_FOUND(BAD_REQUEST, "해당 사용자가 존재하지 않습니다."); + + private final HttpStatus status; + private final String message; + + @Override + public ErrorMessage getErrorMessage() { + return new ErrorMessage(this.message); + } +} diff --git a/backend/src/main/java/com/zzang/chongdae/member/repository/entity/MemberEntity.java b/backend/src/main/java/com/zzang/chongdae/member/repository/entity/MemberEntity.java index 9218ed0b3..77c94405c 100644 --- a/backend/src/main/java/com/zzang/chongdae/member/repository/entity/MemberEntity.java +++ b/backend/src/main/java/com/zzang/chongdae/member/repository/entity/MemberEntity.java @@ -27,7 +27,7 @@ public class MemberEntity extends BaseTimeEntity { private Long id; @NotNull - @Column(unique = true) + @Column(unique = true, length = 10) private String nickname; public boolean isSameMember(Long memberId) { diff --git a/backend/src/main/java/com/zzang/chongdae/offering/exception/OfferingErrorCode.java b/backend/src/main/java/com/zzang/chongdae/offering/exception/OfferingErrorCode.java new file mode 100644 index 000000000..be868abc3 --- /dev/null +++ b/backend/src/main/java/com/zzang/chongdae/offering/exception/OfferingErrorCode.java @@ -0,0 +1,27 @@ +package com.zzang.chongdae.offering.exception; + +import org.springframework.http.HttpStatus; + +import static org.springframework.http.HttpStatus.BAD_REQUEST; + +import com.zzang.chongdae.global.exception.ErrorMessage; +import com.zzang.chongdae.global.exception.ErrorResponse; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum OfferingErrorCode implements ErrorResponse { + + NOT_FOUND(BAD_REQUEST, "해당 공모가 존재하지 않습니다."), + PARTICIPANT_FULL(BAD_REQUEST, "해당 공모에 참여 가능한 인원수를 초과하였습니다."), + CANNOT_PARTICIPATE(BAD_REQUEST, "참여할 수 없는 공모입니다."); + + private final HttpStatus status; + private final String message; + + @Override + public ErrorMessage getErrorMessage() { + return new ErrorMessage(this.message); + } +} diff --git a/backend/src/main/java/com/zzang/chongdae/offering/repository/entity/OfferingEntity.java b/backend/src/main/java/com/zzang/chongdae/offering/repository/entity/OfferingEntity.java index 7622e007b..08f758370 100644 --- a/backend/src/main/java/com/zzang/chongdae/offering/repository/entity/OfferingEntity.java +++ b/backend/src/main/java/com/zzang/chongdae/offering/repository/entity/OfferingEntity.java @@ -13,6 +13,7 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; import java.math.BigDecimal; import java.time.LocalDateTime; import lombok.AccessLevel; @@ -37,6 +38,7 @@ public class OfferingEntity extends BaseTimeEntity { private MemberEntity member; @NotNull + @Column(length = 30) private String title; @NotNull @@ -58,15 +60,18 @@ public class OfferingEntity extends BaseTimeEntity { private String meetingAddressDetail; @NotNull + @Positive private Integer totalCount; @NotNull + @Positive private Integer currentCount = 1; @NotNull private Boolean isManualConfirmed; @NotNull + @Positive private BigDecimal totalPrice; public void updateCurrentCount() { diff --git a/backend/src/main/java/com/zzang/chongdae/offering/service/OfferingService.java b/backend/src/main/java/com/zzang/chongdae/offering/service/OfferingService.java index 60568a5a0..c1c4212f0 100644 --- a/backend/src/main/java/com/zzang/chongdae/offering/service/OfferingService.java +++ b/backend/src/main/java/com/zzang/chongdae/offering/service/OfferingService.java @@ -1,9 +1,12 @@ package com.zzang.chongdae.offering.service; +import com.zzang.chongdae.global.exception.MarketException; +import com.zzang.chongdae.member.exception.MemberErrorCode; import com.zzang.chongdae.member.repository.MemberRepository; import com.zzang.chongdae.member.repository.entity.MemberEntity; import com.zzang.chongdae.offering.domain.OfferingPrice; import com.zzang.chongdae.offering.domain.OfferingStatus; +import com.zzang.chongdae.offering.exception.OfferingErrorCode; import com.zzang.chongdae.offering.repository.OfferingRepository; import com.zzang.chongdae.offering.repository.entity.OfferingEntity; import com.zzang.chongdae.offering.service.dto.OfferingAllResponse; @@ -27,13 +30,13 @@ public class OfferingService { public OfferingDetailResponse getOfferingDetail(Long offeringId, Long memberId) { OfferingEntity offering = offeringRepository.findById(offeringId) - .orElseThrow(); // TODO: 예외 처리하기 + .orElseThrow(() -> new MarketException(OfferingErrorCode.NOT_FOUND)); OfferingPrice offeringPrice = offering.toOfferingPrice(); OfferingStatus offeringStatus = offering.toOfferingStatus(); MemberEntity member = memberRepository.findById(memberId) - .orElseThrow(); // TODO: 로그인 추가하면 교체 필요 + .orElseThrow(() -> new MarketException(MemberErrorCode.NOT_FOUND)); Boolean isParticipated = offeringMemberRepository.existsByOfferingAndMember(offering, member); return new OfferingDetailResponse(offering, offeringPrice, offeringStatus, isParticipated); @@ -54,7 +57,7 @@ public OfferingAllResponse getAllOffering(Long lastId, Integer pageSize) { public OfferingMeetingResponse getOfferingMeeting(Long offeringId) { OfferingEntity offering = offeringRepository.findById(offeringId) - .orElseThrow(); // TODO: 예외 처리하기 + .orElseThrow(() -> new MarketException(OfferingErrorCode.NOT_FOUND)); return new OfferingMeetingResponse(offering.toOfferingMeeting()); } } diff --git a/backend/src/main/java/com/zzang/chongdae/offeringmember/exception/OfferingMemberErrorCode.java b/backend/src/main/java/com/zzang/chongdae/offeringmember/exception/OfferingMemberErrorCode.java new file mode 100644 index 000000000..fc1aa05c2 --- /dev/null +++ b/backend/src/main/java/com/zzang/chongdae/offeringmember/exception/OfferingMemberErrorCode.java @@ -0,0 +1,24 @@ +package com.zzang.chongdae.offeringmember.exception; + +import static org.springframework.http.HttpStatus.BAD_REQUEST; + +import com.zzang.chongdae.global.exception.ErrorMessage; +import com.zzang.chongdae.global.exception.ErrorResponse; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +@AllArgsConstructor +public enum OfferingMemberErrorCode implements ErrorResponse { + + DUPLICATED(BAD_REQUEST, "이미 참여한 공모엔 참여할 수 없습니다."); + + private final HttpStatus status; + private final String message; + + @Override + public ErrorMessage getErrorMessage() { + return new ErrorMessage(this.message); + } +} diff --git a/backend/src/main/java/com/zzang/chongdae/offeringmember/service/OfferingMemberService.java b/backend/src/main/java/com/zzang/chongdae/offeringmember/service/OfferingMemberService.java index e32e1b21e..c7cb31bae 100644 --- a/backend/src/main/java/com/zzang/chongdae/offeringmember/service/OfferingMemberService.java +++ b/backend/src/main/java/com/zzang/chongdae/offeringmember/service/OfferingMemberService.java @@ -1,11 +1,15 @@ package com.zzang.chongdae.offeringmember.service; +import com.zzang.chongdae.global.exception.MarketException; +import com.zzang.chongdae.member.exception.MemberErrorCode; import com.zzang.chongdae.member.repository.MemberRepository; import com.zzang.chongdae.member.repository.entity.MemberEntity; import com.zzang.chongdae.offering.domain.OfferingStatus; +import com.zzang.chongdae.offering.exception.OfferingErrorCode; import com.zzang.chongdae.offering.repository.OfferingRepository; import com.zzang.chongdae.offering.repository.entity.OfferingEntity; import com.zzang.chongdae.offeringmember.domain.OfferingMemberRole; +import com.zzang.chongdae.offeringmember.exception.OfferingMemberErrorCode; import com.zzang.chongdae.offeringmember.repository.OfferingMemberRepository; import com.zzang.chongdae.offeringmember.repository.entity.OfferingMemberEntity; import com.zzang.chongdae.offeringmember.service.dto.ParticipationRequest; @@ -24,9 +28,9 @@ public class OfferingMemberService { @Transactional public Long participate(ParticipationRequest request) { MemberEntity member = memberRepository.findById(request.memberId()) - .orElseThrow(); // TODO: 예외처리 하기 + .orElseThrow(() -> new MarketException(MemberErrorCode.NOT_FOUND)); OfferingEntity offering = offeringRepository.findById(request.offeringId()) - .orElseThrow();// TODO: 예외처리 하기 + .orElseThrow(() -> new MarketException(OfferingErrorCode.NOT_FOUND)); validateParticipate(offering, member); OfferingMemberEntity offeringMember = new OfferingMemberEntity( @@ -44,13 +48,13 @@ private void validateParticipate(OfferingEntity offering, MemberEntity member) { private void validateClosed(OfferingEntity offering) { OfferingStatus offeringStatus = offering.toOfferingStatus(); if (offeringStatus.isClosed()) { - throw new IllegalArgumentException("아이고 못들어가요 ㅜㅜ"); // TODO: 예외처리 하기 + throw new MarketException(OfferingErrorCode.CANNOT_PARTICIPATE); } } private void validateDuplicate(OfferingEntity offering, MemberEntity member) { if (offeringMemberRepository.existsByOfferingAndMember(offering, member)) { - throw new IllegalArgumentException("이미 참여한 공모엔 참여할 수 없습니다."); // TODO: 예외처리 하기 + throw new MarketException(OfferingMemberErrorCode.DUPLICATED); } } } diff --git a/backend/src/main/java/com/zzang/chongdae/offeringmember/service/dto/ParticipationRequest.java b/backend/src/main/java/com/zzang/chongdae/offeringmember/service/dto/ParticipationRequest.java index 7828b6236..8d4d98d7e 100644 --- a/backend/src/main/java/com/zzang/chongdae/offeringmember/service/dto/ParticipationRequest.java +++ b/backend/src/main/java/com/zzang/chongdae/offeringmember/service/dto/ParticipationRequest.java @@ -1,4 +1,10 @@ package com.zzang.chongdae.offeringmember.service.dto; -public record ParticipationRequest(Long memberId, Long offeringId) { +import jakarta.validation.constraints.NotNull; + +public record ParticipationRequest(@NotNull + Long memberId, + + @NotNull + Long offeringId) { }