[Section3] Spring MVC - 비즈니스 로직에 대한 예외처리
🧑🏻💻 TIL(Today I Learned)
✔️ checked Exception, unchecked Exception
💡 체크 예외와 언체크 예외
➡️ 애플리케이션에서 발생하는 예외는 체크 예외와 언체크 예외로 구분
- 체크 예외(Checked Exception)
: 발생한 예외를 잡아서(catch) 체크한 후에 해당 예외를 복구하든가 아니면 회피하든가 등의 어떤 구체적인 처리를 해야하는 예외
: ex) ClassNotFoundException - 언체크 예외(Unchecked Exception)
: 예외를 잡아서(catch) 해당 예외에 대한 어떤 처리를 할 필요가 없는 예외
: 명시적으로 잡아서 어떤 처리를 할 필요가 없음
: ex) NullPointerException, ArrayIndexOutOfBoundsException
🔎 개발자가 의도적으로 예외를 던질 수(throw) 있는 상황
- 백엔드 서버와 외부 시스템과의 연동에서 발생하는 에러 처리
- 시스템 내부에서 조회하려는 리소스(자원, Resource)가 없는 경우
💡 서비스 계층에서 예외 던지기
➡️ Java에서 던져진 예외는 메서드 바깥 즉, 메서드를 호출한 지점으로 던져짐
➡️서비스 계층의 메서드는 API 계층인 Controller의 핸들러 메서드가 이용하기 때문에 서비스 계층에서 던져진 예외는 Controller의 핸들러 메서드 쪽에서 잡아서 처리할 수 있음
➡️ 전날 실습에서 Controller에서 발생한 예외를 Exception Advice에서 처리하도록 공통화해두었으니 서비스 계층에서 던진 예외 또한 여기서 처리
@Service
public class MemberService {
...
public Member findMember(long memberId) {
// TODO should business logic
// (1)
throw new RuntimeException("Not found member");
}
...
}
- 서비스 계층에서 예외 던지기
→ 회원 정보 조회를 요청했으나 조회되는 회원이 없다고 가정하고 메서드 밖으로 던지기
@RestControllerAdvice
public class GlobalExceptionAdvice {
...
...
// (1)
@ExceptionHandler
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleResourceNotFoundException(RuntimeException e) {
System.out.println(e.getMessage());
return null;
}
}
- 서비스 계층에서 던져진 예외를 GlobalExceptionAdvice 클래스에서 받기
➡️ 하지만 서비스 계층에서 의도적으로 던질 수 있는 예외 상황은 회원 정보가 존재하지 않는 경우뿐만 아니라 다양한 하게 존재하기 때문에 메서드 이름은 적절하지 않음
➡️ RuntimeException을 그대로 던지고 Exception Advice에서 RuntimeException을 그대로 잡는 것은 예외의 의도가 명확하지 않으며 구체적으로 어떤 예외가 발생했는지에 대한 예외 정보를 얻는 것이 어려움
💡사용자 정의 예외(Custom Exception) 사용
➡️ RuntimeException과 같은 추상적인 예외가 아닌 좀 더 구체적으로 표현할 수 있는 Custom Exception을 만들어서 예외를 던질 수 있음
import lombok.Getter;
public enum ExceptionCode {
MEMBER_NOT_FOUND(404, "Member Not Found");
@Getter
private int status;
@Getter
private String message;
ExceptionCode(int status, String message) {
this.status = status;
this.message = message;
}
}
- Custom Exception에 사용할 ExceptionCode를 enum으로 정의
- ExceptionCode를 enum으로 정의하면 비즈니스 로직에서 발생하는 다양한 유형의 예외를 enum에 추가해서 사용 가능
import lombok.Getter;
public class BusinessLogicException extends RuntimeException {
@Getter
private ExceptionCode exceptionCode;
public BusinessLogicException(ExceptionCode exceptionCode) {
super(exceptionCode.getMessage());
this.exceptionCode = exceptionCode;
}
}
- 서비스 계층에서 사용할 Custom Exception 정의
- RuntimeException을 상속하고 있으며 ExceptionCode를 멤버 변수로 지정하여 생성자를 통해서 더 구체적인 예외 정보 제공 가능
- 서비스 계층에서 개발자가 의도적으로 예외를 던져야 하는 다양한 상황에서 Exception Code 정보만 바꿔가며 던질 수 있음
@Service
public class MemberService {
...
...
public Member findMember(long memberId) {
// TODO should business logic
// (1)
throw new BusinessLogicException(ExceptionCode.MEMBER_NOT_FOUND);
}
}
- 서비스 계층에 BusinessLogicException 적용
- 조금 더 구체적인 예외 정보를 던지도록 변경
@RestControllerAdvice
public class GlobalExceptionAdvice {
...
@ExceptionHandler
public ResponseEntity handleBusinessLogicException(BusinessLogicException e) {
System.out.println(e.getExceptionCode().getStatus());
System.out.println(e.getMessage());
return new ResponseEntity<>(HttpStatus.valueOf(e.getExceptionCode().getStatus()));
}
- BusinessLogicException을 처리하도록 수정된 GlobalExceptionAdvice
- 메서드명 변경
- 메서드 파라미터 변경
- @ResponseStatus 제거
→ 이 애너테이션은 고정된 HttpStatus를 지정하기 때문에 동적으로 지정할 수 있도록 ResponseEntity로 변경
➡️ 코드 수정이 다 끝나고 Postman에서 MemberController의 getMember() 메서드에 전송 요청하면 아래와 같은 결과가 콘솔에 나옴
( Response 응답 메세지를 작성하지 않아서 포스트맨에는 아무것도 나오지 않음, 콘솔에만 찍힘)
'SEB_BE_45 > 공부 정리' 카테고리의 다른 글
[Section3] Spring MVC - JPA 기반 액세스 계층 (0) | 2023.06.21 |
---|---|
[Section3] Spring MVC - JDBC 기반 데이터 액세스 실습 (0) | 2023.06.21 |
[Sectoion3] Spring MVC - 예외 처리 (0) | 2023.06.14 |
[Section3] Spring MVC - 서비스 계층 (0) | 2023.06.13 |
[Section3] Spring MVC - API 계층 2 (0) | 2023.06.12 |