개발/부트캠프

본캠프 : GlobalExceptionHandler 사용

EJ EJ 2025. 3. 5. 21:19

1. common 패키지에 advice 패키지 추가 후 GlobalExceptionHandler 클래스 생성

 

2. GlobalExceptionHandler 클래스 작성

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(InvalidCredentialException.class)
    public ResponseEntity<Map<String, Object>> handleInvalidCredentialException(InvalidCredentialException ex) {
        return getErrorResponse(HttpStatus.UNAUTHORIZED, ex.getMessage());
    }

    private ResponseEntity<Map<String, Object>> getErrorResponse(HttpStatus status, String message) {
        Map<String, Object> errorResponse = new HashMap<>();
        errorResponse.put("status", status.name());
        errorResponse.put("code", status.value());
        errorResponse.put("message", message);

        return new ResponseEntity<>(errorResponse, status);
    }
}

 

3. common 패키지에 exception 패키지 추가 후 ApplicationException 클래스 생성/작성

@Getter
public class ApplicationException extends RuntimeException {
    private final HttpStatus status;

    public ApplicationException(String message, HttpStatus status) {
        super(message);
        this.status = status;
    }
}

 

4. common 패키지에 exception 패키지 추가 후 InvalidCredentialException 클래스 생성/작성

public class InvalidCredentialException extends ApplicationException {

    public InvalidCredentialException(String message) {
        super(message, HttpStatus.UNAUTHORIZED);
    }
}

 

5. 서비스 로직에서 exception 적용

@Service
@RequiredArgsConstructor
public class AuthService {

    private final MemberRepository memberRepository;
    private final PasswordEncoder passwordEncoder;

    // 로그인
    @Transactional(readOnly = true)
    public AuthLoginResponseDto login(AuthLoginRequestDto dto) {
        Member member = memberRepository.findByEmail(dto.getEmail()).orElseThrow(
                () -> new InvalidCredentialException("해당 이메일이 없습니다."));

        if (!passwordEncoder.matches(dto.getPassword(), member.getPassword())) {
            throw new InvalidCredentialException("비밀번호가 일치하지 않습니다.");
        }

        return new AuthLoginResponseDto(member.getId());
    }

 

끝!

 

[ GlobalExceptionHandler의 동작 원리 및 예외 처리 순서 ]

Spring Boot의 전역 예외 처리(Global Exception Handling) 기능을 사용하여 예외가 발생했을 때 일괄적으로 처리하는 방법을 구현하고 있습니다.

 

<1> @ControllerAdvice를 통한 글로벌 예외 처리

동작 원리

  1. @ControllerAdvice
    • Spring MVC의 전역 예외 처리 클래스를 정의하는 어노테이션
    • 컨트롤러에서 발생하는 예외를 이 클래스에서 한 곳에서 처리할 수 있도록 해줌
  2. @ExceptionHandler(InvalidCredentialException.class)
    • InvalidCredentialException이 발생하면 이 메서드가 자동으로 실행됨
    • HTTP 상태 코드 401 Unauthorized와 함께 JSON 형태의 응답을 반환
  3. getErrorResponse(HttpStatus status, String message)
    • 에러 메시지를 Map<String, Object> 형식으로 변환
    • status, code, message를 포함하여 일관된 오류 응답을 생성

예외 발생 시 응답 예시 (InvalidCredentialException 발생)

{
    "status": "UNAUTHORIZED",
    "code": 401,
    "message": "해당 이메일이 없습니다."
}
 
 

<2> ApplicationException (커스텀 예외의 부모 클래스)

동작 원리

  • 모든 커스텀 예외의 부모 클래스 역할을 하는 ApplicationException
  • RuntimeException을 상속받아 Unchecked Exception(체크 예외가 아님)으로 만듦
  • 예외 발생 시 HTTP 상태 코드를 함께 저장할 수 있도록 필드(status) 추가

 

<3> InvalidCredentialException

동작 원리

  • ApplicationException을 상속받아 예외 처리
  • 예외가 발생하면 **HTTP 상태 코드 401 Unauthorized**가 자동 설정됨

=> InvalidCredentialException 예외가 발생하면, GlobalExceptionHandler가 이를 감지하고 적절한 JSON 응답을 반환

  • 401 Unauthorized 상태 코드와 함께 에러 메시지를 포함한 JSON 응답을 전송

 

🚀 이 방식의 장점

  1. 예외 처리 코드의 중복 제거 → 서비스 코드에서 직접 ResponseEntity를 반환하지 않아도 됨
  2. 일관된 오류 응답 제공 → 모든 예외 응답이 같은 형식(status, code, message)을 가짐
  3. 유지보수 용이 → 새로운 예외 타입이 추가될 경우 @ExceptionHandler만 추가하면 됨

'개발 > 부트캠프' 카테고리의 다른 글

본캠프 : 향상된 for문, stream 사용법  (0) 2025.03.08
본캠프 : 비밀번호 암호화  (0) 2025.03.07
본캠프 : Git rebase  (0) 2025.03.04
본캠프 : 페이징 조회 Pageable  (0) 2025.03.03
본캠프 : BaseEntity 사용  (0) 2025.03.02