Skip to content

Commit

Permalink
[fix] API 응답 관련 개선사항들 적용 (#53)
Browse files Browse the repository at this point in the history
* [fix] #52 change response dto

* [fix] #52 add method HandlerMethodValidationException in handler to handle Exception

* [fix] #49 redefine error code with domain number

* [fix] #49 rename argument name

* [fix] #49 delete validateMember by using IsMember annotation

* [fix] #49 subdivide exception in Global Exception Handler

* [fix] #49 make dto's field name more detailed

* [fix] #49 fix custom error code

---------

Co-authored-by: chaewonkim <[email protected]>
  • Loading branch information
tkdwns414 and chaewonni authored Jul 12, 2024
1 parent b4132ac commit 678d0ae
Show file tree
Hide file tree
Showing 28 changed files with 124 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingRequestHeaderException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.HandlerMethodValidationException;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.NoHandlerFoundException;

Expand Down Expand Up @@ -73,24 +76,51 @@ public ResponseEntity<BusinessErrorCode> handleBusinessException(BusinessExcepti
}

// 존재하지 않는 요청에 대한 예외
@ExceptionHandler(value = {NoHandlerFoundException.class, HttpRequestMethodNotSupportedException.class})
public ResponseEntity<BusinessErrorCode> handleNoPageFoundException(Exception e) {
@ExceptionHandler(value = {NoHandlerFoundException.class})
public ResponseEntity<BusinessErrorCode> handleNoPageFoundException(NoHandlerFoundException e) {
log.error("GlobalExceptionHandler catch NoHandlerFoundException : {}", BusinessErrorCode.NOT_FOUND_END_POINT.getMessage());
return ResponseEntity
.status(BusinessErrorCode.NOT_FOUND_END_POINT.getHttpStatus())
.body(BusinessErrorCode.NOT_FOUND_END_POINT);
}

@ExceptionHandler(value = {MethodArgumentNotValidException.class})
public ResponseEntity<BusinessErrorCode> handleException(MethodArgumentNotValidException e) {
log.error("handleException() in GlobalExceptionHandler throw MethodArgumentNotValidException : {}", e.getMessage());
// 잘못된 Method로 요청한 경우
@ExceptionHandler(value = {HttpRequestMethodNotSupportedException.class})
public ResponseEntity<BusinessErrorCode> handleNoPageFoundException(HttpRequestMethodNotSupportedException e) {
log.error("GlobalExceptionHandler catch NoHandlerFoundException : {}", BusinessErrorCode.NOT_FOUND_END_POINT.getMessage());
return ResponseEntity
.status(BusinessErrorCode.METHOD_NOT_ALLOWED.getHttpStatus())
.body(BusinessErrorCode.METHOD_NOT_ALLOWED);
}

@ExceptionHandler(value = {HandlerMethodValidationException.class, MethodArgumentNotValidException.class})
public ResponseEntity<BusinessErrorCode> handleValidationException(Exception e) {
log.error("GlobalExceptionHandler catch MethodArgumentNotValidException : {}", e.getMessage());
return ResponseEntity
.status(BusinessErrorCode.INVALID_ARGUMENTS.getHttpStatus())
.body(BusinessErrorCode.INVALID_ARGUMENTS);
}

@ExceptionHandler(value = {MissingServletRequestParameterException.class})
public ResponseEntity<BusinessErrorCode> handleMissingParameterException(MissingServletRequestParameterException e) {
log.error("GlobalExceptionHandler catch MissingServletRequestParameterException : {}", e.getMessage());
return ResponseEntity
.status(BusinessErrorCode.MISSING_REQUIRED_PARAM.getHttpStatus())
.body(BusinessErrorCode.MISSING_REQUIRED_PARAM);
}

@ExceptionHandler(value = {MissingRequestHeaderException.class})
public ResponseEntity<BusinessErrorCode> handleMissingHeaderException(MissingRequestHeaderException e) {
log.error("GlobalExceptionHandler catch MissingRequestHeaderException : {}", e.getMessage());
return ResponseEntity
.status(BusinessErrorCode.MISSING_REQUIRED_HEADER.getHttpStatus())
.body(BusinessErrorCode.MISSING_REQUIRED_HEADER);
}

@ExceptionHandler(MaxUploadSizeExceededException.class)
public ResponseEntity<BusinessErrorCode> handleMaxSizeException(MaxUploadSizeExceededException e) {
log.error("GlobalExceptionHandler catch MaxUploadSizeExceededException : {}", e.getMessage());
e.printStackTrace();
return ResponseEntity
.status(BusinessErrorCode.PAYLOAD_TOO_LARGE.getHttpStatus())
.body(BusinessErrorCode.PAYLOAD_TOO_LARGE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.kkumulkkum.server.annotation.IsMember;
import org.kkumulkkum.server.annotation.UserId;
import org.kkumulkkum.server.dto.meeting.request.MeetingCreateDto;
import org.kkumulkkum.server.dto.meeting.request.MeetingRegisterDto;
Expand Down Expand Up @@ -49,20 +50,20 @@ public ResponseEntity<MeetingsDto> getMeetings(
return ResponseEntity.ok(meetingService.getMeetings(userId));
}

@IsMember(meetingIdParamIndex = 0)
@GetMapping("/meetings/{meetingId}")
public ResponseEntity<MeetingDto> getMeeting(
@UserId Long userId,
@PathVariable Long meetingId
) {
return ResponseEntity.ok(meetingService.getMeeting(userId, meetingId));
return ResponseEntity.ok(meetingService.getMeeting(meetingId));
}

@IsMember(meetingIdParamIndex = 0)
@GetMapping("/meetings/{meetingId}/members")
public ResponseEntity<MembersDto> getMembers(
@UserId Long userId,
@PathVariable Long meetingId
) {
return ResponseEntity.ok(meetingService.getMembers(userId, meetingId));
return ResponseEntity.ok(meetingService.getMembers(meetingId));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import lombok.RequiredArgsConstructor;
import org.kkumulkkum.server.annotation.UserId;
import org.kkumulkkum.server.dto.user.request.ImageUpdateDto;
import org.kkumulkkum.server.dto.user.request.NameUpdateDto;
import org.kkumulkkum.server.dto.user.response.UserDto;
import org.kkumulkkum.server.dto.user.response.UserNameDto;
import org.kkumulkkum.server.service.user.UserService;
Expand Down Expand Up @@ -37,9 +38,9 @@ public ResponseEntity<Void> deleteImage(
@PatchMapping("/users/me/name")
public ResponseEntity<UserNameDto> updateName(
@UserId final Long userId,
@Valid @RequestBody final UserNameDto userNameDto
@Valid @RequestBody final NameUpdateDto nameUpdateDto
) {
return ResponseEntity.ok().body(userService.updateName(userId, userNameDto));
return ResponseEntity.ok().body(userService.updateName(userId, nameUpdateDto));
}

@GetMapping("/users/me")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import java.time.LocalDateTime;

public record MeetingDto (
Long id,
Long meetingId,
String name,
@JsonFormat(pattern = "yyyy-MM-DD")
LocalDateTime createdAt,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public static MeetingsDto of(List<Meeting> meetings) {
}

public record MeetingDto (
Long id,
Long meetingId,
String name,
int memberCount
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.kkumulkkum.server.dto.member.response;

public record MemberDto(
Long id,
Long memberId,
String name,
String profileImg
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,26 @@
import java.time.LocalDateTime;

public record ParticipantStatusUserInfoDto(
Long id,
Long participantId,
Long memberId,
String name,
String profileImg,
LocalDateTime preparationAt,
LocalDateTime departureAt,
LocalDateTime arrivalAt
) {
public static ParticipantStatusUserInfoDto from(
Long id,
Long participantId,
Long memberId,
String name,
String profileImg,
LocalDateTime preparationAt,
LocalDateTime departureAt,
LocalDateTime arrivalAt
) {
return new ParticipantStatusUserInfoDto(
id,
participantId,
memberId,
name,
profileImg,
preparationAt,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.kkumulkkum.server.dto.participant.response;

public record LateComerDto(
Long id,
Long participantId,
String name,
String profileImg
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package org.kkumulkkum.server.dto.participant.response;

public record ParticipantDto(
Long id,
Long participantId,
Long memberId,
String name,
String profileImg,
String state
) {
public static ParticipantDto of(Long id, String name, String profileImg, String state) {
public static ParticipantDto of(Long participantId, Long memberId, String name, String profileImg, String state) {
return new ParticipantDto(
id,
participantId,
memberId,
name,
profileImg,
state
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import java.time.temporal.ChronoUnit;

public record MainPromiseDto(
Long id,
Long promiseId,
String name,
String meetingName,
String dressLevel,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.time.LocalDateTime;

public record PromiseDto(
Long promiseId,
String placeName,
String address,
String roadAddress,
Expand All @@ -16,6 +17,7 @@ public record PromiseDto(
) {
public static PromiseDto from(Promise promise) {
return new PromiseDto(
promise.getId(),
promise.getPlaceName(),
promise.getAddress(),
promise.getRoadAddress(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static PromisesDto of(List<Promise> promises, Boolean done) {
}

public record PromiseDto(
Long id,
Long promiseId,
String name,
int dDay,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy.MM.dd")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.kkumulkkum.server.domain.UserInfo;

public record UserDto(
Long userId,
String name,
int level,
int promiseCount,
Expand All @@ -12,6 +13,7 @@ public record UserDto(
) {
public static UserDto from(UserInfo userInfo) {
return new UserDto(
userInfo.getUser().getId(),
userInfo.getName(),
userInfo.getLevel(),
userInfo.getPromiseCount(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,22 @@
@Getter
@AllArgsConstructor
public enum AuthErrorCode implements DefaultErrorCode {
// 400 Bad Request
INVALID_PROVIDER(HttpStatus.BAD_REQUEST, 40091, "유효하지 않은 소셜 플랫폼입니다."),
INVALID_APPLE_PUBLIC_KEY(HttpStatus.BAD_REQUEST, 40092,"유효하지 않은 Apple Public Key입니다."),
CREATE_PUBLIC_KEY_EXCEPTION(HttpStatus.BAD_REQUEST, 40093,"Apple public key 생성에 문제가 발생했습니다."),
INVALID_APPLE_IDENTITY_TOKEN(HttpStatus.BAD_REQUEST, 40094, "Apple Identity Token 형식이 올바르지 않습니다."),
EXPIRED_APPLE_IDENTITY_TOKEN(HttpStatus.BAD_REQUEST, 40495, "Apple Identity Token 유효기간이 만료됐습니다."),

// 401 Unauthorized
UNAUTHORIZED(HttpStatus.UNAUTHORIZED, 40190, "인증되지 않은 사용자입니다."),
INVALID_TOKEN(HttpStatus.UNAUTHORIZED, 40191, "액세스 토큰의 형식이 올바르지 않습니다."),
EXPIRED_TOKEN(HttpStatus.UNAUTHORIZED, 40192, "액세스 토큰이 만료되었습니다."),
UNSUPPORTED_TOKEN(HttpStatus.UNAUTHORIZED, 40193, "지원하지 않는 토큰 형식입니다."),
EMPTY_TOKEN(HttpStatus.UNAUTHORIZED, 40194, "토큰이 제공되지 않았습니다."),
MISMATCH_REFRESH_TOKEN(HttpStatus.UNAUTHORIZED, 40195, "리프레시 토큰이 일치하지 않습니다."),
UNKNOWN_TOKEN(HttpStatus.UNAUTHORIZED, 40196, "알 수 없는 토큰입니다."),

// 404 Not Found
NOT_FOUND_REFRESH_TOKEN(HttpStatus.NOT_FOUND, 40490, "존재하지 않는 리프레시 토큰입니다."),
// 400 BAD_REQUEST
INVALID_PROVIDER(HttpStatus.BAD_REQUEST, 40010, "유효하지 않은 소셜 플랫폼입니다."),
INVALID_APPLE_PUBLIC_KEY(HttpStatus.BAD_REQUEST, 40011,"유효하지 않은 Apple Public Key입니다."),
CREATE_PUBLIC_KEY_EXCEPTION(HttpStatus.BAD_REQUEST, 40012,"Apple public key 생성에 문제가 발생했습니다."),
INVALID_APPLE_IDENTITY_TOKEN(HttpStatus.BAD_REQUEST, 40013, "Apple Identity Token 형식이 올바르지 않습니다."),
EXPIRED_APPLE_IDENTITY_TOKEN(HttpStatus.BAD_REQUEST, 40014, "Apple Identity Token 유효기간이 만료됐습니다."),
// 401 UNAUTHORIZED
UNAUTHORIZED(HttpStatus.UNAUTHORIZED, 40110, "인증되지 않은 사용자입니다."),
INVALID_TOKEN(HttpStatus.UNAUTHORIZED, 40111, "액세스 토큰의 형식이 올바르지 않습니다."),
EXPIRED_TOKEN(HttpStatus.UNAUTHORIZED, 40112, "액세스 토큰이 만료되었습니다."),
UNSUPPORTED_TOKEN(HttpStatus.UNAUTHORIZED, 40113, "지원하지 않는 토큰 형식입니다."),
EMPTY_TOKEN(HttpStatus.UNAUTHORIZED, 40114, "토큰이 제공되지 않았습니다."),
MISMATCH_REFRESH_TOKEN(HttpStatus.UNAUTHORIZED, 40115, "리프레시 토큰이 일치하지 않습니다."),
UNKNOWN_TOKEN(HttpStatus.UNAUTHORIZED, 40116, "알 수 없는 토큰입니다."),
// 404 NOT_FOUND
NOT_FOUND_REFRESH_TOKEN(HttpStatus.NOT_FOUND, 40410, "존재하지 않는 리프레시 토큰입니다."),
;

private HttpStatus httpStatus;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@
@Getter
@AllArgsConstructor
public enum AwsErrorCode implements DefaultErrorCode {
// 400 Bad Request
// 400 BAD_REQUEST
INVALID_IMAGE_EXTENSION(HttpStatus.BAD_REQUEST, 40080, "이미지 확장자는 jpg, png, webp만 가능합니다."),
IMAGE_SIZE_EXCEEDED(HttpStatus.BAD_REQUEST,40081, "이미지 사이즈는 5MB를 넘을 수 없습니다."),

// 404 Not Found
// 404 NOT_FOUND
NOT_FOUND_IMAGE(HttpStatus.NOT_FOUND, 40480, "삭제할 이미지를 찾을 수 없습니다."),
;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,18 @@
@Getter
@AllArgsConstructor
public enum BusinessErrorCode implements DefaultErrorCode {
// 400 Bad Request
// 400 BAD_REQUEST
BAD_REQUEST(HttpStatus.BAD_REQUEST,40000, "잘못된 요청입니다."),
INVALID_ARGUMENTS(HttpStatus.BAD_REQUEST, 40001, "인자의 형식이 올바르지 않습니다."),
PAYLOAD_TOO_LARGE(HttpStatus.BAD_REQUEST,40002,"최대 업로드 크기를 초과했습니다."),
// 404 Not Found
MISSING_REQUIRED_PARAM(HttpStatus.BAD_REQUEST,40003,"필수 파라미터가 누락되었습니다."),
MISSING_REQUIRED_HEADER(HttpStatus.BAD_REQUEST,40004,"필수 헤더가 누락되었습니다."),
// 404 NOT_FOUND
NOT_FOUND(HttpStatus.NOT_FOUND,40400, "요청한 정보를 찾을 수 없습니다."),
NOT_FOUND_END_POINT(HttpStatus.NOT_FOUND,40401, "요청한 엔드포인트를 찾을 수 없습니다."),
// 405 METHOD_NOT_ALLOWED
METHOD_NOT_ALLOWED(HttpStatus.METHOD_NOT_ALLOWED,40500, "지원하지 않는 메소드입니다."),
// 500 INTERNAL_SEVER_ERROR
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR,50000, "서버 내부 오류입니다."),
;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
@Getter
@AllArgsConstructor
public enum MeetingErrorCode implements DefaultErrorCode {
// 403 Forbidden
NOT_JOINED_MEETING(HttpStatus.FORBIDDEN, 40310, "참여하지 않은 모임입니다."),
// 404 Not Found
NOT_FOUND_MEETING(HttpStatus.NOT_FOUND, 40420, "모임을 찾을 수 없습니다."),
// 409 Conflict
ALREADY_JOINED(HttpStatus.CONFLICT, 40910, "이미 참여한 모임입니다."),
// 403 FORBIDDEN
NOT_JOINED_MEETING(HttpStatus.FORBIDDEN, 40330, "참여하지 않은 모임입니다."),
// 404 NOT_FOUND
NOT_FOUND_MEETING(HttpStatus.NOT_FOUND, 40430, "모임을 찾을 수 없습니다."),
// 409 CONFLICT
ALREADY_JOINED(HttpStatus.CONFLICT, 40930, "이미 참여한 모임입니다."),
;

private HttpStatus httpStatus;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
@Getter
@AllArgsConstructor
public enum MemberErrorCode implements DefaultErrorCode {
// 403 Forbidden
NOT_JOINED_MEMBER(HttpStatus.FORBIDDEN, 40310, "모임에 참여하지 않은 회원입니다."),
// 403 FORBIDDEN
NOT_JOINED_MEMBER(HttpStatus.FORBIDDEN, 40340, "모임에 참여하지 않은 회원입니다."),
;

private HttpStatus httpStatus;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
@AllArgsConstructor
public enum OpenApiErrorCode implements DefaultErrorCode {
// 400 BAD_REQUEST
INVALID_ARGUMENT(HttpStatus.BAD_REQUEST, 40070, "유효하지 않은 인자입니다."),
// 500 Internal Server Error
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, 50070, "외부 API 서버 오류입니다."),
INVALID_ARGUMENT(HttpStatus.BAD_REQUEST, 40090, "유효하지 않은 인자입니다."),
// 500 INTERVAL_SERVER_ERROR
INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, 50090, "외부 API 서버 오류입니다."),
;


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
@AllArgsConstructor
public enum ParticipantErrorCode implements DefaultErrorCode {
// 400 BAD_REQUEST
NOT_JOINED_PROMISE(HttpStatus.BAD_REQUEST, 40040, "참여하지 않은 약속입니다."),
INVALID_STATE(HttpStatus.BAD_REQUEST, 40041, "유효하지 않은 상태 변경입니다."),
NOT_JOINED_PROMISE(HttpStatus.BAD_REQUEST, 40060, "참여하지 않은 약속입니다."),
INVALID_STATE(HttpStatus.BAD_REQUEST, 40061, "유효하지 않은 상태 변경입니다."),
// 403 FORBIDDEN
FORBIDDEN_PARTICIPANT(HttpStatus.FORBIDDEN, 40340, "약속 참여자가 아닙니다."),
FORBIDDEN_PARTICIPANT(HttpStatus.FORBIDDEN, 40360, "약속 참여자가 아닙니다."),
;

private HttpStatus httpStatus;
Expand Down
Loading

0 comments on commit 678d0ae

Please sign in to comment.