enum 은 서로 연관된 상수들의 집합으로 반복적으로 사용되는 코드 값을 표현할 때 유용합니다.
실무에서는 개발 편의성을 위해 DB에 있는 업무 코드들을 표현하기도 합니다. (등급 코드, 사용 여부 등)
하지만 프로젝트가 나눠지면 매번 복사해줘야 되고, 변경점이 생기면 반영이 필요하기에 썩 좋은 아이디어는 아닙니다.
때문에 쉬운 방법으로는 코드 테이블을 만들거나, 최신의 개발에서는 코드 정보를 MSA로 나누기도 합니다.
그럼에도 불구하고 업무 성격상 변경이 매우 적은 경우에는 개발 편의성과 성능 향상을 위해 사용할 수 있기에 실무에서 해본 리팩토링 내용을 기록해봅니다.
기존 코드의 문제
@Getter
@AllArgsConstructor
public enum RowStatusCd {
U("USE"), // 사용
N("UNUSE"), // 미사용
D("DELETE"); // 삭제
private String status;
public static String USE = U.name();
public static String UNUSE = N.name();
public static String DEL = D.name();
}
위 코드는 사용 여부와 soft delete를 표현하는 실무 코드의 일부를 각색하여 리팩토링 했습니다.
- U, N, D 은 DB 값이며, 그 의미를 enum으로 접근해야지 알 수 있습니다.
- 향후 DB 값이 바뀐다면 enum 명 자체가 변경되어야 하므로 변경 여파가 큽니다.
- 1번의 문제를 보완하기 위해 static 변수를 사용했습니다.
- 하지만 이 것이 DB 값과 직결된다는 것을 코드에서는 알 수 없습니다.
DB 값을 dataValue 로 명시한 코드
@Getter
@AllArgsConstructor
public enum RowStatusCode {
USE("U"),
UNUSE("N"),
DELETE("D"),
;
private final String dataValue;
}
- DB에서는 RowStatusCD로 축약되어 있었지만 애플리케이션으로 가져오면서는 축약하지 않고 RowStatusCode를 사용했습니다.
- enum의 네이밍을 명시적으로 바꿈으로써 주석을 달 필요가 없어지고 사용하는 곳에서 의도를 바로 알 수 있게 하였습니다.
- dataValue를 사용함으로써 static 변수를 사용하지 않아도 됩니다.
enum을 사용하는 곳에서 코드의 길이는 큰 차이가 없지만, DB 값이라는 명시성을 보여줄 수 있습니다.
// AS-IS
SYS_USER.ROW_STS_CD.in(RowStatusCode.USE, RowStatusCode.UNUSE),
// TO-BE
SYS_USER.ROW_STS_CD.in(USE.getDataValue(), UNUSE.getDataValue()),
좀 더 복잡한 실무 예시
@Getter
@AllArgsConstructor
public enum CreditCard {
KB("02", "국민", "3455"),
HANA("11", "하나", "3600"),
NH("08", "농협", "3700"),
;
private final String dataValue; // DB 에 저장된 값
private final String name; // 카드명
private final String bin; // 카드빈 (카드사별로 할당되는 카드의 시작 값)
public static CreditCard findByValue(final String value) {
return Arrays.stream(CreditCard.values())
.filter(e -> e.dataValue.equals(value))
.findFirst()
.orElseThrow(() -> new IllegalStateException("유효하지 않은 카드 정보 입니다."));
}
public static CreditCard findByCardNo(final String cardNo) {
final String cardBin = cardNo.substring(0, 4);
return Arrays.stream(CreditCard.values())
.filter(e -> e.dataValue.equals(cardBin))
.findFirst()
.orElseThrow(() -> new IllegalStateException("유효하지 않은 카드 정보 입니다."));
}
}
샘플 코드보다 좀 더 복잡한 실무에서의 제가 생각하는 best practice를 준비해봤습니다.
- 각 enum 이름은 쉽게 이해할 수 있는 업무 내용을 사용합니다.
- 각 enum에 매칭 되는 DB 값은 dataValue로 네이밍 합니다. (value로 직역하기에는 모호함이 있었습니다.)
- 필요에 따라 Arrays.stream 대신 EnumMap을 구성합니다.
- 실제 코드에서는 주석 내용을 팀 컨벤션으로 공유한 후 모두 삭제합니다.
리팩토링 전의 코드와 같은 형태를 취하려면, enum 이름에 dataValue가 있어야 합니다.
문법적으로 불가능하거니와 enum 이름이 '02' 면 업무 로직을 파악하는데 직관적이지 못합니다.
저는 클린 코드와 컨벤션을 준수하지만 기존 코드와의 일관성을 위해서는 일부 트레이드오프 해야 되는 부분도 있다고 생각합니다.
그렇기에 위의 코드를 실무에 적용하는 것은 조금 더 고민해보겠지만, 일반적인 enum과 DB값으로서의 enum을 구분해서 사용해야 된다는 것에 대한 고민을 이 글을 통해 말하고 싶습니다.
'Series > 내가 해본' 카테고리의 다른 글
ErrorMessage를 관리하는 방법 (1) | 2022.06.15 |
---|---|
현재 환경에 맞는 값을 가져오기 with enum (0) | 2022.05.18 |
Elastic Beanstalk 구성 삽질기 - 글쑤시개 (0) | 2021.08.14 |
Slack Bot 으로 채널에 글쓰기 (0) | 2021.08.04 |
DynamicUpdate 활용기 (0) | 2021.07.27 |