Search

스프링 예외처리

1. 예외란

가. 오류(Error) vs 예외(Exception)

오류(Error): 어플리케이션의 아래 계층, 시스템 레벨에서 발생하는 것, 개발자의 로직에 의한 문제가 아니기 때문에 코드 상으로 해결 불가
예외(Exception): 개발자의 로직에 의해 발생하는 것으로 코드로 해결 가능

나. Checked vs UnChecked

차이점
상속관계

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
예외처리