Wookim

Spring 예외 처리 패턴 본문

programming language/Java

Spring 예외 처리 패턴

개발자인 경우 2022. 1. 3. 18:42

예외 처리 패턴

구성 요소

  1. controller
    1. ExceptionHandler (or Advice Controller)
    2. Service call method
  2. Custom Exception Class
  3. Error Enum class
  4. Service

예외 흐름 부터 살펴보기

  1. controller
  2. service 호출
  3. service
  4. SomeException(사용자 정의) 예외 발생
  5. SomeException 예외에 새로 정의한 열거형 에러 정보 전달
  6. controller
  7. SomeException 예외 핸들러 캐치
  8. SomeException 에서 SomeError의 code와 msg 정보 추출
  9. 리퀘스트 정보 설정
  10. 리턴

Controller

@RequiredArgsConstructor
@RestController
public class SomeController{

  	/** beans */
	private final SomeService someService;
	...
  
  	/** logger */
	private final Logger logger = LoggerFactory.getLogger(SomeController.class);

  	/** SomeException 예외가 발생 시 해당 핸들러에서 예외 처리 */
	@ExceptionHandler(SomeException.class)
	public ResponseEntity<Object> someExcpetionHandle(SomeException e) throws Exception{

            Map<String, Object> errorMap = new HashMap<>();

            if(!(e == null || e.getErrorMessage().isEmpty() || e.getCode().isEmpty()){
                errorMap.put("code", e.getCode());
                errorMap.put("message", e.getErrorMessage());
            }else{
                errorMap.put("code", 500); // 예외 처리 되지 않은 서버 예외 코드
                errorMap.put("message", e.getMessage());	
            }
			return ResponseEntity.badRequest().body(errorMap);
	}
	
	/** Exception 예외 발생시 해당 핸들러에서 예외 처리 */
	@ExceptionHandler(Exception.class)
	public ResponseEntity<Object> exceptionHanlde(Exception e) throws Exception{
            Map<String, Object> errorMap = new HashMap<>();
            errorMap.put("code", 500);
            errorMap.put("message", e.getMessage());
            return ResponseEntity.badRequest().body(errorMap);
	}
	
	/** 특정 데이터 조회 API */
	@GetMapping("/...")
	public ResponseEntity<Object> getSomeData(){
         	return ResponseEntity.ok().body(someService.getSomeData());
	}
**}**

controller 설명

  1. 커스템 예외 핸들러
	/** SomeException 예외가 발생 시 해당 핸들러에서 예외 처리 */
	@ExceptionHandler(SomeException.class)
	public ResponseEntity<Object> someExcpetionHandle(SomeException e) throws Exception{

            Map<String, Object> errorMap = new HashMap<>();
            errorMap.put("code", e.getCode());
            errorMap.put("message", e.getErrorMessage());
            return ResponseEntity.badRequest().body(errorMap);
   	}

@ExceptionHandler(SomeException.class)

이 적용된 메소드가 있는 SomeController에서

SomeException 예외 발생(직접 정의한 예외) 시 해당 메소드에서 예외를 캐치한다.

이 후 처리를 위한 코드를 작성하면 된다.

  1. Exeption 예외 핸들러
	/** Exception 예외 발생시 해당 핸들러에서 예외 처리 */
	@ExceptionHandler(Exception.class)
	public ResponseEntity<Object> exceptionHanlde(Exception e) throws Exception{

            Map<String, Object> errorMap = new HashMap<>();
            errorMap.put("code", 500);
            errorMap.put("message", e.getMessage());
            return ResponseEntity.badRequest().body(errorMap);
	}

@ExceptionHandler(Exception.class)

이 적용된 메소드가 있는 SomeController에서

Exception 예외 발생시 해당 메소드에서 예외를 캐치한다.

이 후 처리를 위한 코드를 작성하면 된다.

  1. Service 메소드 호출
	/** 특정 데이터 조회 API */
	@GetMapping("/...")
	public ResponseEntity<Object> getSomeData(){
    		return ResponseEntity.ok().body(someService.getSomeData());
	}

서비스 메소드 호출 코드


Custom Exception

public class SomeException extends RuntimeException{

        /** 에러를 정의한 열거형 클래스 */
        private SomeError error;

        /** 생성자 */
        public SomeException(){
            super();
        }

        /** 생성자 */
        public SomeException(String msg){
            super(msg);
        }

        /** 열거형 에러 클래스를 인자로 받는 커스텀 생성자 */
        public SomeException(SomeError error){
            super(error.getMessage());
            this.error = error;
        }

        /** 에러 클래스에서 에러 메시지 전달 */
        public String getErrorMessage(){
            return this.error.getMessage();
        }

        /** 에러 클래스에서 에러 코드 전달 */
        public String getCode(){
            return this.error.getCode();
        }
}

RuntimeException 에러를 상속하여 직접 예외 클래스를 선언했다.

생성자들을 보면 부모 클래스의 생성자를 호출한다. (super())

해당 예외 객체 생성 시 부모인 RuntimeException 객체가 생성됨을 의미한다.

새로 정의한 멤버인 SomeError 객체와 해당 객체의 정보를 뽑는 메소드를 선언했다.

아래 코드에서 SomeError 클래스를 살펴보자


Custom Error Enum class

public enum SomeError{
	SOME_ERROR_OCCUR("SE0001", "Some error occured"),
	SOME_DATA_IS_NULL_ERROR("SE0002", "Some data is null error occured"),
	...
  ;
	
	private String code;
	private String msg;

	public String getCode(){
           return this.code;
	}

	public String getMsg(){
           return this.msg;
	}
}

위 코드처럼 에러 코드와 메시지를 관리하는 열거형 클래스를 정의하고

발생하는 에러 및 코드를 관리한다.

사용법은 아래의 예외 발생 코드를 살펴보자


Service 에서 예외 발생 시키기

@Service
public SomeServiceImpl implement SomeService{
	
	@Override
	public SomeResDto getSomeData(SomeReqDto dto){
		...
		// 예외 발생 시키기
		throw new SomeException(SomeError.SOME_ERROR_OCCUR);
	}
}

위 코드를 보면 서비스의 getSomeData() 메소드가 실행되면

우리가 정의한 SomeException 예외가 발생한다.

이때 인자로 우리가 또 정의한 SomeError의 특정 열거형 정보인 SOME_ERROR_OCCUR를 매개변수로 전달했다.

위 코드를 이용해 SomeException 클래스의 SomeException(SomeError error)

생성자를 호출 했기 때문에 SomeException 클래스의 멤버인 error가 할당된다.

  /** 열거형 에러 클래스를 인자로 받는 커스텀 생성자 */
  public SomeException(SomeError error){
     super(error.getMessage());
     this.error = error;
  }

  /** 에러 클래스에서 에러 메시지 전달 */
  public String getErrorMessage(){
      return this.error.getMessage();
  }

  /** 에러 클래스에서 에러 코드 전달 */
  public String getCode(){
      return this.error.getCode();
  }

Controller Exception Handler

/** SomeException 예외가 발생 시 해당 핸들러에서 예외 처리 */
@ExceptionHandler(SomeException.class)
public ResponseEntity<Object> someExcpetionHandle(SomeException e) throws Exception{

    Map<String, Object> errorMap = new HashMap<>();
    errorMap.put("code", e.getCode());
    errorMap.put("message", e.getErrorMessage());
    return ResponseEntity.badRequest().body(errorMap);
}

서비스에서 컨트롤러로 SomeException 에러를 전파하면 위 핸들러에서 이를 캐치한다

 

서비스에서 예외를 발생 시킬때 SomeError 객체를 넘기는 생성자를 이용했기에

e.getCode(), e,getErrorMessge() 메소드를 사용하여

SomeError 열거형 클래스의 SOME_ERROR_OCCUR 에 정의된 code와 msg를 가져올 수 있다.

 

그리고 응답시 ResponseEntity 객체를 리턴하기 때문에

json으로 응답을 보내 프론트에 예외 정보를 전달한다.

 

 

이로서 예외처리 패턴을 정리해봤다.

필자는 controller에 ExceptionHandler를 직접 정의했다.

 

다음에는 ControllerAdvice 어노테이션과 기능을 통해

모든 컨트롤러 혹은 패키지 경로별로 예외를 핸들링 하는 기능을 사용해 정리해 보자

Comments