프로젝트를 진행하면 할 수록 협업에서 Exception처리가 많이 중요하다는 것을 느꼈다. (특히, 프론트엔드와의 협업)
프로그램은 클라이언트의 요구사항에 맞게 실행되는 것도 중요하지만, 대부분의 사용자는 작동보다는 오류에 대해서 민감하게 반응한다. 따라서, 프로그램은 오류처리에 대한 것이 확실해야한다.
내가 Exception을 처리하는 방식을 깃허브를 통해서 여러 사례를 찾아보고 다음과 같이 처리 하였다.
첫번째, 런타임을 상속하는 추상클래스를 만들었다.
public abstract class ApplicationException extends RuntimeException{
private final String errorCode;
private final HttpStatus httpStatus;
protected ApplicationException(String errorCode, HttpStatus httpStatus, String message){
super(message);
this.errorCode = errorCode;
this.httpStatus = httpStatus;
}
public String getErrorCode() {
return errorCode;
}
public HttpStatus getHttpStatus() {
return httpStatus;
}
}
에러코드를 통해서 프론트엔드는 해당 코드에 대해서 Exception처리할 수 있도록 하였다.
두번째, 각 도메인은 다음과 같이 상속하여서 ApplicationException을 상속받았다.
public class UserException extends ApplicationException {
protected UserException(String errorCode, HttpStatus httpStatus, String message) {
super(errorCode, httpStatus, message);
}
}
두번째와 같이 구성한 이유는 실질적으로 throw new를 하는 클래스에 대한 뼈대 역할을 한다.
세번째, enum 클래스를 통해서 code, Httpstatus, message를 구성해서 값을 저장한다.
@Getter
@RequiredArgsConstructor
public enum UserExceptionList {
DUPLICATION_ID("U0001", CONFLICT,"이미 존재하는 아이디입니다."),
DUPLICATION_NICKNAME("U0002", CONFLICT, "이미 존재하는 닉네임 입니다."),
VALID_FORM_ID("U0003",CONFLICT,"로그인 Id는 5~12글자의 영소문자, 숫자만 가능합니다."),
VALID_FORM_NICKNAME("U0004",CONFLICT,"닉네임은 2~5글자의 영소문자, 숫자, 한글만 가능합니다.");
private final String CODE;
private final HttpStatus HTTPSTATUS;
private final String MESSAGE;
}
네번째, 실질적인 throw new 전용 클래스를 구성하였다.
public class UserIdDuplicationException extends UserException{
public UserIdDuplicationException() {
super(UserExceptionList.DUPLICATION_ID.getCODE(),
UserExceptionList.DUPLICATION_ID.getHTTPSTATUS(),
UserExceptionList.DUPLICATION_ID.getMESSAGE());
}
}
다음과 같이 구성한 이유는
if (userRepository.findByNickName(nickName).isEmpty()) {
if (Pattern.matches("^[a-z0-9가-힣]{2,5}$", nickName)) {
return null;
}else {
throw new UserNickNameValidException();
}
} else {
throw new UserNickNameDuplicationException();
}
위와 같이 구성한다면, Service로직에서는 코드 가독성을 높일 수 있다. 그리고 throw new 하는 클래스의 사용한 곳을 다음과 같이 확인 할 수 있어 추적에 용이하다.
Service로직으로 바로 Enum클래스를 접근한다면, 네번째 클래스를 생성하지 않고 Exception 처리가 가능하나 클래스 재사용에 대해서 불가능하기에 코드 가독성이 낮아진다.
개선 방향성: 네번째 클래스를 생성을 최소화 및 하지 않는 방향성으로 할 수는 없을까??
throw new UserException(UserExceptionList.VALID_FORM_ID.getCODE(),
UserExceptionList.VALID_FORM_ID.getHTTPSTATUS(),
UserExceptionList.VALID_FORM_ID.getMESSAGE());
다음과 같이 UserException을 다음과 같이 입력한다면 가능은 하나, Service로직에 대한 코드 가독성이 낮아지고 UserException의 생성자가 public으로 열린다.
위와 같은 방법이 완벽한 방법이 아니기에 다음 개인 프로젝트에서는 위의 개선 사항을 보완하는 구성을 생각하고 찾아보아야겠다.