본문 바로가기
공부/프로그래밍

[Java] enum 으로 되어있는 공통코드를 목록 조회로 공통화 하기

by demonic_ 2025. 3. 5.
반응형

개발하다 보면 열거형(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 타입을 추가할 때마다 코드를 수정해야 하는 문제가 있습니다.

 

 

리팩토링 접근 방법

이 문제를 해결하기 위해 다음과 같은 리팩토링 전략을 사용할 수 있습니다:

  1. 리플렉션을 활용한 동적 로직 처리
  2. CodeType Enum에 클래스 정보 추가
  3. 공통 메서드 추출

 

리팩토링된 코드

@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);
        }
    }
}

 

 

 

주요 개선 사항

  1. 동적 처리: CodeType의 clazz 필드를 활용해 동적으로 Enum 인스턴스를 처리합니다.
  2. 중복 제거: 기존 switch 문에서 반복되던 로직을 convertToListCodes 메서드로 통합했습니다.
  3. 확장성: 새로운 CodeType을 추가할 때 서비스 코드 수정 없이 CodeType enum만 업데이트하면 됩니다.

 

 

주의사항 및 고려사항

  • 리플렉션 성능: 리플렉션은 약간의 성능 오버헤드가 있으므로, 자주 호출되는 경우 캐싱을 고려할 수 있습니다.
  • 타입 안전성: 모든 Enum이 getLabel() 메서드를 가지고 있다고 가정합니다.

그래서 getLabel 이 없으면 에러가 날 수 있습니다.

 

 

선택적 개선: 인터페이스 도입

더 명시적인 타입 안전성을 위해 다음과 같은 인터페이스를 도입할 수 있습니다:

public interface LabeledEnum {
    String getLabel();
}

 

CounselField와 CounselStyle이 이 인터페이스를 구현하면, 리플렉션 대신 직접 메서드 호출이 가능해집니다.

 

 

결론

이러한 리팩토링을 통해 코드의 가독성을 높이고, 새로운 Enum 타입 추가 시 유연성을 확보할 수 있습니다. 반복되는 로직을 공통화하고 동적 처리를 통해 더 유지보수하기 쉬운 코드를 작성할 수 있습니다.

 

반응형