1. 예외란
가. 오류(Error) vs 예외(Exception)
•
오류(Error): 어플리케이션의 아래 계층, 시스템 레벨에서 발생하는 것, 개발자의 로직에 의한 문제가 아니기 때문에 코드 상으로 해결 불가
•
예외(Exception): 개발자의 로직에 의해 발생하는 것으로 코드로 해결 가능
나. Checked vs UnChecked
•
차이점
image from https://www.nextree.co.kr/p3239/
•
상속관계
image from https://www.nextree.co.kr/p3239/
2. 비지니스 예외처리
가. Error Response 객체 반영
•
추후 시도
나. 컨트롤러의 책임과 예외 처리
•
컨트롤러의 책임은 모든 요청에 대한 값을 검증하여 유효한 값을 서비스 레이어에 전달하는 것
다. Business Exception
•
Business Exception이란 API 명세에 기록된 제약사항에 맞지 않을 경우 발생시키는 Exception
•
Business Exception 목적
→ 특정 로직의 책임 제한, 책임 이상의 조건에 대해 예외를 발생시킴으로써 책임을 제한할 수 있음
→ 유지보수, 상품의 가격을 계산하는 로직에 상품 매진 등의 처리 로직이 껴있으면 유지보수에 어려움 발생. 상품 매진 부분은 예외처리하면 유지보수 시 계산 로직에만 집중할 수 있음
3. 프로그래밍 오류에 대한 예외
가. 전제조건 위배에 따라 예외 발생
•
전제조건이란 API에 명세에 기록된 제약사항
→ 입력 값의 제약사항은 양수인데, 음수가 입력된 경우 전제조건을 위배
•
전제조건을 지키지 못하는 경우에는 런타임 예외를 발생시킬 것
나. 복구 가능 여부와 Checked, Unchecked 예외 사용
•
복구 가능하다면, Checked Exception
•
복구 가능하지 않다면, Unchecked Exception
•
확실하지 않다면, Unchecked Exception
다. Unchecked Exception은 RuntimeException을 상속할 것
4. 스프링부트에서 예외처리 방법
가. 예외 전역처리 클래스 생성
•
@ControllerAdvice 어노테이션 추가된 클래스에서 어플리케이션 전체에 대한 예외처리 책임
// controller/advice/GlobalControllerExceptionHandler.java
@ControllerAdvice
public class GlobalControllerExceptionHandler {
}
Java
복사
나. 예외 처리할 메서드 생성
•
@ExceptionHandler() 어노테이션 추가된 메서드에서 소괄호 안에 선언된 예외에 대한 처리를 담당
// controller/advice/GlobalControllerExceptionHandler.java
@ResponseStatus(HttpStatus.UNAUTHORIZED)
@ExceptionHandler(UnauthorizedAccessException.class)
public String handleUnauthorizedAccess(UnauthorizedAccessException unauthorizedAccessException, Model model) {
model.addAttribute("errorMessage", unauthorizedAccessException.getMessage());
return "error/401";
}
Java
복사
다. 예외 파일 생성
// exception/UnauthorizedAccessException.java
public class UnauthorizedAccessException extends RuntimeException {
public UnauthorizedAccessException() {
super("Unauthorized Access");
}
public UnauthorizedAccessException(String message) {
super(message);
}
}
Java
복사
라. 뷰에 에러 메시지 반영
•
@ExceptionHandler()가 추가된 메서드에서 특정 에러 발생에 따라 뷰에 알람으로 표시
→ 모델로 특정 메시지 전달에 따라 전달한 메시지를 출력
→ 또는 메시지 유무에 따라 이미 html에 작성된 메시지 오픈
•
에러 페이지로 이동 및 예외메시지 출력
// UserService.java
public void checkAccessId(User loginUser, Long accessId) {
if (!loginUser.matchesId(accessId)) {
throw new UnauthorizedAccessException("다른 사람의 정보를 수정할 수 없습니다.");
}
}
Java
복사
// error/401.html
{{#partial "message"}}
401 UNAUTHORIZED 권한 없음<br/>
{{errorMessage}}
{{/partial}}
{{> components/errorBase}}
HTML
복사
참고) 로그인 시 아이디나 비번이 틀렸을 경우
•
예외 정의
// controller/advice/GlobalControllerExceptionHandler.java
@ResponseStatus(HttpStatus.UNAUTHORIZED)
@ExceptionHandler(LoginFailedException.class)
public String handleLoginFailed(Model model) {
model.addAttribute("loginFailed", true);
return "users/login";
}
Java
복사
// exception/LoginFailedException.java
public class LoginFailedException extends RuntimeException {
public LoginFailedException() {
super("Login Failed");
}
}
Java
복사
•
예외 발생 로직
@PostMapping("/login")
public String login(String userId, String password, HttpSession session) {
HttpSessionUtils.setUserSession(session, userService.authenticate(userId, password));
return "redirect:/";
}
Java
복사
// service/UserService.java
public User authenticate(String userId, String password) {
return userRepository.findByUserId(userId)
.filter(u -> u.matchesPassword(password))
.orElseThrow(LoginFailedException::new);
}
Java
복사
•
예외 발생 결과
{{#if loginFailed}}
<div class="alert alert-danger" role="alert">아이디 또는 비밀번호가 틀립니다. 다시 로그인 해주세요.</div>
{{/if}}
HTML
복사
참고
예외란
비지니스 예외처리
프로그래밍 오류에 대한 예외
•
Effective Java 3/E
예외처리
•
응용(K 코드 참고), https://github.com/PizzaCola-K/spring-boot-qna