본문 바로가기

Java

[Java] 예외 처리 - custom exception

custom exception이란

예외 클래스에는 자바에서 미리 정의한 예외 클래스들이 있다. -> 표준 예외

그리고 개발자가 직접 예외 클래스를 정의하여 사용할 수 있는데 이를 custom exception이라고 한다. ( 사용자 정의 예외 )

비지니스 로직에서 발생가능한 예외를 더 구체적으로 알려주기 위한 목적 

 

참고 링크 

https://tecoble.techcourse.co.kr/post/2020-08-17-custom-exception/

https://ssoco.tistory.com/69

 

 

custom exception 만드는 팁 

설명에 앞서 가능하다면 표준예외를 사용하는 것을 우선 추천한다.

표준예외는 가독성이 좋으며, 충분히 의미 전달이 되며, 메모리 사용량 감소하며, 로딩 시간이 줄어든다. 

하지만 custom exception은 설계 측면에서, 유지보수 측면 등 사용하는 다양한 이유가 있다. 

 

 

1) 이름으로 정보 전달 및 클래스명에 Exception 붙이기

이름으로 어떤 예외가 발생했는지 알 수 있도록 명명할 수 있다. 

컨벤션으로 클래스명에 Exception 붙이는 것이 좋다. 

 

 

2) 메서드가 던지는 모든 예외를 문서화 

예외가 발생하는 상황을 구체적으로 문서화 기록

@throws를 이용하면 문서로 기록이 가능하다. 

그리고 이름에서 알 수 있는 구체적인 Exception throw하기 ( Exception, RuntimeException 같은 원인을 알기 어려운 상위 클래스 사용 추천 x ) 

단, unchecked exception은 checked exception과 구분하기 위해 메서드에 throws 선언문은 생략하는 편 

 

/**
*
*  @throws NumberFormatException - arg가 숫자형 데이터가 아닌 경우 throw
* @throws SQLException - 예외가 발생할 수 있는 상황 기술 ... 
*/

publicvoidfoo(String arg)throws NumberFormatException, SQLException {
}

 

 

3) 예외 메시지에 실패 정보 담기 

사용자 정의 예외는 표준 예외보다 더 상세한 상세 메시지를 전달가능하다.

 

- 메세지를 받는 생성자 구현

- toString() 을 Overriding하여 실패 원인에 대한 정보(예외 발생에 영향을 준 모든 필드와 인자의 값)를 추가

 

다음 코드처럼 IndexOutOfBoundsException 예외에 대한 custom class를 만들어서 더 구체적인 예외 메시지를 전달 할 수 있도록 수정할 수 있다. 

public class IllegalIndexException extends IndexOutOfBoundsException {
	private static final String message = "범위를 벗어났습니다.";

	public IllegalIndexException(List<?> target, int index) {
		super(message + " size: " + target.size() + " index: " + index);
	}
}

 

 

4) 예외 생성 발생 비용 줄이기

상위 클래스 Throwable의 fillInStackTrace() override하면 stack trace 생성 비용을 줄일 수 있다. 

보통 Custom Exception은 유효하지 않은 값일 때

하위 비즈니스 로직을 수행하지 못하도록 하기 위한 용도일 때가 많다.

 

즉 StackTrace가 필요하지 않고 단순히 try-catch로 이후 flow를 제어하거나, Spring 환경에서 ControllerAdvice로 예외를 처리한다든가 하는 상황에서는 불필요한 성능 저하를 막기 위해

 아무 trace도 갖지 않도록 직접 fillInStackTrace()를 오버라이딩 처리할 수 있다.

 

@Override
public synchronized Throwable fillInStackTrace() {
	return this;
}

 

 

5) 예외 캐싱

static final 키워드를 이용해 예외를 미리 캐싱(로드)해둘 수 있다. 

예를 들어 구현해낸 Custom Exception이 stack trace도 갖지 않고, 상황에 따라 정보를 다르게 주는 예외가 아니라 단순하게 메세지만 넘겨준다면 해당 예외를 캐싱해두는 것도 비용 절감의 방법이다.

public class CustomException extends RuntimeException {
	public static final CustomException CUSTOM_EXCEPTION = new CustomException("대충 예외라는 내용");
	//...
}

 

6) 예외 응집도 고려 

패키지로 따로 사용자 정의 예외를 모아 관리 할 수 있다. 

예를 들어 사용자 정의 예외를 사용한다면 예외에 필요한 메시지, 전달할 정보의 데이터, 데이터 가공 메소드들을 한 곳에서 관리할 수 있다. 이는 우리에게 객체의 책임이 분리된 깔끔한 코드를 안겨줄 것이다.

 

 

7) 예외 발생 후처리 

Spring에서 @RestControllerAdvice를 통해 전역적인 예외 처리가 가능하다.

이 부분은 사용자 정의 예외와 spring을 같이 고려했을 때의 장점이다.

반면

표준 예외는 재사용성이 강하지만 표준 예외의 발생 위치를 알기 어렵다. 

 

예를 들어 IllegalArgumentException 예외가 발생했다면 some 메서드에서 발생했다고 확신할 수 있을까

프로젝트가 커질 수록 중복되는 표준 예외를 발생시키는 곳이 많아질 것이다. 

하지만 사용자 정의 예외를 사용하면 이런 혼란스러움을 줄일 수 있다.

 

@Controller
public class SomeController {
    // ...
    @PostMapping("/some")
    public ResponseEntity<Void> some(@RequestBody SomeRequest request) {
        Something something = someService.someMethod(request);
        if (somevalidate(something)) {
            throw new IllegalArgumentException();
        }

        SomeExternalLibrary.doSomething(something);

        return ResponseEntity.ok().build();
    }
    // ...
}