View

300x250

이번에 Enum 간의 타입 변환을 하는 새로운 방식을 찾아서, 기록으로 남겨두려고 한다. 생각도 못했던 방식… 오호…

대충 상황은 이런 느낌이였다.

레이어 분리를 위해 동일한 상태 값에 대해서 도메인 레이어에서는 Entity를 위한 enum 타입을 사용해주고 있었고, 데이터 레이어에서는 Model을 위한 enum 타입을 사용해주고 있었다.

enum MyDataTypeForEntity { A, B, C, D, none }

enum MyDataTypeForModel { A, B, C, D, none }

Entity와 Model에서는 이 두 타입을 각각 사용해 정의해주고 있었고, 나머지는 대부분 원시값으로 속성을 사용해주고 있었다.

class MyEntity {
  final String name;
  final MyDataTypeForEntity dataType;
  
  MyEntity(this.name, this.dataType);
}

class MyModel {
  final String name;
  final MyDataTypeForModel dataType;
  
  MyModel(this.name, this.dataType);
}

그런데, 계층을 넘어오면서 Entity와 Model 간에 상호변환이 필요한 시점이 있었는데 여기에서 두 타입간의 변환에서 막혀버렸다.

MyEntity fromModelToEntity(MyModel model) {
  final name = model.name;
  final type = model.dataType;
  
  return MyEntity(name, type);
  << 에러 발생
  << Entity 가 가진 type은 MyDataTypeForEntity
  << Model이 가진 type은 MyDataTypeForModel
  << 두 타입이 변환이 불가능한 별도의 enum 이라 implicit 형변환이 불가능함
}

처음에는 ValueMapper를 따로 만들어서 Model에 내부 메서드나 getter로 넣어버릴까? 라고 생각을 했는데, Model 내부에 Entity 변환을 위한 메서드가 들어가는게 그렇게 썩 좋은 코드로 생각이 되지는 않았다.

enum MyDataTypeForModel { 
	A, B, C, D, none;
	
	MyDataTypeForEntity toEntityEnum() {
	  // 이런 Enum 내부 메서드를 넣어볼까?
	}
}

그래서 모델보다는 변환 메서드인 fromModelToEntity에 타입을 변환하는 로직도 들어가는게 좋겠다는 생각이 들었다. 그러고나서 그 다음에 고민했던 부분은 타입을 변환하는 방식을 어떻게 가져갈 지 였다.

단순히 Switch 문으로 처리해버리기에는 타입이 늘어날 때 마다 여기를 수정해줘야 한다는 단점이 있어, 유지보수를 하기 번거롭지 않을까 라는 생각이 들었다.

MyEntity fromModelToEntity(MyModel model) {
  final name = model.name;
  final type = switch (model.dataType) {
    MyDataTypeForModel.A => MyDataTypeForEntity.A,
    MyDataTypeForModel.B => MyDataTypeForEntity.B,
    MyDataTypeForModel.C => MyDataTypeForEntity.C,
    MyDataTypeForModel.D => MyDataTypeForEntity.D,
    MyDataTypeForModel.none => MyDataTypeForEntity.none,
    // E가 추가되면 여기에 작성해줘야함
    // 문법 상 default => 00 에 대해 작성해주지 못해서, 
    // Model을 수정하면 여기에서 에러를 뱉기에 코드 수정이 강제된다.
  };
  
  return MyEntity(name, type);
}

그치만 다른 방법은 생각이 안나는걸? 고민을 조금 하다가 switch를 사용하는 방법을 들고 옆자리 사수분에게 어떻게 생각하는지를 여쭤봤다. 그리고 나온 답변은 firstWhere를 사용해본 예시가 프로젝트에 있을거니깐, 코드 한 번 찾아보라고 하셨다. 사실 처음 들었을 때는 퍼스트웨어라는 패키지가 따로 있는 줄 알고 ‘띠용?’ 이라 생각하고 있었다. ㅋㅋㅋ

어차피 이름이 동일한 값끼리 매핑을 할 것이니깐, 그 이름이 일치하는 열거 값으로 변환을 해주면 되기에 firstWhere를 사용하면 좀 더 유도리 있는 코드로 작성할 수 있다.

MyEntity fromModelToEntity(MyModel model) {
  final name = model.name;
  final type = MyDataTypeForEntity.values.firstWhere(
    (type) => type.name == model.dataType.name,
    orElse: () => MyDataTypeForEntity.none,
  );

  return MyEntity(name, type);
}

요렇게 MyDataTypeForEntity의 값들 중에서 model이 가지고 있는 dataType의 name과 동일한 name을 가진 타입으로 반환하는 놀라운 방법을 사용해주고 있었다. 더더욱 놀라운 것은 MyDataTypeForEntity 이랑 MyDataTypeForModel 에서 열거 값을 하나씩 확장해서 E 가 추가된다고 하더라도 변환 코드 상에서는 수정이 필요없다. 😮😮 더더더더욱 놀라운 점은 두 enum 중에서 한쪽에만 값이 추가되더라도 알아서 orElse 쪽에서 default 처럼 작동해서 none 같은 값으로 보내버릴 수 있다는 점이다. 😮😮😮😮

만약에 의존성 분리 등의 이슈로 두 쌍둥이 Enum이 있고 열거 값이 거의 동일하면서, 서로 간에 형변환이 필요한 시점이라면 firstWhere를 통한 열거 값 기반의 타입 캐스팅 방식을 적용해볼 수 있을 것 같다!


샤라웃

회사코드

320x100
Share Link
reply
반응형
«   2024/12   »
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 31