[Spring] Error Handler

yoon·2024년 1월 10일

spring-boot

목록 보기
6/41
post-thumbnail

✔ global handler

@RestControllerAdvice
: 파라미터를 주지 않으면 global하게 동작

@RestControllerAdvice
public class RestApiExceptionHandler {
    @ExceptionHandler(value = {Exception.class})
    public ResponseEntity exception(Exception e){

        log.error("RestApiExceptionHandler", e);
        return ResponseEntity.status(400).build();
    }

✔ 특정 패키지, 클래스 지정

1. 파라미터로 지정해주기

@Slf4j
@RestControllerAdvice(basePackages = "com.example.exception.controller") 
//클래스 지정도 가능 basePackageClasses={RestApiController.class, RestApiBController.class}
public class RestApiExceptionHandler {
    @ExceptionHandler(value = {IndexOutOfBoundsException.class})
    public ResponseEntity exception(IndexOutOfBoundsException e){

        log.error("RestApiExceptionHandler", e);
        return ResponseEntity.status(200).build();
    }

2. 동일한 클래스에 ExceptionHandler 작성해주기

@Slf4j
@RestController
@RequestMapping("/api/b")
public class RestApiBController {
   @GetMapping("/hello")
   public void hello(){
       throw new NumberFormatException("Number Format Exception");
   }

   @ExceptionHandler(value = {NumberFormatException.class})
   public ResponseEntity numberFormatErrorHandler(NumberFormatException e){
       log.error("RestApiBController",e);
       return ResponseEntity.ok().build();
   }
}

✔ 항상 동일한 response 형태 내려주기

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder //builder()를 통해 객체 생성
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public class Api<T> {

    private String resultCode;
    private String resultMessage;
    private T data;
}

@Slf4j
@RestControllerAdvice
public class RestApiExceptionHandler {
@ExceptionHandler(value = {NoSuchElementException.class})
    public ResponseEntity noSuchElement(NoSuchElementException e){
        log.error("",e);
        var response =  Api.builder()
                .resultMessage(HttpStatus.NOT_FOUND.name())
                .resultCode(String.valueOf(HttpStatus.NOT_FOUND.value()))
                .build();
        return ResponseEntity.status(HttpStatus.NOT_FOUND)
                .body(response);

    }
}

다음과 같이 Api 클래스에 제네릭을 사용하여 반환 데이터의 타입은 자유롭게 지정해주고, resultCode와 resultMessage를 지정해주면 항상 동일한 형태의 response를 내려줄 수 있다.

◾ ex) 유저아이디로 유저 찾기

@RestController
@RequestMapping("/api/user")
public class UserApiController {
   private static List<UserResponse> userList = List.of(
           UserResponse.builder()
                   .id("1")
                   .age(15)
                   .name("Kim")
                   .build()
           ,
           UserResponse.builder()
                   .id("2")
                   .age(20)
                   .name("Lee")
                   .build()
   );
   @GetMapping("/id/{userId}")
   public Api<UserResponse> getUser(
           @PathVariable String userId
   ){
       var user = userList.stream().filter(
               it->it.getId().equals(userId)
       ).findFirst().get();

       Api<UserResponse> response = Api.<UserResponse>builder()
               .resultCode(String.valueOf(HttpStatus.OK.value()))
               .resultMessage(HttpStatus.OK.name())
               .data(user)
               .build();

       return response;
   }
}

Pathvariable을 통해 입력받은 userId에 해당하는 유저 데이터를 반환한다.

  • UserResponse 모델에 @Build를 사용하여서 setter가 아닌 build()로 인스턴스 생성 가능
  • user데이터가 현재 list로 되어있기 때문에 stream을 통해 filter 사용
  • 만약 list에 없는 userId를 입력하면 위에서 작성해준 noSuchElement() 에러 핸들러가 동작한다.
profile
하루하루 차근차근🌱

0개의 댓글