Skip to content

Commit

Permalink
Merge pull request #42 from tukcomCD2024/feat/#40_ExceptionHandle
Browse files Browse the repository at this point in the history
Feat/#40 exception handle
  • Loading branch information
seokho-1116 authored Jan 18, 2024
2 parents 81a8f05 + 5b2ec65 commit e61518f
Show file tree
Hide file tree
Showing 20 changed files with 101 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public interface AuthApi {
description = "ok"
)
})
@GetMapping(
@PostMapping(
value = "/sign-up",
produces = {"application/json"}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ public record SignUpRequest(
String authId,

@Schema(description = "사용자 이메일")
@NotBlank(message = "사용자 이메일은 필수입니다.")
@NotNull(message = "사용자 이메일은 필수입니다.")
@Email
String email,

@Schema(description = "사용자 프로필 url")
@NotBlank(message = "사용자 프로필 url은 필수입니다.")
@NotNull(message = "사용자 프로필 url은 필수입니다.")
String profileUrl,

@Schema(description = "소셜 프로바이더 타입")
@NotNull(message = "소셜 프로바이더 타입은 필수입니다.")
@NotBlank(message = "소셜 프로바이더 타입은 필수입니다.")
SocialType socialType
) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import org.springframework.validation.annotation.Validated;

@Schema(description = "토큰 재발급 요청")
public record TokenReIssueRequest(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
package site.timecapsulearchive.core.domain.auth.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import org.springframework.validation.annotation.Validated;
import jakarta.validation.constraints.Pattern;

@Schema(description = "인증 문자 요청")
@Validated
public record VerificationMessageSendRequest(

@Schema(description = "핸드폰 번호")
@Pattern(regexp = "^01(?:0|1|[6-9])[.-]?(\\d{3}|\\d{4})[.-]?(\\d{4})$", message = "10 ~ 11 자리의 숫자만 입력 가능합니다.")
String phone
) {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package site.timecapsulearchive.core.domain.auth.dto.response;

import io.swagger.v3.oas.annotations.media.Schema;
import org.springframework.validation.annotation.Validated;

@Schema(description = "임시 인증 토큰")
public record TemporaryTokenResponse(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package site.timecapsulearchive.core.domain.auth.dto.response;

import io.swagger.v3.oas.annotations.media.Schema;
import org.springframework.validation.annotation.Validated;

@Schema(description = "완전한 인증 토큰")
public record TokenResponse(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
package site.timecapsulearchive.core.domain.auth.entity;

import com.fasterxml.jackson.annotation.JsonCreator;

public enum SocialType {
KAKAO, GOOGLE;

@JsonCreator
public static SocialType from(String s) {
return SocialType.valueOf(s.toUpperCase());
}

public static SocialType getSocialType(String registrationId) {
if (isKakaoLogin(registrationId)) {
return SocialType.KAKAO;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
public class AlreadyReIssuedTokenException extends BusinessException {

public AlreadyReIssuedTokenException() {
super(ErrorCode.ALREADY_RE_ISSUED_TOKEN_EXCEPTION);
super(ErrorCode.ALREADY_RE_ISSUED_TOKEN_ERROR);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import java.io.IOException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.MediaType;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
Expand All @@ -30,8 +29,7 @@ public void onAuthenticationFailure(
log.info("oauth2 인증 실패", exception);

ErrorResponse errorResponse = ErrorResponse.create(
ErrorCode.OAUTH2_NOT_AUTHENTICATED_EXCEPTION.getCode(),
exception.getMessage()
ErrorCode.OAUTH2_NOT_AUTHENTICATED_ERROR
);

response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ public void onAuthenticationSuccess(HttpServletRequest request, HttpServletRespo
log.info("oauth2 인증 실패", exception);

ErrorResponse errorResponse = ErrorResponse.create(
ErrorCode.INTERNAL_SERVER_ERROR.getCode(),
exception.getMessage()
ErrorCode.INTERNAL_SERVER_ERROR
);

response.getWriter()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PatchMapping;
import site.timecapsulearchive.core.domain.auth.dto.request.CheckStatusRequest;
import site.timecapsulearchive.core.domain.member.dto.reqeust.CheckStatusRequest;
import site.timecapsulearchive.core.domain.member.dto.reqeust.MemberDetailUpdateRequest;
import site.timecapsulearchive.core.domain.member.dto.response.MemberDetailResponse;
import site.timecapsulearchive.core.domain.member.dto.response.MemberStatusResponse;
Expand All @@ -35,7 +35,7 @@ public interface MemberApi {
)
})
@GetMapping(
value = "/me",
value = "/",
produces = {"application/json"}
)
ResponseEntity<MemberDetailResponse> findMemberById();
Expand All @@ -53,7 +53,7 @@ public interface MemberApi {
)
})
@PatchMapping(
value = "/me",
value = "/",
consumes = {"multipart/form-data"}
)
ResponseEntity<Void> updateMemberById(@ModelAttribute MemberDetailUpdateRequest request);
Expand All @@ -70,7 +70,7 @@ public interface MemberApi {
)
})
@GetMapping(
value = "/me/status",
value = "/status",
produces = {"application/json"}
)
ResponseEntity<ApiSpec<MemberStatusResponse>> checkStatus(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import site.timecapsulearchive.core.domain.auth.dto.request.CheckStatusRequest;
import site.timecapsulearchive.core.domain.member.dto.reqeust.CheckStatusRequest;
import site.timecapsulearchive.core.domain.member.dto.reqeust.MemberDetailUpdateRequest;
import site.timecapsulearchive.core.domain.member.dto.response.MemberDetailResponse;
import site.timecapsulearchive.core.domain.member.dto.response.MemberStatusResponse;
Expand All @@ -16,7 +16,7 @@

@RestController
@RequiredArgsConstructor
@RequestMapping("/")
@RequestMapping("/me")
public class MemberApiController implements MemberApi {

private final MemberService memberService;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package site.timecapsulearchive.core.domain.auth.dto.request;
package site.timecapsulearchive.core.domain.member.dto.reqeust;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public MemberStatusResponse checkStatus(
String authId,
SocialType socialType
) {

Boolean isVerified = memberQueryRepository.findIsVerifiedByAuthIdAndSocialType(
authId,
socialType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
Expand Down Expand Up @@ -73,7 +72,7 @@ private RequestMatcher notRequireAuthenticationMatcher() {
antMatcher("/v3/api-docs/**"),
antMatcher("/swagger-ui/**"),
antMatcher(HttpMethod.POST, "/auth/token/re-issue"),
antMatcher(HttpMethod.POST, "/me/status"));
antMatcher(HttpMethod.GET, "/me/status"));
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,18 @@ public enum ErrorCode {

//global
INTERNAL_SERVER_ERROR(500, "G001", "서버에 오류가 발생하였습니다."),
INPUT_INVALID_VALUE_ERROR(400, "G002", "잘못된 입력 값입니다."),
INPUT_INVALID_TYPE_ERROR(400, "G003", "잘못된 입력 타입입니다."),

//jwt
INVALID_TOKEN_EXCEPTION(400, "J001", "jwt 토큰이 유효하지 않습니다."),
ALREADY_RE_ISSUED_TOKEN_EXCEPTION(400, "J002", "이미 액세스 토큰 재발급에 사용된 리프레시 토큰입니다."),
INVALID_TOKEN_ERROR(400, "J001", "jwt 토큰이 유효하지 않습니다."),
ALREADY_RE_ISSUED_TOKEN_ERROR(400, "J002", "이미 액세스 토큰 재발급에 사용된 리프레시 토큰입니다."),

//auth
AUTHENTICATION_EXCEPTION(401, "A001", "인증에 실패했습니다. 인증 수단이 유효한지 확인하세요."),
AUTHENTICATION_ERROR(401, "A001", "인증에 실패했습니다. 인증 수단이 유효한지 확인하세요."),

//ouath
OAUTH2_NOT_AUTHENTICATED_EXCEPTION(401, "O001", "OAuth2 인증에 실패하였습니다.");
OAUTH2_NOT_AUTHENTICATED_ERROR(401, "O001", "OAuth2 인증에 실패하였습니다.");

private final int status;
private final String code;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,57 @@
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Collections;
import java.util.List;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;

@Schema(description = "에러 발생 시 응답")
public record ErrorResponse(

@Schema(description = "에러 코드")
String code,

@Schema(description = "에러 메시지")
String message,

@Schema(description = "에러 리스트 ex) 필드 에러들")
List<Error> result
List<Error> errors
) {

public static ErrorResponse create(String code, String message) {
return new ErrorResponse(code, message, Collections.emptyList());
public static ErrorResponse create(final ErrorCode errorCode) {
return new ErrorResponse(
errorCode.getCode(),
errorCode.getMessage(),
Collections.emptyList()
);
}

public static ErrorResponse create(final ErrorCode errorCode,
final BindingResult bindingResult) {
return new ErrorResponse(
errorCode.getCode(),
errorCode.getMessage(),
Error.from(bindingResult)
);
}

private record Error(
public record Error(
String field,
String value,
String reason
) {

public static List<Error> from(final BindingResult bindingResult) {
return bindingResult.getFieldErrors().stream()
.map(Error::from)
.toList();
}

private static Error from(final FieldError fieldError) {
return new Error(
fieldError.getField(),
String.valueOf(fieldError.getRejectedValue()),
fieldError.getDefaultMessage()
);
}

}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
package site.timecapsulearchive.core.global.error;

import static site.timecapsulearchive.core.global.error.ErrorCode.INPUT_INVALID_TYPE_ERROR;
import static site.timecapsulearchive.core.global.error.ErrorCode.INPUT_INVALID_VALUE_ERROR;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import site.timecapsulearchive.core.global.error.exception.BusinessException;
Expand All @@ -10,14 +16,38 @@
@Slf4j
public class GlobalExceptionHandler {

@ExceptionHandler(Exception.class)
protected ResponseEntity<ErrorResponse> handleGlobalException(final Exception e) {
log.error(e.getMessage(), e);
ErrorResponse errorResponse = ErrorResponse.create(ErrorCode.INTERNAL_SERVER_ERROR);
return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}

@ExceptionHandler(BusinessException.class)
protected ResponseEntity<ErrorResponse> handleBusinessException(final BusinessException e) {
log.error("handleBusinessException", e);
final ErrorCode errorCode = e.getErrorCode();
final ErrorResponse errorResponse = ErrorResponse.create(errorCode.getCode(),
errorCode.getMessage());
log.error(e.getMessage(), e);
ErrorCode errorCode = e.getErrorCode();
ErrorResponse errorResponse = ErrorResponse.create(errorCode);

return ResponseEntity.status(errorCode.getStatus())
.body(errorResponse);
}

@ExceptionHandler
protected ResponseEntity<ErrorResponse> handleRequestArgumentNotValidException(
MethodArgumentNotValidException e) {
log.warn(e.getMessage());
ErrorResponse response = ErrorResponse.create(INPUT_INVALID_VALUE_ERROR, e.getBindingResult());
return ResponseEntity.status(INPUT_INVALID_VALUE_ERROR.getStatus())
.body(response);
}

@ExceptionHandler
protected ResponseEntity<ErrorResponse> handleRequestTypeNotValidException(
HttpMessageNotReadableException e) {
log.warn(e.getMessage());
ErrorResponse response = ErrorResponse.create(INPUT_INVALID_TYPE_ERROR);
return ResponseEntity.status(INPUT_INVALID_VALUE_ERROR.getStatus())
.body(response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
public class InvalidTokenException extends BusinessException {

public InvalidTokenException() {
super(ErrorCode.INVALID_TOKEN_EXCEPTION);
super(ErrorCode.INVALID_TOKEN_ERROR);
}

public InvalidTokenException(Throwable throwable) {
super(ErrorCode.INVALID_TOKEN_EXCEPTION, throwable);
super(ErrorCode.INVALID_TOKEN_ERROR, throwable);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,7 @@ private void unsuccessfulAuthentication(
SecurityContextHolder.clearContext();

ErrorResponse errorResponse = ErrorResponse.create(
ErrorCode.AUTHENTICATION_EXCEPTION.getCode(),
exception.getMessage()
ErrorCode.AUTHENTICATION_ERROR
);

response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
Expand Down

0 comments on commit e61518f

Please sign in to comment.