실제 예외 처리 흐름
물론입니다! 아래는 Spring Boot REST API 프로젝트에서 Controller → Service → Exception 처리까지 전체 흐름을 보여주는 실제 사용 예시입니다. 주석과 함께 자세히 설명드릴게요.
✅ 예시 시나리오
사용자 등록 API에서 중복 이메일이 들어오면 DuplicateEmailException
을 발생시키고, 이를 @RestControllerAdvice
에서 처리합니다.
1. 사용자 정의 예외 클래스 (중복 이메일 예외)
package com.example.exception;
public class DuplicateEmailException extends RuntimeException {
public DuplicateEmailException(String email) {
super("이미 사용 중인 이메일입니다: " + email);
}
}
2. 서비스 계층
package com.example.service;
import com.example.exception.DuplicateEmailException;
import com.example.model.User;
import com.example.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
public void registerUser(String email, String name) {
if (userRepository.existsByEmail(email)) {
throw new DuplicateEmailException(email);
}
User user = new User(email, name);
userRepository.save(user);
}
}
3. 컨트롤러 계층
package com.example.controller;
import com.example.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@PostMapping("/register")
public ResponseEntity<String> register(@RequestParam String email, @RequestParam String name) {
userService.registerUser(email, name);
return ResponseEntity.ok("회원가입 성공");
}
}
4. 공통 예외 처리기 (@RestControllerAdvice
)
@RestControllerAdvice
)package com.example.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
@RestControllerAdvice
public class RestGlobalExceptionHandler {
@ExceptionHandler(DuplicateEmailException.class)
public ResponseEntity<ErrorResponse> handleDuplicateEmail(DuplicateEmailException ex, HttpServletRequest request) {
ErrorResponse response = new ErrorResponse(
LocalDateTime.now(),
HttpStatus.CONFLICT.value(),
"Duplicate Email",
ex.getMessage(),
request.getRequestURI()
);
return new ResponseEntity<>(response, HttpStatus.CONFLICT);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleAll(Exception ex, HttpServletRequest request) {
ErrorResponse response = new ErrorResponse(
LocalDateTime.now(),
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"Internal Server Error",
"시스템에 문제가 발생했습니다.",
request.getRequestURI()
);
return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
🔁 실행 결과 예시
✅ 성공 시
POST /api/users/register?email=new@example.com&name=홍길동
200 OK
"회원가입 성공"
❌ 중복 이메일 예외
POST /api/users/register?email=test@example.com&name=홍길동
409 Conflict
{
"timestamp": "2025-06-26T11:25:00.123",
"status": 409,
"error": "Duplicate Email",
"message": "이미 사용 중인 이메일입니다: test@example.com",
"path": "/api/users/register"
}
이 예시는 실무에서 매우 자주 사용하는 구조입니다.
✅ 1. @RestControllerAdvice
가 하는 일
@RestControllerAdvice
가 하는 일@RestControllerAdvice
는 전체 애플리케이션에서 발생한 예외를 가로채어 처리해주는 글로벌 예외 처리기입니다.@ExceptionHandler(SomeException.class)
와 함께 사용되어, 특정 예외 타입이 발생했을 때 자동으로 해당 메서드로 우회되며 처리됩니다.
✅ 2. 왜 Controller에서 throws
가 없어도 동작하는가?
throws
가 없어도 동작하는가?throws
는 체크 예외 (checked exception) 처리 시 컴파일러가 요구합니다.하지만 대부분의 애플리케이션 예외 (
RuntimeException
계열)는 **언체크 예외 (unchecked exception)**이며,throws
를 명시하지 않아도 됩니다.따라서, 예외가 Controller 내부에서 발생하면 자동으로 컨트롤러 바깥으로 전파되고,
@RestControllerAdvice
에서 캐치하여 응답합니다.
// throws 없어도 런타임 예외는 밖으로 던져짐
@PostMapping("/register")
public ResponseEntity<String> register(@RequestParam String email) {
userService.registerUser(email); // 내부에서 런타임 예외 발생 가능
return ResponseEntity.ok("회원가입 성공");
}
✅ 3. 왜 "마지막에 예외 처리"라고 말하는가?
@RestControllerAdvice
는 최종 예외 처리자입니다. → 즉, 중간에서 try-catch 하지 않는 이상, 예외는 계속 위로 올라가@RestControllerAdvice
에서 처리됩니다.Spring MVC가 요청을 처리하는 DispatcherServlet → Controller → Service → 예외 발생 순서에서, 예외가 어디서 터지든 결국
@RestControllerAdvice
로 모이게 되는 구조입니다.
✅ 요약
@RestControllerAdvice
때문에 예외가 마지막에 처리됨?
✅ 네, 전역 예외 핸들러 역할
Controller에 throws
없어도 되는 이유는?
✅ RuntimeException은 언체크 예외라 명시적 throws
불필요
어디서 던져도 @RestControllerAdvice
가 잡나?
✅ 중간에 catch 안 하면 모두 거기서 처리됨
Last updated