Java에서 오류 코드 / 문자열을 정의하는 가장 좋은 방법은 무엇입니까?


118

Java로 웹 서비스를 작성 중이며 오류 코드 및 관련 오류 문자열을 정의하는 가장 좋은 방법을 찾으려고합니다 . 숫자 오류 코드와 함께 그룹화 된 오류 문자열이 필요합니다. 오류 코드와 오류 문자열은 모두 웹 서비스에 액세스하는 클라이언트로 전송됩니다. 예를 들어, SQLException이 발생하면 다음을 수행 할 수 있습니다.

// Example: errorCode = 1, 
//          errorString = "There was a problem accessing the database."
throw new SomeWebServiceException(errorCode, errorString);

클라이언트 프로그램에 다음 메시지가 표시 될 수 있습니다.

"오류 # 1이 발생했습니다. 데이터베이스에 액세스하는 데 문제가 있습니다."

내 첫 번째 생각은 Enum오류 코드 중 하나를 사용 toString하고 오류 문자열을 반환하는 메서드를 재정의하는 것이 었습니다. 내가 생각해 낸 것은 다음과 같습니다.

public enum Errors {
  DATABASE {
    @Override
    public String toString() {
      return "A database error has occured.";
    }
  },

  DUPLICATE_USER {
    @Override
    public String toString() {
      return "This user already exists.";
    }
  },

  // more errors follow
}

내 질문은 : 이 작업을 수행하는 더 좋은 방법이 있습니까? 외부 파일에서 읽는 것보다 코드 솔루션을 선호합니다. 이 프로젝트에 Javadoc을 사용하고 있으며 오류 코드를 인라인으로 문서화하고 문서에서 자동으로 업데이트하도록하는 것이 도움이 될 것입니다.


댓글이 늦었지만 언급할만한 가치가 있습니다 ... 1) 예외에 오류 코드가 정말로 필요합니까? 아래 blabla999 답변을 참조하십시오. 2) 사용자에게 너무 많은 오류 정보를 전달하는 데주의해야합니다. 유용한 오류 정보를 서버 로그에 기록해야하지만 클라이언트는 최소한의 정보 만 알려야합니다 (예 : "로그인 문제가 발생했습니다"). 이것은 보안과 스푸 퍼가 발판을 잡는 것을 방지하는 문제입니다.
wmorrison365 2013

답변:


161

분명히 enum 솔루션의 더 나은 구현이 있습니다 (일반적으로 꽤 좋습니다).

public enum Error {
  DATABASE(0, "A database error has occured."),
  DUPLICATE_USER(1, "This user already exists.");

  private final int code;
  private final String description;

  private Error(int code, String description) {
    this.code = code;
    this.description = description;
  }

  public String getDescription() {
     return description;
  }

  public int getCode() {
     return code;
  }

  @Override
  public String toString() {
    return code + ": " + description;
  }
}

대신 설명을 반환하기 위해 toString ()을 재정의 할 수 있습니다. 확실하지 않습니다. 어쨌든 요점은 각 오류 코드에 대해 별도로 재정의 할 필요가 없다는 것입니다. 또한 서수 값을 사용하는 대신 코드를 명시 적으로 지정했습니다. 이렇게하면 순서를 변경하고 나중에 오류를 추가 / 제거하기가 더 쉽습니다.

이것은 전혀 국제화되어 있지 않다는 것을 잊지 마십시오. 그러나 웹 서비스 클라이언트가 로케일 설명을 보내지 않는 한, 어쨌든 쉽게 국제화 할 수 없습니다. 적어도 클라이언트 측에서 i18n에 사용할 오류 코드가있을 것입니다.


13
국제화하려면 설명 필드를 리소스 번들에서 조회 할 수있는 문자열 코드로 바꾸시겠습니까?
Marcus Downing

@Marcus : 그 아이디어가 좋아요. 저는이 일을 꺼내는 데 집중하고 있지만 국제화를 보면 여러분이 제안한대로 할 것입니다. 감사!
William Brendel

@marcus, toString ()이 오버라이드되지 않은 경우 (그럴 필요가 없음) 문자열 코드는 DATABASE 또는 DUPLICATE_USER가 될 열거 형 값 toString () 일 수 있습니다.
루블

@Jon Skeet! 이 솔루션이 마음에 들었습니다. 현지화 (또는 다른 언어로 번역 등)하기 쉬운 솔루션을 만들 수있는 방법이 있습니다. Android에서 사용하는 것을 생각하면 하드 코딩 된 문자열 대신 R.string.IDS_XXXX를 사용할 수 있습니까?
AB

1
@AB : 일단 열거 형을 얻은 후에는 속성 파일 등을 통해 열거 형 값에서 관련 지역화 된 리소스를 추출하는 클래스를 쉽게 작성할 수 있습니다.
Jon Skeet

34

내가 아는 한, 속성 파일에서 오류 메시지를 외부화하는 것을 선호합니다. 이것은 응용 프로그램의 국제화 (언어 당 하나의 속성 파일)의 경우에 매우 유용합니다. 오류 메시지를 수정하는 것도 더 쉬우 며 Java 소스를 다시 컴파일 할 필요가 없습니다.

내 프로젝트에는 일반적으로이 오류에 대한 속성 파일의 키를 포함하는 오류 코드 (문자열 또는 정수,별로 신경 쓰지 않음)가 포함 된 인터페이스가 있습니다.

public interface ErrorCodes {
    String DATABASE_ERROR = "DATABASE_ERROR";
    String DUPLICATE_USER = "DUPLICATE_USER";
    ...
}

속성 파일에서 :

DATABASE_ERROR=An error occurred in the database.
DUPLICATE_USER=The user already exists.
...

솔루션의 또 다른 문제는 유지 보수 가능성입니다. 오류가 2 개 뿐이고 이미 12 줄의 코드가 있습니다. 따라서 관리해야 할 수백 개의 오류가있을 때 열거 파일을 상상해보십시오!


2
내가 할 수 있다면 이것을 1 개 이상 올릴 것입니다. 문자열을 하드 코딩하는 것은 유지 관리에 좋지 않습니다.
Robin

3
인터페이스에 문자열 상수를 저장하는 것은 나쁜 생각입니다. 패키지 또는 관련 영역별로 개인 생성자가있는 최종 클래스에서 열거 형을 사용하거나 문자열 상수를 사용할 수 있습니다. John Skeets가 Enums로 대답하십시오. 확인해주십시오. stackoverflow.com/questions/320588/…
Anand Varkey Philips

21

toString () 오버로딩은 약간 이상해 보입니다. toString ()의 정상적인 사용이 약간 늘어난 것 같습니다.

이건 어떤가요:

public enum Errors {
  DATABASE(1, "A database error has occured."),
  DUPLICATE_USER(5007, "This user already exists.");
  //... add more cases here ...

  private final int id;
  private final String message;

  Errors(int id, String message) {
     this.id = id;
     this.message = message;
  }

  public int getId() { return id; }
  public String getMessage() { return message; }
}

나에게 훨씬 더 깨끗하고 ... 덜 장황 해 보인다.


5
모든 객체 (열거 형은 제외)에 toString ()을 오버로드하는 것은 매우 정상입니다.
cletus

+1 Jon Skeet의 솔루션만큼 유연하지는 않지만 여전히 문제를 훌륭하게 해결합니다. 감사!
William Brendel

2
나는 toString ()이 객체를 식별하는 데 충분한 정보를 제공하는 데 가장 일반적이고 유용하게 사용된다는 것을 의미했습니다. 종종 클래스 이름을 포함하거나 객체 유형을 의미있게 알려주는 방법을 포함합니다. '데이터베이스 오류가 발생했습니다'만 반환하는 toString ()은 많은 상황에서 놀랍습니다.
Cowan

1
나는 이런 식으로 toString ()을 사용하는 것이 약간 '해커'인 것처럼 보이며 Cowan에 동의합니다. 정상적인 사용이 아닌 돈에 대한 빠른 가격입니다. 열거 형의 경우 toString ()은 열거 형 상수의 이름을 반환해야합니다. 이것은 변수의 값을 원할 때 디버거에서 흥미로울 것입니다.
Robin

19

마지막 작업에서 나는 enum 버전에서 조금 더 깊이 들어갔다.

public enum Messages {
    @Error
    @Text("You can''t put a {0} in a {1}")
    XYZ00001_CONTAINMENT_NOT_ALLOWED,
    ...
}

@Error, @Info, @Warning은 클래스 파일에 유지되며 런타임에 사용할 수 있습니다. (메시지 전달을 설명하는 데 도움이되는 몇 가지 다른 주석도 있습니다.)

@Text는 컴파일 타임 주석입니다.

다음을 수행하는 주석 프로세서를 작성했습니다.

  • 중복 된 메시지 번호 (첫 번째 밑줄 앞 부분)가 없는지 확인하십시오.
  • 구문-메시지 텍스트 확인
  • 열거 형 값으로 키가 지정된 텍스트를 포함하는 messages.properties 파일을 생성하십시오.

오류를 기록하고 예외로 래핑하는 데 도움이되는 몇 가지 유틸리티 루틴을 작성했습니다 (원하는 경우).

나는 그들이 그것을 오픈 소스로 만들려고 노력하고 있습니다 ...-Scott


오류 메시지를 처리하는 좋은 방법입니다. 이미 오픈 소스 했습니까?
bobbel

5

java.util.ResourceBundle을 살펴 보는 것이 좋습니다. I18N에 관심을 가져야하지만 그렇지 않더라도 그만한 가치가 있습니다. 메시지를 외부화하는 것은 아주 좋은 생각입니다. 비즈니스 직원이보고자하는 정확한 언어를 입력 할 수있는 스프레드 시트를 제공하는 것이 유용하다는 것을 알게되었습니다. 컴파일 타임에 .properties 파일을 생성하는 Ant 태스크를 작성했습니다. 그것은 I18N을 사소하게 만듭니다.

Spring도 사용하고 있다면 훨씬 좋습니다. 그들의 MessageSource 클래스는 이러한 종류의 일에 유용합니다.


4

이 특정 죽은 말을 계속해서 채찍질하기 위해 최종 고객에게 오류가 표시 될 때 숫자 오류 코드를 잘 사용 했습니다. 최종 고객이 실제 오류 메시지를 자주 잊어 버리거나 잘못 읽었지만 때때로 제공 할 수있는 숫자 값을 유지하고보고 할 수 있기 때문입니다. 실제로 일어난 일에 대한 단서입니다.


3

이를 해결하는 방법에는 여러 가지가 있습니다. 내가 선호하는 접근 방식은 인터페이스를 사용하는 것입니다.

public interface ICode {
     /*your preferred code type here, can be int or string or whatever*/ id();
}

public interface IMessage {
    ICode code();
}

이제 메시지를 제공하는 열거 형을 얼마든지 정의 할 수 있습니다.

public enum DatabaseMessage implements IMessage {
     CONNECTION_FAILURE(DatabaseCode.CONNECTION_FAILURE, ...);
}

이제이를 문자열로 변환 할 수있는 몇 가지 옵션이 있습니다. 문자열을 코드로 컴파일하거나 (주석 또는 열거 형 생성자 매개 변수 사용) 구성 / 속성 파일 또는 데이터베이스 테이블 또는 혼합에서 읽을 수 있습니다. 당신은 항상 당신이 초기 텍스트로 전환 할 수있는 일부 메시지해야하므로 후자는 나의 선호하는 접근 방법이다 (즉. 동안 데이터베이스에 연결하거나 설정을 읽기).

단위 테스트와 리플렉션 프레임 워크를 사용하여 인터페이스를 구현하는 모든 유형을 찾아서 각 코드가 어딘가에서 사용되고 구성 파일에 모든 예상 메시지가 포함되어 있는지 확인하고 있습니다.

https://github.com/javaparser/javaparser 또는 Eclipse 와 같은 Java를 구문 분석 할 수있는 프레임 워크를 사용 하면 열거 형이 사용되는 위치를 확인하고 사용하지 않는 것을 찾을 수도 있습니다.


2

저 (및 저희 회사의 나머지 팀)는 오류 코드를 반환하는 대신 예외를 발생시키는 것을 선호합니다. 오류 코드는 모든 곳에서 확인하고 전달해야하며 코드 양이 커지면 코드를 읽을 수 없게 만드는 경향이 있습니다.

그런 다음 오류 클래스가 메시지를 정의합니다.

추신 : 그리고 실제로 국제화에도 관심이 있습니다!
PPS : 필요에 따라 raise-method를 재정의하고 로깅, 필터링 등을 추가 할 수도 있습니다 (적어도 예외 클래스와 친구가 확장 / 변경 가능한 환경에서).


죄송합니다. Robin,하지만 (적어도 위의 예에서) 두 가지 예외가 있어야합니다. "데이터베이스 오류"와 "중복 사용자"는 완전히 다르기 때문에 개별적으로 잡을 수있는 두 개의 개별 오류 하위 클래스를 만들어야합니다 ( 하나는 시스템이고 다른 하나는 관리자 오류)
blabla999

그리고 하나 또는 다른 예외를 구별하지 않는 경우 오류 코드는 무엇에 사용됩니까? 따라서 적어도 핸들러 위에는 전달되고 전환되는 오류 코드를 처리하는 것입니다.
blabla999

예외의 이름이 오류 코드보다 훨씬 더 설명적이고 자기 설명 적이라고 생각합니다. 좋은 예외 이름 인 IMO를 발견하는 데 더 많은 고려를하는 것이 좋습니다.
duffymo

@ blabla999 아, 내 생각이 정확합니다. 대략적인 예외를 포착 한 다음 "오류 코드 == x, y 또는 z"인 경우 테스트하는 이유는 무엇입니까? 그런 고통과 곡물에 반대합니다. 또한 스택의 다른 수준에서 다른 예외를 포착 할 수 없습니다. 모든 수준에서 포착하고 각각에서 오류 코드를 테스트해야합니다. 그것은 클라이언트 코드를 훨씬 더 장황하게 만듭니다. 즉, OPs 질문에 답해야한다고 생각합니다.
wmorrison365 2013

2
이것은 웹 서비스를위한 것입니다. 클라이언트는 문자열 만 구문 분석 할 수 있습니다. 서버 측에는 클라이언트에 대한 최종 응답에서 사용할 수있는 errorCode 멤버가있는 예외가 여전히 발생합니다.
pkrish

1

조금 늦었지만 나 자신을위한 예쁜 해결책을 찾고있었습니다. 다른 종류의 메시지 오류가있는 경우 간단한 사용자 지정 메시지 팩토리를 추가하여 나중에 원하는 세부 정보와 형식을 지정할 수 있습니다.

public enum Error {
    DATABASE(0, "A database error has occured. "), 
    DUPLICATE_USER(1, "User already exists. ");
    ....
    private String description = "";
    public Error changeDescription(String description) {
        this.description = description;
        return this;
    }
    ....
}

Error genericError = Error.DATABASE;
Error specific = Error.DUPLICATE_USER.changeDescription("(Call Admin)");

편집 : 좋아, 여기에 열거 형을 사용하는 것은 특정 열거 형을 영구적으로 변경하기 때문에 약간 위험합니다. 클래스로 변경하고 정적 필드를 사용하는 것이 더 좋을 것 같지만 더 이상 '=='를 사용할 수 없습니다. 그래서하지 말아야 할 일이 좋은 예라고 생각합니다. (또는 초기화 중에 만하세요) :)


1
EDIT에 완전히 동의합니다. 런타임에 enum 필드를 변경하는 것은 좋은 습관이 아닙니다. 이 디자인으로 모든 사람이 오류 메시지를 편집 할 수 있습니다. 이것은 매우 위험합니다. 열거 형 필드는 항상 최종적이어야합니다.
b3nyc

0

오류 코드 / 메시지 정의에 대한 열거 형은 i18n 문제가 있지만 여전히 좋은 솔루션입니다. 실제로 두 가지 상황이있을 수 있습니다. 코드 / 메시지가 최종 사용자 또는 시스템 통합 자에게 표시됩니다. 나중의 경우에는 I18N이 ​​필요하지 않습니다. 나는 웹 서비스가 나중의 경우라고 생각합니다.


0

interface메시지 상수로 사용 하는 것은 일반적으로 나쁜 생각입니다. 내 보낸 API의 일부로 클라이언트 프로그램에 영구적으로 누출됩니다. 나중에 클라이언트 프로그래머가 해당 오류 메시지 (공개)를 프로그램의 일부로 구문 분석 할 수 있다는 것을 누가 알겠습니까?

문자열 형식의 변경으로 인해 클라이언트 프로그램이 중단 될 수 있으므로이를 지원하기 위해 영원히 잠길 것입니다.


0

아래 예를 따르십시오.

public enum ErrorCodes {
NO_File("No file found. "),
private ErrorCodes(String value) { 
    this.errordesc = value; 
    }
private String errordesc = ""; 
public String errordesc() {
    return errordesc;
}
public void setValue(String errordesc) {
    this.errordesc = errordesc;
}

};

코드에서 다음과 같이 호출하십시오.

fileResponse.setErrorCode(ErrorCodes.NO_FILE.errordesc());

0

PropertyResourceBundle을 사용하여 엔터프라이즈 응용 프로그램에서 오류 코드를 정의하여 로캘 오류 코드 리소스를 관리합니다. 오류 코드의 수가 많고 구조화 된 경우 코드를 작성하는 대신 오류 코드를 처리하는 가장 좋은 방법입니다 (오류 코드가 적을 수 있음).

PropertyResourceBundle에 대한 자세한 내용은 Java 문서를 참조하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.