Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error message globalization #32

Open
corhythm opened this issue May 27, 2022 · 4 comments
Open

Error message globalization #32

corhythm opened this issue May 27, 2022 · 4 comments

Comments

@corhythm
Copy link

corhythm commented May 27, 2022

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
@Getter
public enum ErrorCode {

    // Common
    INVALID_INPUT_VALUE(400, "C001", "Invalid input value"),
    METHOD_NOT_ALLOWED(405, "C002", "Method not allowed"),
    ENTITY_NOT_FOUND(400, "C003", "Entity is not found"),
    INTERNAL_SERVER_ERROR(500, "C004", "Server error"),
    INVALID_TYPE_VALUE(400, "C005", "Invalid type value"),
    HANDLE_ACCESS_DENIED(403, "C006", "Access is denied"),


    // Admin
    ADMIN_NOT_FOUND(400, "A001", "Admin account is not found"),
    ADMIN_PASSWORD_MISMATCHED(400, "A002", "Admin password is mismatched"),
    ADMIN_AND_GROUP_NOT_MAPPED(400, "A003", "Admin and root group is not mapped."),
    
    ;

    private final String code;
    private final String message;
    private final int status;

    ErrorCode(final int status, final String code, final String message) {
        this.status = status;
        this.message = message;
        this.code = code;
    }
}

안녕하세요. 쓰신 글 너무 감명깊게 읽었습니다. 그래서 제가 현재 진행중인 프로젝트에 @cheese10yun 님의 global exception 전략을 적용하고 싶은데 한 가지 문제가 생겨서 여쭤보고 싶어서 이렇게 글 남깁니다.
먼저 제가 이해한 바로는 @Valid로 예외가 발생한 경우에만 errors에 해당 FieldError가 담기고 그 이외에 개발자가 의도적으로 발생시켜준 예외(BussinessException을 상속받은 예외)에는 FieldError가 담기지 않는 거로 저는 이해했습니다.

현재 저는 웹 프로젝트를 개발하고 있는데 해당 프로젝트는 4개 국어(한국어, 영어, 일본어, 스페인어)를 지원하기 때문에 에러 메시지 역시 국제화를 해줘야 합니다. 하나의 언어만 지원한다면 에러 메시지를 ErrorCode의 message에 적어주면 좋겠지만, 4개 국어를 지원하기 때문에 에러 메시지가 동적으로 바뀌어야 합니다. 그래서 messages(messages_ko.properties, messages_en.properties, messages_ja.properties, messages_es.properties)에 각각의 언어로 적힌 에러메시지를 정의해놓고, 해당 메시지를 locale 값을 기준으로 동적으로 ErrorCode의 메시지 부분에 바인딩 해주고 싶습니다. FieldError 같은 경우는 필드에 메시지를 정의해주면 알아서 번역이 되지만(e.g. @NotNull(message = "{login.password.notnull}")) 필드에서 발생하는 에러가 아닌 개발자가 던지는 에러에 에러 메시지는 어떻게 동적을 바인딩할 수 있는지 잘 감이 안 옵니다... 제가 하고 싶은 방법은 2가지 중에 하나입니다.

  • ErrorCode enum class에서 -> INVALID_INPUT_VALUE(400, "c001", "{input.invalid_value}") 같이 작성을 하면 Locale 값에 의해 동적으로 메시지 바인딩
  • 에러를 던지는 부분 코드에서, throw new AdminUserNotFoundException("{input.invalid_value}", ErrorCode.ADMIN_NOT_FOUND);

사실 2번은 제가 첨부한 코드에서 이미 error message를 정의한 상태에서 메시지 부분만 오버라이팅한다는 점이 조금 어색하지만 enum class 내에서 resouces의 메시지를 동적으로 바인딩할 수 없다면 2번도 나쁘지 않다고 생각합니다. @cheese10yun 님 같으면 어떻게 하실지 궁금합니다.

그리고 웹 프로젝트다 보니, 예외가 발생하면 사용자에게 에러 메시지를 보여주려고 하는데, 만약 field error 값이 담겨있다면 field error의 reason 값을 에러 메시지로 보여주고 그 이외에 개발자가 발생시킨 에외는 field error가 없다보니 message 필드 값을 보여주려고 하는데, 그냥 일관성 있게 예외가 발생하면 message 부분만 보여주는 게 더 나을려나요?

너무 두서없이 글을 써서 죄송하고 답변해주시면 정말 감사하겠습니다!!

@cheese10yun
Copy link
Owner

@corhythm

  • ErrorCode enum class에서 -> INVALID_INPUT_VALUE(400, "c001", "{input.invalid_value}") 같이 작성을 하면 Locale 값에 의해 동적으로 메시지 바인딩
  • 에러를 던지는 부분 코드에서, throw new AdminUserNotFoundException("{input.invalid_value}", ErrorCode.ADMIN_NOT_FOUND);

두 가지 방법 모두 좋다고 생각합니다. 위 예제는 주로 내부 시스템에서 사용하는 기준으로 작성한 부분이 많습니다. 일반적인 유저향 서비스의 경우에 국제화 메시지를 맞춰야 한다면 두 가지 방법 모두 좋다고 생각합니다

저 같은 경우에는 두 가지 방법 모두 사용할거 같습니다. 사용자가 직접 메시지를 내주고 예외를 발생 시키면 해당 메시지가 최종적으로 내려가게 하고 그렇지 않으면 에러 기존 Error Code 메시지를 따르게 진행할거 같습니다.

이런식의 방식은 error message가 개별적으로 작성하여 문구의 통일성 및 중복이 많아지는 부분이 단점이 있는데 따로 messages.properties를 관리 하고 있으니 두 가지 방법 모두 사용해도 무방할거 같습니다.

두 번째 질문은 서버는 최대한 예외가 발생하면 그에 맞는 메시지를 적절하게 내보내주는 것이 맞고 그에 따른 활용은 클라이언트 사이드에서 결정 해야 한다고 생각 합니다.

여러 필드의 요청이 올바르지 않는 경우 서버는 문제있는 필드들, 올바르지 않는 사유 등등을 내려주는 것이고 그것을 활용하는 것은 클라이언트의 영역이리거 생각 합니다. 그래서 서버는 본인의 메시지를 (field가 있는 경우 포함하여) 내려주는 구조가 더 좋다고 생각합니다.

@corhythm
Copy link
Author

@cheese10yun 답변해주셔서 정말 감사합니다. 프로젝트를 어떻게 구성해야 될지 이해하는 데 큰 도움이 되었습니다.
@cheese10yun 님 답변을 종합해서 저는 enum 클래스에서 ErrorCode에서 HANDLE_ACCESS_DENIED(403, "C006", "${error.handle_access_denied)"),이런 식으로 구성하고 싶습니다. 하지만 enum 클래스에서 properties 파일에 접근하는 걸 이틀 동안 잘 못하고 있는데(@Valid로 오류가 발생하는 건 MessageSource로 할 수 있지만 Enum 클래스에서 이게 가능한지 잘 모르겠습니다.), 이게 혹시 가능한 방법이면 검색 키워드 좀 알려주실 수 있을까요?

@cheese10yun
Copy link
Owner

@corhythm
굳이 Enum class에서 메시지를 접근하지 마시고 https://github.com/cheese10yun/spring-guide/blob/master/src/main/java/com/spring/guide/global/error/ErrorResponse.java 에서 Error Code에 정의한 message.properties 키 값을 가져다가 읽어 오는게 더 좋을거 같네요

ErrorCode 객체에서 message.properties 파일을 읽어 오고 국가 별로 분기 처리하는 것도 너무 역할과 책임이 과중해지는거 같아요.

@corhythm
Copy link
Author

@cheese10yun 넵 이해했습니다. 이틀동안 고민했는데 답변해주셔서 너무 감사드립니다 ㅠㅠ @cheese10yun 님이 쓰신 글 너무 잘 읽고 있어요!! 좋은 글 감사드립니다 👍

@cheese10yun cheese10yun reopened this Jun 1, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants