프로젝트 (Java)/예약마켓

[프로젝트] 6. GlobalExceptionHandler 설정

hihyuk 2024. 1. 25. 09:47

ExceptionHandlerFilter 구현

ExceptionHandlerFilter는 Spring Security 필터 체인의 일부로서, 요청 처리 중 발생하는 예외를 잡아 처리합니다. 이 필터를 통해 UsernameFromTokenException과 같은 인증 관련 예외뿐만 아니라, RuntimeException 및 기타 예외에 대한 처리도 일관되게 관리할 수 있습니다.

package com.myshop.global.filter;

import com.myshop.global.exception.UsernameFromTokenException;
import com.myshop.global.exception.handler.ErrorDetailResponse;
import com.myshop.global.exception.handler.ErrorResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@Component
public class ExceptionHandlerFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        try{
            filterChain.doFilter(request,response);
        } catch (UsernameFromTokenException ex){
            log.info("UsernameFromTokenException handler filter");
            setErrorResponse(HttpStatus.FORBIDDEN,response,ex);
        } catch (RuntimeException ex){
            log.info("RuntimeException exception handler filter");
            setErrorResponse(HttpStatus.FORBIDDEN,response,ex);
        } catch (Exception ex){
            log.info("Exception exception handler filter");
            setErrorResponse(HttpStatus.FORBIDDEN,response,ex);
        }

    }

    public void setErrorResponse(HttpStatus status, HttpServletResponse response,Throwable ex){
        response.setStatus(status.value());
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");

        ErrorResponse errorResponse = new ErrorResponse(new ErrorDetailResponse(ex.getMessage()));
    }
}

 

사용자 정의 예외 클래스

BadRequestExceptionUsernameFromTokenException과 같은 사용자 정의 예외 클래스를 구현하여, 애플리케이션에서 발생할 수 있는 특정한 에러 상황을 명확하게 표현합니다.

BadRequestException

package com.myshop.global.exception;

import lombok.Getter;

@Getter
public class BadRequestException extends RuntimeException {
    public BadRequestException(String message) {
        super(message);
    }
}

UsernameFromTokenException

package com.myshop.global.exception;

public class UsernameFromTokenException extends RuntimeException{
    public UsernameFromTokenException(String message){
        super(message);
    }
}

 

GlobalExceptionHandler 설정

GlobalExceptionHandler@RestControllerAdvice를 사용하여 정의되며, 애플리케이션에서 발생하는 다양한 예외를 처리합니다. 이 클래스에서는 BadRequestException을 처리하는 메소드를 구현하여, 에러 발생 시 사용자에게 HTTP 상태 코드와 에러 메시지를 담은 응답을 반환합니다.

GlobalExceptionHandler

package com.myshop.global.exception.handler;

import com.myshop.global.exception.BadRequestException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.Arrays;
import java.util.Optional;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    /**
     * 사용자 정의 예외처리
     */
    @ExceptionHandler(BadRequestException.class)
    protected ResponseEntity<?> handleBadRequestException(BadRequestException e) {
        String message = Optional.ofNullable(e.getMessage()).orElseGet(() -> "올바른 요청이 아닙니다. ");
        ErrorDetailResponse detailResponse = ErrorDetailResponse.builder().msg(message).build();
        ErrorResponse response = ErrorResponse.builder()
                .errors(Arrays.asList(detailResponse))
                .build();
        return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
    }

}

 

ErrorDetailResponse 및 ErrorResponse 클래스

에러 응답을 구성하는 ErrorDetailResponseErrorResponse 클래스를 구현하여, 에러 정보를 명확하게 구조화하여 반환합니다.

ErrorDetailResponse

package com.myshop.global.exception.handler;

import lombok.Builder;
import lombok.Getter;

@Getter
public class ErrorDetailResponse {
    private String msg;

    @Builder
    public ErrorDetailResponse(String msg) {
        this.msg = msg;
    }
}

ErrorResponse

package com.myshop.global.exception.handler;

import lombok.Builder;
import lombok.Getter;

import java.util.Arrays;
import java.util.List;

@Getter
public class ErrorResponse {
    private List<ErrorDetailResponse> errors;

    @Builder
    public ErrorResponse(List<ErrorDetailResponse> errors) {
        this.errors = errors;
    }

    public ErrorResponse(ErrorDetailResponse error) {
        this.errors = Arrays.asList(error);
    }
}

 

결론

GlobalExceptionHandler와 필터를 통한 예외 처리 방식을 도입함으로써, 애플리케이션의 에러 처리를 중앙에서 관리할 수 있게 되며, API 사용자에게 보다 일관되고 명확한 에러 응답을 제공할 수 있습니다. 이는 애플리케이션의 안정성과 사용자 경험을 모두 향상시키는 중요한 요소입니다.