티스토리 뷰

728x90
반응형

 

 

 

대망의 API - 서비스 계층의 연동 마지막 글이다.

 

아직은 에러 메시지가 클라이언트가 아닌 콘솔에만 출력되고 있다.

 

지난 글에서 사용자 정의 예외(Custom Exception) 클래스를 전부 구축했으니

 

이번 글에선 GlobalExceptionAdvice 클래스와 ErrorResponse 클래스를 수정하고,

 

포스트맨을 이용해 다양한 에러 메시지를 출력해 본다.

 

2022.08.25 - [개발/Spring] - [Spring]Spring MVC - 비즈니스로직 예외 던지기(throw) 및 처리

 

[Spring]Spring MVC - 비즈니스로직 예외 던지기(throw) 및 처리

지난 글까진 @ExceptionHandler와 @RestControllerAdvice를 이용한 예외 처리에 대해 알아보았다. 또한 예외 처리는 오류 처리(Trouble Shooting)라고도 불리며 오류 발생 시 대응하는 방법을 제시하는 개념이라

gnidinger.tistory.com

 

getMember()에 요청 전송 시 에러 메시지 출력

 

가장 먼저 지난 글 마지막에 구현했던 handleBusinessLogicException()에서 처리된 예외를 출력해보자.

 

위 사진에서 올린 바와 같이 현재는 콘솔에만 메시지가 출력되는 상황이다.

 

먼저 핸들러 메서드가 받은 에러메세지를 기반으로 필요한 정보만 전달하는 ErrorResponse 클래스를 수정한다.

package com.gnidinger.response;

import com.codestates.exception.ExceptionCode;
import lombok.Getter;
import org.springframework.validation.BindingResult;

import javax.validation.ConstraintViolation;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Getter
public class ErrorResponse {
    private int status;
    private String message;

    public ErrorResponse(int status, String message) {
        this.status = status;
        this.message = message;
    }
    ...
    public static ErrorResponse of(ExceptionCode exceptionCode) {
        return new ErrorResponse(exceptionCode.getStatus(), exceptionCode.getMessage());
    }
    ...
}

먼저 에러 메시지를 담을 변수와 생성자를 추가하고,

 

Static Factory Method(정적 메서드) of()를 생성해준다.

 

이는 enum으로 작성했던 ExceptionCode를 매개변수로 받아 Status와 Message를 전달하는 역할을 한다.

 

계속해서 에러를 처리할 GlobalExceptionAdvice 클래스를 수정하자.

package com.gnidinger.advice;

import com.codestates.exception.BusinessLogicException;
import com.codestates.response.ErrorResponse;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.validation.ConstraintViolationException;

@RestControllerAdvice
public class GlobalExceptionAdvice {
    ...
    @ExceptionHandler
    public ResponseEntity handleBusinessLogicException(BusinessLogicException e) {
        System.out.println(e.getExceptionCode().getStatus());
        System.out.println(e.getMessage());

        final ErrorResponse response = ErrorResponse.of(e.getExceptionCode());
//  return new ResponseEntity<>(HttpStatus.valueOf(e.getExceptionCode().getStatus())); // 기존 코드
        return new ResponseEntity<>(response, HttpStatus.valueOf(e.getExceptionCode().getStatus()));
    }
}

먼저 Static Factory Method(정적 메서드) of()를 사용해 객체를 생성한다.

 

Custom Exception인 BusinessLogicException 클래스에서 ExceptionCode(Status, Message)를 읽어온다.

 

리턴하는 ResponseEntity<>에 HttpStatus만 바로 담던 부분에 response를 추가해

 

response(Status, Message)와 HttpStatus(404 NOT_FOUND)를 담아 유연하게 만들어주고 있다.

 

콘솔에 출력하던 부분은 삭제하지 않고 그대로 두었다.

 

클라이언트를 통해 요청을 보내면 의도했던 메시지가 출력되는 것을 확인할 수 있다.

 

 

ExceptionHandler 구현 - METHOD_NOT_ALLOWED

 

이어서 핸들러 메서드에 맞지 않는 요청을 보낼 때의 에러 메시지를 출력해 보자.

 

예를 들자면 POST로 보내야 할 요청을 PATCH로 잘못 보냈을 때의 에러 메시지이다.

 

먼저 HttpStatus를 전달해줄 메서드를 ErrorResponse에 추가한다.

package com.gnidinger.response;
    ...
import org.springframework.http.HttpStatus;
    ...

@Getter
public class ErrorResponse {
    ...
    public static ErrorResponse of(HttpStatus httpStatus) {
        return new ErrorResponse(httpStatus.value(), httpStatus.getReasonPhrase());
    }
    ...
}

이번엔 HttpStatus를 입력받아 value()(405), getReasonPhrase()(Method Not Allowed)를 읽어온다.

 

계속해서 GlobalExceptionAdvice 클래스에 해당 에러를 처리할 핸들러 메서드를 작성해 보자.

package com.gnidinger.advice;
...
import org.springframework.web.HttpRequestMethodNotSupportedException;
...

@RestControllerAdvice
public class GlobalExceptionAdvice {
    @ExceptionHandler
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    public ErrorResponse handleHttpRequestMethodNotSupportedException
            (HttpRequestMethodNotSupportedException e) {
        System.out.println(HttpStatus.METHOD_NOT_ALLOWED.value());
        System.out.println(HttpStatus.METHOD_NOT_ALLOWED.getReasonPhrase());

        final ErrorResponse response = ErrorResponse.of(HttpStatus.METHOD_NOT_ALLOWED);

        return response;
    }
}

이번엔 고정된 HttpStatus를 사용할 것이기 때문에 @ResponseStatus를 붙여주었다.

 

value()와 getReasonPhrase()를 콘솔에도 출력하는 것을 확인할 수 있다.

 

결과는 아래와 같다.

 

 

ExceptionHandler 구현 - INTERNAL_SERVER_ERROR

 

마지막으로 개발자의 구현 실수로 발생하는 예외를 처리하기 위한 코드를 구현해보자.

 

먼저 NullPointerException을 유도하기 위해 MemberService 클래스의 deleteMember()를 수정하자.

    public void deleteMember(long memberId) {
        String logResult = null;
        System.out.println(logResult.toUpperCase());
    }

ErrorResponse는 위에서 작성했으니 GlobalExceptionAdvice 클래스에 핸들러 메서드만 추가하면 끝이다.

    @ExceptionHandler
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ErrorResponse handleException(Exception e) {
        System.out.println(HttpStatus.INTERNAL_SERVER_ERROR.value());
        System.out.println(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase());

        final ErrorResponse response = ErrorResponse.of(HttpStatus.INTERNAL_SERVER_ERROR);

        return response;
    }

위의 두 케이스와 동일한 방식으로 정의했으며, 결과는 아래와 같다.

 

이렇게 에러 메시지를 클라이언트에 출력하는 것까지 구현해 보았다.

 

API ↔ 서비스 계층의 연동이라는 게 아직까지도 막연하고 코끼리 다리를 만지는 기분이 들지만,

 

계속 열심히 정리하고 복습해야겠다.

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함