자바의 예외처리 방법
자바에서는 아래와 같이 try-catch를 사용하여 예외를 처리하였다. (Java 예외처리 더보기)
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("0으로 나눌 수 없습니다.");
}
- 한 코드 블록 안에서 직접 예외를 잡아서 처리하는 방식
- 장점: 즉각적이고 단순한 처리 가능
- 단점: 코드가 길어지면 여러 곳에서 같은 예외 처리 중복되며, 컨트롤러나 서비스 로직이 지저분해진다.
Spring Boot 예외처리 방법
일반 자바 애플리케이션은 예외가 터지면 예외를 직접 처리(try-catch)하여 콘솔에 에러메시지를 띄우거나, 혹은 그냥 프로그램이 강제 종료되었다.
그러나, 스프링은 주로 웹 애플리케이션을 만들 때 사용하기 때문에 예외가 발생하면, 스프링이 이를 감지하여 클라이언트에게 HTTP 응답으로 바꿔주는 과정이 필요하다. 따라서 발생하는 수많은 예외를 자동으로 감지하고, 우리가 원한다면 직접 제어할 수 있는 기능이 필요하다.
1) 기본 방식: @ExceptionHandler (잘 사용 안함)
Spring Boot에서 예외를 처리를 방법 중 하나는 컨트롤러 클래스 안에 직접 예외 처리 메서드를 넣는 방식이다.
@RestController
public class SampleController {
@GetMapping("/divide")
public int divide(@RequestParam int a, @RequestParam int b) {
return a / b; // b가 0이면 ArithmeticException 발생
}
// 이 컨트롤러에서 발생하는 ArithmeticException 예외만 처리된다.
@ExceptionHandler(ArithmeticException.class)
public String handleArithmeticException(ArithmeticException e) {
return "잘못된 요청입니다: " + e.getMessage();
}
}
- 해당 컨트롤러에서 발생한 예외만 처리
- 따라서 다른 컨트롤러에서 예외가 나면 적용 되지 않는다.
- 작고 단순한 앱이면 괜찮지만, 크거나 여러 컨트롤러가 있으면 불편하다.
2) 공통 예외 처리 @ControllerAdvice
프로젝트 전체에서 발생한 예외를 한 handler에서 공통으로 처리하는 전역 예외처리 방식이다.
@ControllerAdvice
public class GlobalExceptionHandler {
// 모든 곳에서 발생한 ArithmeticException 예외가 처리된다.
@ExceptionHandler(ArithmeticException.class)
public ResponseEntity<String> handleArithmeticException(ArithmeticException e) {
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body("수학적 오류가 발생했습니다: " + e.getMessage());
}
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleGeneralException(Exception e) {
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("알 수 없는 오류가 발생했습니다.");
}
}
- @ControllerAdvice는 모든 컨트롤러에서 발생한 예외를 감지해서 처리한다.
- 예외 종류별로 @ExceptionHandler를 나눠서 상황에 맞게 응답을 줄 수 있다.
- 중복 줄이고, 유지보수 편하고, 코드도 깔끔하기 때문에 보통 이 방식을 많이 사용한다.
ResponseStatusException
ResponseStatusException은 예외 클래스를 따로 만들지 않고, 즉시 상태 코드와 메시지를 던지고 싶을 때 사용하는 Spring의 예외 클래스이다.
@Service
public class PersonService {
public String greet(String name) {
if (name == null || name.isBlank()) {
throw new ResponseStatusException(
HttpStatus.BAD_REQUEST,"이름이 없습니다. 인사하려면 이름이 필요합니다."
);
}
return name + "님, 안녕하세요!";
}
}
- 오류 발생 시 설정한 상태코드와 메시지가 자동으로 반환
(오류를 처리할 핸들러가 별도로 필요하지 않다.) - 간단한 예외 상황에서 바로 응답하고 싶을 때 유용
- 단, 복잡한 예외 로직 처리은 어렵다.
커스텀 예외처리
자바에서 기본으로 제공하는 예외 (NullPointerException, IllegalArgumentException 등)만으로는 비즈니스 로직의 의미를 명확하게 표현하기 어려운 경우, 개발자가 직접 정의한 예외 클래스를 사용할 수 있다.
| 기존 예외 | 커스텀 예외 | |
| 예외 유형 | 기술적인 오류 중심 | 도메인 의미 중심 |
| 예시 | IllegalArgumentException | NameRequiredException |
| 예외 이름의 의미 | 어떤 상황인지 알기 어려움 | 예외 이름만으로도 원인 파악 가능 |
| 형태 | 대부분 동일한 형태 | 예외마다 상태코드와 메시지 지정, 처리 로직 분기 가능 |
사용자 정의 예외 클래스 (예외 정의)
public class NameRequiredException extends RuntimeException {
public NameRequiredException(String action) {
super("이름이 없습니다. " + action + "하려면 이름이 필요합니다.");
}
}
서비스 클래스 (예외 발생)
@Service
public class PersonService {
public String greet(String name) {
if (name == null || name.isBlank()) {
throw new NameRequiredException("인사");
}
return name + "님, 안녕하세요!";
}
}
전역 예외 처리 클래스 (예외 처리)
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(NameRequiredException.class)
public ResponseEntity<String> handleNameRequired(NameRequiredException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
}'Spring' 카테고리의 다른 글
| [Spring] Validation (1) | 2025.05.16 |
|---|---|
| [Spring] Spring Bean 등록과 의존성 주입 (0) | 2025.05.15 |
| [Spring] JDBC 시작하기 (0) | 2025.05.14 |
| [Spring] 요청 · 응답 데이터 (0) | 2025.05.12 |
| [Spring] Spring에서 Annotation을 사용하는 이유와 Spring Bean 관리 (0) | 2025.05.12 |