반응형
개발하다 보면 열거형(Enum)을 사용해 코드 목록을 관리하는 경우가 많습니다. 하지만 초기 구현에서는 종종 비슷한 로직이 반복되는 코드를 볼 수 있죠. 다음과 같은 코드를 예로 들어보겠습니다.
다음의 공통코드가 있습니다.
@Getter
@AllArgsConstructor
public enum CounselField {
EMPLOYMENT("취업"),
COURSE("진로"),
PERSON("인간관계"),
EXAM("시험"),
;
private final String label;
}
@Getter
@AllArgsConstructor
public enum CounselStyle {
ACCURATE("정확해요"),
FIT("잘맞아요"),
;
private final String label;
}
그리고 코드를 조회할때 찾을 수 있게 다음과 같이 묶습니다.
@Service
@RequiredArgsConstructor
public class CodeService {
public ResponseList<ListCode> findByCode(String codeType) {
CodeType codes = CodeType.findByName(codeType);
List<ListCode> listCodes = switch (codes) {
case COUNSEL_FIELD -> Arrays.stream(CounselField.values())
.map(o -> new ListCode(o.name(), o.getLabel()))
.toList();
case COUNSEL_STYLE -> Arrays.stream(CounselField.values())
.map(o -> new ListCode(o.name(), o.getLabel()))
.toList();
default -> throw new InvalidDataGodsTownException("등록되지 않은 코드입니다. => " + codeType);
};
return ResponseList.noPage(listCodes);
}
}
여기서 COUNSEL_FIELD와 COUNSEL_STYLE 케이스의 로직이 거의 동일하다는 점을 볼 수 있습니다. 이런 중복 코드는 유지보수성을 떨어뜨리고 새로운 Enum 타입을 추가할 때마다 코드를 수정해야 하는 문제가 있습니다.
리팩토링 접근 방법
이 문제를 해결하기 위해 다음과 같은 리팩토링 전략을 사용할 수 있습니다:
- 리플렉션을 활용한 동적 로직 처리
- CodeType Enum에 클래스 정보 추가
- 공통 메서드 추출
리팩토링된 코드
@Service
@RequiredArgsConstructor
public class CodeService {
public ResponseList<ListCode> findByCode(String codeType) {
CodeType codes = CodeType.findByName(codeType);
List<ListCode> listCodes = convertToListCodes(codes);
return ResponseList.noPage(listCodes);
}
private List<ListCode> convertToListCodes(CodeType codeType) {
Class<?> enumClass = codeType.getClazz();
if (!enumClass.isEnum()) {
throw new InvalidDataGodsTownException("등록되지 않은 코드입니다. => " + codeType.name());
}
Object[] enumValues = enumClass.getEnumConstants();
return Arrays.stream(enumValues)
.map(value -> {
String name = ((Enum<?>) value).name();
String label = getLabelFromEnum(value);
return new ListCode(name, label);
})
.toList();
}
private String getLabelFromEnum(Object enumValue) {
try {
Method getLabelMethod = enumValue.getClass().getMethod("getLabel");
return (String) getLabelMethod.invoke(enumValue);
} catch (Exception e) {
throw new InvalidDataGodsTownException("label을 가져올 수 없습니다: " + enumValue);
}
}
}
주요 개선 사항
- 동적 처리: CodeType의 clazz 필드를 활용해 동적으로 Enum 인스턴스를 처리합니다.
- 중복 제거: 기존 switch 문에서 반복되던 로직을 convertToListCodes 메서드로 통합했습니다.
- 확장성: 새로운 CodeType을 추가할 때 서비스 코드 수정 없이 CodeType enum만 업데이트하면 됩니다.
주의사항 및 고려사항
- 리플렉션 성능: 리플렉션은 약간의 성능 오버헤드가 있으므로, 자주 호출되는 경우 캐싱을 고려할 수 있습니다.
- 타입 안전성: 모든 Enum이 getLabel() 메서드를 가지고 있다고 가정합니다.
그래서 getLabel 이 없으면 에러가 날 수 있습니다.
선택적 개선: 인터페이스 도입
더 명시적인 타입 안전성을 위해 다음과 같은 인터페이스를 도입할 수 있습니다:
public interface LabeledEnum {
String getLabel();
}
CounselField와 CounselStyle이 이 인터페이스를 구현하면, 리플렉션 대신 직접 메서드 호출이 가능해집니다.
결론
이러한 리팩토링을 통해 코드의 가독성을 높이고, 새로운 Enum 타입 추가 시 유연성을 확보할 수 있습니다. 반복되는 로직을 공통화하고 동적 처리를 통해 더 유지보수하기 쉬운 코드를 작성할 수 있습니다.
반응형
'공부 > 프로그래밍' 카테고리의 다른 글
React에서 자식 컴포넌트 제어하기(useRef, forwardRef) (0) | 2025.03.18 |
---|---|
flutter 무한스크롤: riverpod 상태관리 삽질(스크롤 위치 초기화 및 리빌드 문제) (0) | 2025.03.13 |
Flutter에서 VerticalDivider 올바르게 사용하기 (0) | 2025.03.04 |
TypeScript와 ES6로 Enum처럼 코드 집합 관리하기: 공통 메서드와 타입 안전성 (0) | 2025.03.04 |
tiptap 에디터 focus border 없애기, 영역 클릭시 focus 하기 (0) | 2025.03.03 |
댓글