순간을 성실히, 화려함보단 꾸준함을

어느 날 이넘(enum) 이 내게 찾아왔다! 본문

나의 개발 메모장

어느 날 이넘(enum) 이 내게 찾아왔다!

폭발토끼 2023. 5. 17. 21:27

안녕하세요
오늘은 글또 7번째 글을 작성해보려고 합니다.

 

주제는 '어느 날 이넘(enum) 이 내게 찾아왔다!'
즉, enum 활용하여 고객의 요구사항을 구현했던 경험을 작성해보려고 합니다.

배경

어떠한 신청서를 신청하게 되면 승인/반려 처리를 하는 프로세스가 존재합니다.
이때 '반려' 처리를 하게 되면 각 신청서에 대해 반려 사유를 신청자에게 알람톡이 가게끔 기능을 개발해달라는 요구사항이 들어왔습니다.


신청서는 학자금, 리조트, 의료비 등등 다양한 유형들의 신청서들이 존재하고 있습니다.

모든 신청서관련 클래스는 공통의 추상 클래스 를 상속받고 있는 상황이었습니다.
단순히 생각했을때는 상속받고 있는 신청서 클래스마다 반려처리시 알람톡이 발송되게끔 코드를 작성하면 될 듯 싶습니다. 그러나 신청서의 유형은 소수가 아닌 다수였고 가장 큰 문제는 앞으로도 신청서의 유형이 증가 할 여지가 있다는 것 이었습니다.

 

신청서가 늘어날때마다 동일한 코드를 추가해야 되고 이는 좋지 못한 유지보수가 되어버립니다.

그래서 추상 클래스 내에 해당 기능을 정의하도록 정하였습니다. 그러나 신청서 마다 반려문구가 달라야 하는 요구사항에 대해 이를 어떻게 하면 각 신청서 유형별로 문구를 설정할 수 있을지 고민이 되었습니다.

  1. if ~ else 문으로 분기처리하기
  2. DB table 을 생성하여 신청서 유형별로 문구 select 해오기
  3. enum 을 사용하여 DB 를 타지 않고 application layer 에서 해결하기

이렇게 3가지의 선택지가 존재했고 전 3번을 선택하였습니다.

if~else 문으로 신청서 유형을 분기처리한다면 이 역시 각 하위 클래스마다 기능을 개발하는 것과 큰 차이가 없었으며 단 하나의 컬럼(반려문구)을 select 하기위해 DB 와 connection 을 맺는 건 비용적인 측면에서 비효율적이라고 생각했습니다.

개발과정

enum 의 맴버변수로 필요한 필드는 2개였습니다.
(신청서 유형 코드, 반려문구)

public enum NoApplType {

    private String applCode;    
    private String noApplMessage;
}

저는 enum 생성자를 사용하여 정의할 것이므로 생성자도 추가해주고 상수 또한 정의해줍니다.

public enum NoApplType {

    M003("003","신청하신 학자금 신청이 반려되었습니다.\n");

    private String applCode;
    private String noApplMessage;

    NoApplType(String applCode, String noApplMessage) {
        this.applCode = applCode;
        this.noApplMessage = noApplMessage;
    }
}

필자는 신청서 '코드' 값만을 가지고 반려메세지를 추출하고 싶었습니다. 이를 구현하기 위해 static 생성자와 HashMap 을 사용했는데요. static 생성자에서 모든 상수값을 (코드,반려메세지) 로 (key,value) 형식으로 구현하였습니다.

public enum NoApplType {

    M003("003","신청하신 학자금 신청이 반려되었습니다.\n");

    private String applCode;
    private String noApplMessage;

    private static final Map<String,String> codeMap = new HashMap<>();

    static{
        Arrays.stream(values())
                .forEach(applType -> codeMap.put(applType.applCode,applType.noApplMessage));
    }

    public static NoApplType of(String code){
        return codeMap.get(code);
    }

    NoApplType(String applCode, String noApplMessage) {
        this.applCode = applCode;
        this.noApplMessage = noApplMessage;
    }
}

이렇게 말이죠. 그럼 사용자는 신청서 유형만 알고 있다면 반려처리 되었을때 신청서 코드만 넘기면 원하는 메세지를 얻게 될 수 있습니다.
enum 을 사용해서 고객이 원하는 요구사항을 개발해보았습니다.

추가

추가로 전 예외처리 구현할때 각 예외들을 enum 으로 정의하는 것을 선호합니다.

public enum ErrorType {

    A001("A001","토큰이 유효하지 않습니다.", TokenIsNotValidException.class),
    A002("A002","토큰이 만료되었습니다.", TokenExpiredException.class),
    A003("A003","ACCESS TOKEN 이 만료되었습니다.", AccessTokenRenewException.class),

    M001("M001","회원을 찾을 수 없습니다.", MemberNotFoundException.class),
    M002("M002","아이디 혹은 비밀번호가 올바르지 않습니다.",MemberIdOrPasswordErrorException.class),
    M003("M003","이미 등록된 아이디가 존재합니다.",ValidationMemberIdException.class),
    M004("M004","이미 등록된 이메일이 존재합니다.", ValidationMemberEmailException.class),
    ;

    private final String code;
    private final String message;
    private final Class< ? extends BaseException> classType;

    private static final Map<Class<? extends BaseException>,ErrorType> codeMap = new HashMap<>();

    static {
        Arrays.stream(values())
                .forEach(errorType -> codeMap.put(errorType.classType,errorType));
    }

    public static ErrorType of(Class<? extends BaseException> errorType){
        return codeMap.get(errorType);
    }

    ErrorType(String code, String message, Class<? extends BaseException> classType) {
        this.code = code;
        this.message = message;
        this.classType = classType;
    }
}

이런식으로 말이죠.
그리고 모든 예외를 상속해주는 BaseException 을 선언하여 enum 값을 받게끔 합니다.

public class BaseException extends RuntimeException{

    private final HttpStatus status;
    private final ErrorType errorType = ErrorType.of(this.getClass());

    public BaseException(HttpStatus status) {
        this.status = status;
    }
}

그리고 @ControllerAdvice 를 붙인 클래스에서 response 로 뱉어내는 클래스로 치환하여 클라이언트에게 응답해주면 되겠죠???

마무리

오늘은 enum 타입을 활용한 사례에 대해서 글을 작성해보았습니다.
이론적인 부분에서 이런게 있구나 라고 생각하고 실무에서는 어떨때 사용할 수 있을까? 라는 생각이 들던 차에 딱 enum 을 활용하기 좋은 고객의 요구사항이 들어왔고 무사히 잘 녹여낼 수 있었습니다.

 

앞으로도 학습한 내용을 바탕으로 실무에서 녹여내어 좋은 코드를 작성하는 노력을 해야겠네요!!

 

감사합니다