|
1 | 1 | package com.backend.global.exception; |
2 | 2 |
|
3 | 3 | import com.backend.global.response.RsData; |
4 | | -import jakarta.validation.ConstraintViolationException; |
5 | 4 | import org.springframework.http.HttpStatus; |
6 | 5 | import org.springframework.http.ResponseEntity; |
7 | | -import org.springframework.security.access.AccessDeniedException; |
8 | | -import org.springframework.security.core.AuthenticationException; |
9 | | -import org.springframework.web.bind.MethodArgumentNotValidException; |
10 | 6 | import org.springframework.web.bind.annotation.ExceptionHandler; |
11 | 7 | import org.springframework.web.bind.annotation.RestControllerAdvice; |
12 | | -import org.springframework.web.server.ResponseStatusException; |
13 | | -import org.springframework.http.converter.HttpMessageNotReadableException; |
14 | 8 |
|
15 | 9 | import java.util.NoSuchElementException; |
16 | 10 |
|
| 11 | +import static org.springframework.http.HttpStatus.NOT_FOUND; |
| 12 | + |
17 | 13 | @RestControllerAdvice |
18 | 14 | public class GlobalExceptionHandler { |
19 | | - |
20 | | - /** 커스텀 ServiceException: resultCode를 HTTP 코드로 사용 */ |
| 15 | + // ServiceException의 resultCode에 따라 동적으로 HTTP 상태 코드를 반환 |
21 | 16 | @ExceptionHandler(ServiceException.class) |
22 | 17 | public ResponseEntity<RsData<Void>> handleServiceException(ServiceException e) { |
23 | 18 | HttpStatus httpStatus; |
24 | 19 | try { |
25 | | - httpStatus = HttpStatus.valueOf(Integer.parseInt(e.getResultCode())); |
| 20 | + int resultCode = Integer.parseInt(e.getResultCode().split("-")[0]); |
| 21 | + httpStatus = HttpStatus.valueOf(resultCode); |
26 | 22 | } catch (NumberFormatException ex) { |
27 | 23 | httpStatus = HttpStatus.BAD_REQUEST; |
28 | 24 | } |
29 | 25 |
|
30 | | - String msg = e.getMessage(); |
31 | | - if (msg != null && msg.startsWith(e.getResultCode() + ":")) { |
32 | | - msg = msg.substring(e.getResultCode().length() + 1); |
| 26 | + String rawMessage = e.getMessage(); |
| 27 | + // 메시지에서 resultCode 접두사 제거 |
| 28 | + if (rawMessage != null && rawMessage.startsWith(e.getResultCode() + ":")) { |
| 29 | + rawMessage = rawMessage.substring(e.getResultCode().length() + 1); |
33 | 30 | } |
34 | | - return new ResponseEntity<>(new RsData<>(e.getResultCode(), msg), httpStatus); |
35 | | - } |
36 | 31 |
|
37 | | - /** 스프링의 명시적 상태 예외는 그대로 통과 */ |
38 | | - @ExceptionHandler(ResponseStatusException.class) |
39 | | - public ResponseEntity<RsData<Void>> handleRse(ResponseStatusException e) { |
40 | | - HttpStatus status = HttpStatus.valueOf(e.getStatusCode().value()); |
41 | | - return new ResponseEntity<>(new RsData<>(String.valueOf(status.value()), e.getReason()), status); |
42 | | - } |
| 32 | + RsData<Void> rsData = new RsData<>(e.getResultCode(), rawMessage); |
43 | 33 |
|
44 | | - /** 검증 실패: 400 */ |
45 | | - @ExceptionHandler({ |
46 | | - IllegalArgumentException.class, // 비즈니스/입력 검증 |
47 | | - HttpMessageNotReadableException.class, // JSON 파싱 에러 |
48 | | - ConstraintViolationException.class // @Validated on params |
49 | | - }) |
50 | | - public ResponseEntity<RsData<Void>> handleBadRequest(Exception e) { |
51 | | - return new ResponseEntity<>(new RsData<>("400", e.getMessage()), HttpStatus.BAD_REQUEST); |
| 34 | + return new ResponseEntity<>(rsData, httpStatus); |
52 | 35 | } |
53 | | - |
54 | | - /** @Valid body 바인딩 에러: 400 + 필드 메시지 */ |
55 | | - @ExceptionHandler(MethodArgumentNotValidException.class) |
56 | | - public ResponseEntity<RsData<Void>> handleMethodArgumentNotValid(MethodArgumentNotValidException e) { |
57 | | - String msg = e.getBindingResult().getFieldErrors().stream() |
58 | | - .map(fe -> fe.getField() + ": " + fe.getDefaultMessage()) |
59 | | - .findFirst() |
60 | | - .orElse("잘못된 요청입니다."); |
61 | | - return new ResponseEntity<>(new RsData<>("400", msg), HttpStatus.BAD_REQUEST); |
62 | | - } |
63 | | - |
64 | | - /** 리소스 없음: 404 */ |
65 | | - @ExceptionHandler({ NoSuchElementException.class, NullPointerException.class }) |
66 | | - public ResponseEntity<RsData<Void>> handleNotFound(RuntimeException e) { |
67 | | - return new ResponseEntity<>(new RsData<>("404", e.getMessage() != null ? e.getMessage() : "Not Found"), |
68 | | - HttpStatus.NOT_FOUND); |
69 | | - } |
70 | | - |
71 | | - /** 인증/인가: 401/403 */ |
72 | | - @ExceptionHandler(AuthenticationException.class) |
73 | | - public ResponseEntity<RsData<Void>> handleAuth(AuthenticationException e) { |
74 | | - return new ResponseEntity<>(new RsData<>("401", "인증이 필요합니다."), HttpStatus.UNAUTHORIZED); |
| 36 | + @ExceptionHandler(NoSuchElementException.class) |
| 37 | + public ResponseEntity<RsData<Void>> handle(NoSuchElementException ex) { |
| 38 | + // HTTP 404 Not Found 상태와 함께 에러 응답을 반환합니다. |
| 39 | + return new ResponseEntity<>( |
| 40 | + new RsData<>( |
| 41 | + "404-1", |
| 42 | + ex.getMessage() |
| 43 | + ), |
| 44 | + NOT_FOUND |
| 45 | + ); |
75 | 46 | } |
76 | 47 |
|
77 | | - @ExceptionHandler(AccessDeniedException.class) |
78 | | - public ResponseEntity<RsData<Void>> handleAccess(AccessDeniedException e) { |
79 | | - return new ResponseEntity<>(new RsData<>("403", "접근 권한이 없습니다."), HttpStatus.FORBIDDEN); |
| 48 | + @ExceptionHandler(IllegalArgumentException.class) |
| 49 | + public ResponseEntity<RsData<Void>> handle(IllegalArgumentException ex) { |
| 50 | + return new ResponseEntity<>( |
| 51 | + new RsData<>( |
| 52 | + "400-1", |
| 53 | + ex.getMessage() |
| 54 | + ), |
| 55 | + HttpStatus.CONFLICT |
| 56 | + ); |
80 | 57 | } |
81 | 58 |
|
82 | | - /** 그 외: 500 */ |
83 | | - @ExceptionHandler(Exception.class) |
84 | | - public ResponseEntity<RsData<Void>> handleEtc(Exception e) { |
85 | | - return new ResponseEntity<>(new RsData<>("500", "서버 오류"), HttpStatus.INTERNAL_SERVER_ERROR); |
| 59 | + @ExceptionHandler(NullPointerException.class) |
| 60 | + public ResponseEntity<RsData<Void>> handle(NullPointerException ex) { |
| 61 | + return new ResponseEntity<>( |
| 62 | + new RsData<>( |
| 63 | + "404-1", |
| 64 | + "NullPointerException" |
| 65 | + ), |
| 66 | + NOT_FOUND |
| 67 | + ); |
86 | 68 | } |
87 | 69 | } |
0 commit comments