Exception 처리

Web Application 의 입장에서 바라 보았을 때, 에러가 났을 때 내려줄 수 있는 방법은 많지 않다.

1. 에러 페이지
2. 4XX Error or 5XX Error
3. Client가 200 외에 처리를 하지 못 할 때는 200을 내려주고 별도의 에러 Message 전달

Web Application 입장에서는 이런 에러는 try-catch로 그 때 마다 묶는 것이 아닌 한 곳에 모아서 처리하는게 제일 편리하다.

 

@ControllerAdvice Global 예외 처리 및 특정 package / Controller 예외 처리
@ExceptionHandler 특정 Controller의 예외처리

 

 

package com.example.exception.advice;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class GlobalControllerAdvice {

    @ExceptionHandler(value = Exception.class)
    public ResponseEntity exception(Exception e) {
        System.out.println(e.getClass().getName());

        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("");
    }

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public ResponseEntity methodArgumentNotValidException(MethodArgumentNotValidException e) {

        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
    }

}

@RestControllerAdvice 어노테이션을 설정 함으로써 Spring Web Application 안의 Exception 들을 Global하게 처리할 수 있다.  basePackages 값을 넣으면 원하는 패키지도 설정이 가능하다.

@ExceptionHandler 를 이용하여 특정 Exception에 대해 어떤 처리를 할 지 메소드를 정의할 수 있다.

 

package com.example.exception.controller;

import com.example.exception.dto.User;
import jakarta.validation.Valid;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api")
public class ApiController {
    @PostMapping("/post")
    public User post(@Valid @RequestBody User user) {
        return user;
    }

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public ResponseEntity methodArgumentNotValidException(MethodArgumentNotValidException e) {

        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
    }

}

컨트롤러 내부에  @ExceptionHandler가 정의되어 있는 경우 @RestControllerAdvice, @ControllerAdvice 등 보다 우선순위가 높아 컨트롤러 개별 Exception 처리를 할 수 있다.

 

 

추가 예제 (Validation & Exception)

package com.example.exception.controller;

import com.example.exception.dto.User;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api")
@Validated
public class ApiController 

    @GetMapping("/get")
    public User get(
            @NotNull
            @Size(min=2)
            @RequestParam String name,

            @NotNull
            @Min(1)
            @RequestParam Integer age) {

        User user = new User();
        user.setName(name);
        user.setAge(age);

        int a = 10+age;

        return user;
    }


    @PostMapping("/post")
    public User post(@Valid @RequestBody User user) {
        return user;
    }

}

 

@Validated은 스프링에서 제공하는 어노테이션으로써 컨트롤러뿐만 아니라 서비스, 레포지토리 등 모든 계층의 그룹 기반 유효성 검증을 지원한다. 예외로 ConstraintViolationException 발생한다.

 

 

package com.example.exception.dto;

public class Error {
    private String field;
    private String message;
    private String invalidValue;

   // get/set , toString 메소드 생략

}

 

package com.example.exception.dto;

import java.util.List;

public class ErrorResponse {

    String statusCode;
    String requestUrl;
    String code;
    String message;
    String resultCode;

    List<Error> errorList;

 // get/set , toString 메소드 생략
 
}

 

 

package com.example.exception.advice;

import com.example.exception.controller.ApiController;
import com.example.exception.dto.Error;
import com.example.exception.dto.ErrorResponse;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.ElementKind;
import jakarta.validation.Path;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.ArrayList;
import java.util.List;

@RestControllerAdvice(basePackageClasses = ApiController.class)
public class ApiControllerAdvice {


    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    public ResponseEntity missingServletRequestParameterException(MissingServletRequestParameterException e, HttpServletRequest request) {
        List<Error> errorList = new ArrayList<>();

        String fieldName = e.getParameterName();
        String invalidValue = e.getMessage();

        Error errorElement = new Error();
        errorElement.setField(fieldName);
        errorElement.setMessage(invalidValue);
        errorList.add(errorElement);

        ErrorResponse errorResponse = new ErrorResponse();
        errorResponse.setErrorList(errorList);
        errorResponse.setMessage("missingServletRequestParameterException");
        errorResponse.setRequestUrl(request.getRequestURI());
        errorResponse.setStatusCode(HttpStatus.BAD_REQUEST.toString());
        errorResponse.setResultCode("FAILED");


        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
    }



}

컨트롤러에서 validation 단계에서 exception이 발생한 경우 위와 같이 클라이언트의 잘못된 요청에 대하여 원하는 응답을 내려줄 수 있다.

 

 

{
    "statusCode": "400 BAD_REQUEST",
    "requestUrl": "/api/get",
    "code": null,
    "message": "constraintViolationException",
    "resultCode": "FAILED",
    "errorList": [
        {
            "field": "age",
            "message": "1 이상이어야 합니다",
            "invalidValue": "0"
        }
    ]
}

'Spring' 카테고리의 다른 글

서블릿 컨테이너와 Spring 컨텍스트  (0) 2025.04.15
Filter  (1) 2025.04.15
Validation  (0) 2025.03.24
Java Bean 규약  (0) 2025.03.24
AOP 관점지향 프로그램  (0) 2025.03.23

+ Recent posts