티스토리 뷰

Java & Kotlin

[Java] Enum 사용법 - 고급(Enum 응용하기)

망나니개발자 2020. 6. 2. 17:17
반응형

클린코드 책을 읽다보면 Enum 활용의 중요성에 대해 얘기하는 부분이 있습니다. 이번 내용으로는 Enum에 대해 알아보고 기존의 코드를 개선시켜보도록 하겠습니다. 아래의 내용들은 배달의 민족 기술 포스팅을 참고하여 작성하였습니다!

 

 

1. 기존의 코드 관리 방법 


[ 기존의 코드 관리 방법 ]

일반적으로 프로그래밍을 할 때, Enum과 같이 열거형이 필요한 타입들의 경우 서버에서 데이터를 제공한다. 예를 들어 어떤 글의 진행상태를 나타내는 변수가 있다고 하자. 과거의 시스템과 같은 경우는 아래의 예제와 같이 상수와 관련된 코드들을 별도의 테이블로 만들어서 관리하고 있었다.

 

 

@Entity
@Table("codeVO")
@Getter
@NoArgsConstructor
public class CodeVO extends CommonVO implements Serializable {

    private String codeSn;					// 코드일련번호
    @Setter
    private String lvl1;					// 레벨1코드
    @Setter
    private String lvl2;					// 레벨2코드
    private String codeName;				// 코드한글명
    private String codeDesc;				// 코드설명
    @Setter
    private String selType;					// 조회유형

    @Builder
    public CodeVO(String lvl1, String selType){
        this.lvl1 = lvl1;
        this.selType = selType;
    }

}

 

코드 관리 테이블은 위와 같이 구성이 되어있고, 게시물 테이블의 status도 이에 종속적인 상황이다.

 

public class BoardVO extends CodeVO implements Serializable {
    private String title;					// 제목
    private String contents;				// 내용
    private String status;					// 상태
}

BoardVO 테이블의 데이터는 아래와 같이 조회된다.

 

 

상태가 진행중인 게시물을 조회하기 위해서는 아래와 같이 코드를 작성해주어야 한다. (완전한 코드가 아니고, 대략적으로 적은 것이기 때문에 이해가 안가면 안가시는대로 넘어가셔도 됩니다!)

@RestController
@RequestMapping("/board")
@Log4j2
public class BoardController {

    @Resource(name = "boardService")
    private BoardService boardService;

    @GetMapping(value = "/list")
    public ResponseEntity<List<BoardVO>> list() {
    	BoardVO boardVO = new BoardVO();
        boardVO.setLvl1("001");
        boardVO.setLvl2("002");
        List<BoardVO> boardList = boardService.findAll(boardVO);
        return new ResponseEntity.ok(boardList);
    }
}

 

이러한 경우 게시물을 조회할 때 게시물의 Code Lvl1이 어떤 값인지 계속 확인해주어야 하는 문제가 발생하며 만약 Code 테이블이 잘못되었을 경우 확인이 어렵다. 그래서 이러한 문제를 Enum을 활용하여 해결해보고자 한다.

 

 

2. Enum을 활용한 코드 리팩토링 


[ Enum을 활용한 코드 리팩토링 ]

일반적으로 프로그래밍을 할 때, Enum과 같이 열거형이 필요한 타입들의 경우 서버에서 데이터를 제공한다. 예를 들어 어떤 글의 진행상태를 나타내는 변수가 있다고 하자. 과거의 시스템과 같은 경우는 아래의 예제와 같이 상수와 관련된 코드들을 별도의 테이블로 만들어서 관리하고 있었다.

 

 

먼저 모든 Enum 데이터들이 공통으로 갖는 데이터로 Enum 이름(code), Enum 설명(title)이 있다고 가정하자. 그리고 이러한 내용을 담는 인터페이스를 아래와 같이 구성하자.

public interface EnumMapperType {
	// 해당 Enum의 이름을 조회하는 변수
    String getCode();
    
	// 해당 Enum의 설명을 조회하는 변수
    String getTitle();

}

 

 

Status Enum 클래스를 기준으로 설명하면 PROCEEDING과 COMPLETE이 code에 해당하고 getCode()를 통해 조회가능하며, "진행중"과 "진행완료"는 title에 해당한다. 기존의 Status에 EnumMapperType을 implements하여 재작성하면 아래와 같다.

@RequiredArgsConstructor
public enum Status implements EnumMapperType {

    PROCEEDING("진행중"),
    COMPLETE("진행완료");

    @Getter
    private final String title;

    @Override
    public String getCode() {
        return name();
    }

}

Enum의 변수들은 불변이기 때문에 final 키워드를 붙여줄 수 있고, Lombok의 RequiredArgsConstructor 어노테이션과 같이 활용해주면 용이하다.

 

 

그리고 Status 말고도 Category 등 다른 Enum 항목들이 생길 수 있기 때문에 다양한 Enum 종류들을 관리하기 위한 EnumMapperFactory를 생성한다.

@Getter
@AllArgsConstructor
public class EnumMapperFactory {
	// 다양한 종류의 Enum을 생성 및 관리하는 factory
    private Map<String, List<EnumMapperValue>> factory;

    // 새로운 Enum 종류를 추가하는 함수
    public void put(String key, Class<? extends EnumMapperType> e) {
        factory.put(key, toEnumValues(e));
    }

    // 특정 Enum의 항목들을 조회하는 함수
    public List<EnumMapperValue> get(String key) {
        return factory.get(key);
    }

    // Enum의 내용들을 List로 바꾸어주는 함수
    private List<EnumMapperValue> toEnumValues(Class<? extends EnumMapperType> e) {
        return Arrays.stream(e.getEnumConstants()).map(EnumMapperValue::new)
                .collect(Collectors.toList());
    }

}

toEnumValues 함수는 Enum 타입을 Enum의 Code와 Title을 변수로 갖는 EnumMapperValue로 변환시켜주는 메소드이다. 위의 Status는 {PROCEEDING, "진행중"}, {COMPLETE, "진행완료"}를 갖는데, Enum의 항목들을 순차적으로 접근하여 code와 title을 변수로 갖는 EnumMapperType의 객체로 새로 생성하여 List로 모으고 있다.

 

 

 

EnumMapperType을 implements한 구현체에 대해 실제 값을 갖는 EnumMapperValue는 아래와 같다.

@Getter
public class EnumMapperValue {
    private String code;
    private String title;

    public EnumMapperValue(EnumMapperType enumMapperType) {
        code = enumMapperType.getCode();
        title = enumMapperType.getTitle();
    }

}

 

 

이렇게 생성한 Enum Status를 아래와 같이 Factory에 등록하여 활용할 수 있다.

@Configuration
public class EnumMapper {

    @Bean
    public EnumMapperFactory createEnumMapperFactory() {
        EnumMapperFactory enumMapperFactory = new EnumMapperFactory(new LinkedHashMap<>());
        enumMapperFactory.put("Status", Status.class);
        return enumMapperFactory;
    }

}

 

 

프론트에서 글의 상태를 선택하기 위해 Status를 필요로 한다면 아래와 같은 방식으로 제공할 수 있다.

@ReqruiedArgsConstructor
@RestController
public class EnumsController {

    private final EnumMapperFactory enumMapperFactory;

    @GetMapping("/status")
    public ResponseEntity status(){
        return ResponseEntity.ok(enumMapperFactory.get("Status"));
    }
}

 

또한 게시물의 BoardVO 클래스는 아래와 같이 변경되었다. JPA의 경우 해당 Enum을 저장할 수 있고, @Enumerated(EnumType.STRING) 어노테이션을 활용하여 Enum의 Code가 DB에 저장되도록 설정할 수 있다.

 

@Entity
@Table(name = "product")
@Getter
public class BoardVO extends CodeVO implements Serializable {
    private String title;					// 제목
    private String contents;				// 내용
   
    @Column(nullable = false, length = 10)
    @Enumerated(EnumType.STRING)
    private Status status;					// 상태
}

 

이러한 설정 방식으로 DB에 insert를 하면 아래와 같이 데이터가 저장되는 것을 확인할 수 있으며 이전의 방식에 비해 Query 속도도 빨라지고, 가동성도 높아졌다.

 

항상 진행중인 상태의 게시물만을 필터링하여 제공하길 원하는 경우 아래와 같이 처리할 수 있다.

@RequiredArgsConstructor
@Log4j2
@RestController
@RequestMapping(value = "/board")
public class BoardController {

    private final EnumMapperFactory enumMapperFactory;
    private final BoardService boardService;

    @GetMapping(value = "/list")
    public ResponseEntity<List<BoardVO>> list(@RequestParam(required = false, name = "status") Status status) {
        List<BoardVO> boardList = boardService.findAll();
        boardList = boardList.stream().filter(b -> b.getCategory().getTitle().equals(Status.PROCEEDING.getCode())).collect(Collectors.toList());
        return new ResponseEntity.ok(boardList);
    }

}

 

이렇게 Enum을 하나 활용하는 것 만으로도 엄청난 코드의 향상을 일으킬 수 있다. 앞으로 코드가 관리되어야 하는 테이블의 경우 Enum을 통해 관리하도록 하자!

 

==========================================================================

위의 2. Enum을 활용한 코드 리팩토링의 대부분은 우아한 형제들의 글에서부터 참고하여 작성한 것입니다!

 

 

 

관련 포스팅

  1. Enum 사용법 - 초급(Enum 입문하기) (1/2)

  2. Enum 사용법 - 고급(Enum 응용하기) (2/2)

 

 

 

참고 자료

 

반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG more
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함