kyupid / java-chess-again

자바/스프링 웹 전반 공부
4 stars 0 forks source link

예외처리 대신에 `ErrorMessageDto`에 메시지를 담아 응답하기 (에러처리하기) #56

Open kyupid opened 2 years ago

kyupid commented 2 years ago
    public String sendAuthMail(String userId) throws Exception {
        String authKey;
        if (!hasMember(userId)) {
            authKey = mss.sendAuthMail(userId);
        } else {
            throw new Exception("중복된 이메일이 존재합니다");
        }
        return authKey;
    }

이런 코드가 있다 근데 예외를 던져주면 프론트에서 500에러를 받는다 그게 아니라 메시지를 넘겨주고 싶은데 어떡하지?

예를 들면

    public String sendAuthMail(String userId) {
        String authKey;
        if (!hasMember(userId)) {
            authKey = mss.sendAuthMail(userId);
        } else {
            return ErrorMessageResponseDto.builder()
                    .result(false)
                    .message("중복된 이메일이 존재합니다")
                    .build();
        }
        return authKey;
    }

이렇게 하고 싶은데 자바는 타입 하나만 리턴할수 있잖아?

간단해보이는데 어떻게 해야할지 모르겠네 하 머리좀 굴려봐

kyupid commented 2 years ago
public class ErrorMessageResponseDto {
    private boolean result;
    private String message;
}

이렇게 새로 추가할게 아니라 원래 있던 객체에 필드를 추가해서 사용하면 될거같긴 한데.. 그렇게 하자니 Dto가 가지는 책임은 하나인데 두개로 늘어나버리니까 이렇게 하면 안될거 같아 게다가 result, message는 딱봐도 엄청 많이 쓰일거같잖아 필요할때마다 다른 Dto에 덕지덕지 붙여서 쓰는건 말이 안됨


public class MemberMailAuthInfoDto {
    private String email;
    private String authKey;

    private boolean result;
    private String message;
}
kyupid commented 2 years ago

새리-issue tracker 리포지토리 제너릭 활용한 ApiResult 클래스 참고하기

kyupid commented 2 years ago

일반적으로 우리는 500 대 에러를 클라이언트에 보여주면 안됩니다. 500 에러는 요청을 처리하는데 서버에서 예상하지 못한 예>외발생 같은 경우에 대한 신호이기 때문입니다. 그러므로, 서버 내부 에러는 클라이언트의 비지니스가 아닙니다.

대신에, 우리는 부지런히 내부에러를 처리하거나 캐치해서 400 대 에러를 내려주어야 합니다. 예를들어, 요청된 자원이 존재하지 않는다면 500 에러가 아닌 404 에러를 주어야 합니다.

출처: https://pjh3749.tistory.com/273 [JayTech의 기술 블로그]

kyupid commented 2 years ago
package com.morazo.core.dto;

public class ApiResult<T> {

    private T data;
    private String errorMessage;

    private ApiResult(T data, String errorMessage) {
        this.data = data;
        this.errorMessage = errorMessage;
    }

    public static <T> ApiResult<T> ok(T data) {
        return new ApiResult<>(data, null);
    }

    public static ApiResult<?> fail(String errorMessage) {
        return new ApiResult<>(null, errorMessage);
    }

    public T getData() {
        return data;
    }

    public String getErrorMessage() {
        return errorMessage;
    }
}

코드 출처: https://github.com/swing-park/issue-tracker/blob/BE/feature/88/exception/BE/issuetracker/src/main/java/com/codesquad/issuetracker/dto/ApiResult.java

return 할때 ApiResult<?> foo 이런식으로 주면 됨. 예를 들어서 이전에 해결하려했던 코드가

    public String sendAuthMail(String userId) {
        String authKey;
        if (!hasMember(userId)) {
            authKey = mss.sendAuthMail(userId);
        } else {
            return ErrorMessageResponseDto.builder()
                    .result(false)
                    .message("중복된 이메일이 존재합니다")
                    .build();
        }
        return authKey;
    }

위와같이 따로 Dto를 만들어서 에러메시지를 내보내고 싶었다 근데 리턴타입을 하나만 내보낼수있으니까 어떻게 해결해야할지 고민이었던 거

            return ErrorMessageResponseDto.builder()
                    .result(false)
                    .message("중복된 이메일이 존재합니다")
                    .build();

대신에 throw new Exception("중복된 이메일이 존재합니다"); 라고 하면 예상하고 있던 내부 에러를 클라이언트에 뿌려주는 게 되니까 말이안됨. (여기서 드는 의문은 그럼 throw new Exception 은 언제 사용해줘야하는거? 예상치못한에러라면....뭘 뜻하는걸까) https://github.com/kyupid/java-chess-again/issues/56#issuecomment-1008233276

어쨋든 내가 해결하려고했던건 제네릭을 이용해서 쉽게 해결할수있었다.

    public ApiResult<?> sendAuthMail(String userId) {
        String authKey;
        if (!hasMember(userId)) {
            authKey = mss.sendAuthMail(userId);
        } else {
            return ApiResult.fail("중복된 이메일이 존재합니다");
        }
        return ApiResult.ok(authKey);
    }
    @PostMapping("/signup")
    public ApiResult<?> sendSignUpMail(@RequestBody MemberSignUpRequestDto dto, HttpServletRequest request) {
        ApiResult<?> authKey = memberService.sendAuthMail(dto.getUserId());
        if(authKey.getData() != null) {
            HttpSession session = request.getSession();
            session.setAttribute("authKey", authKey);
            //...
        }
        return authKey;
    }

이렇게 내보내주면됨. 첨엔 제네릭안에 ? 주고 제네릭 타입을 알수없어도 저렇게 내보낼수있는지 몰랐다 근데 이렇게 보니까 그냥 언어차원에서 여러타입을 내보낼수있도록 해주면 안되나? 문제가 뭘까?

kyupid commented 2 years ago

팔로잉 퀘스쳔 두개:

  1. 예외처리 throw new Exception(); 은 언제 사용해줘야하는거? 예상치못한에러라면....뭘 뜻하는걸까
  2. 자바는 언어차원에서 여러타입을 내보낼수있도록 해주면 안되나? 문제가 뭘까?