top of page

C#의 도메인 레이어 (Domain Layer)에서 enum 타입의 사용을 지양하는 이유를 알아보자

애플리케이션을 개발하다보면 때때로 비즈니스 로직 (Business Logic)이나 도메인 레이어 (Domain Layer)에 상수(constants)가 보이는 경우가 있습니다. 도메인 레이어에서는 열거형 (enumeration types)이나 enums를 사용하는 것을 지양하고 대신에 레코드 타입 (record types) 같은 다른 방법을 사용하는 것을 권장합니다.


왜 그럴까요? 이 포스팅에서 enum을 지양해야하는 이유와 record 타입의 이점에 관하여 알아보겠습니다.


콘솔 애플리케이션 만들기

우선 비주얼 스튜디오 (Visual Studio)에서 .NET Core 콘솔 애플리케이션을 만듭니다. 다음의 예제는 Visual Studio 2022의 버전에서 .NET Core 콘솔을 만드는 방법입니다:


  1. Visual Studio를 오픈

  2. Create new project를 클릭

  3. Create new project 템플릿 윈도우에서 Console App (.NET Core)를 찾아 클릭

  4. Next 클릭

  5. Configure your new project 윈도우에서 프로젝트와 폴더명 입력

  6. Next 클릭

  7. Additional information 윈도우에서 .NET 8.0 (Long Term Support) 프레임워크 버전을 셀렉트

  8. Create 클릭


이 예제에서는 .NET 8 콘솔 애플리케이션을 이용 하겠습니다.


enums의 문제점은?

열거형 (enumeration types)은 상수값들을 정돈하고 관리하기가 편하지만, 이 타입은 도메인 모델 (domain model)과 코드와 강한 결합 (tight coupling)을 도입하게 되어 도메인 모델의 확장에 문제를 줍니다.


아래 예제에 Roles라는 enum 타입이 있습니다.


다른 방법으로는 enum에 열거된 값에 아래와 같이 정수값을 지정할 수도 있습니다.


문제 1: 캡슐화 (encapsulation)

코드에서 비교하는 role이 administrator인지 알아보려면 아래의 예제 코드와 같이 IsAdmin이라는 확장 메서드(extension method)를 이용하여 Administrator 또는 SuperAdmin인지 비교를 하게 됩니다.


이런 방법은 도메인 레이어상에서 enum을 이용하는 첫번째 문제를 보여줍니다. 확장 메서드를 이용하여 코드를 진행하는 방법은 가능하지만 객체 지향 패턴 중 캡슐화 원칙 (encapsulation principle)을 무시하는 패턴이 됩니다. 왜냐하면 모델을 쿼리하는 로직과 생성된 모델이 따로 노는 현상이죠. 다른 말로 표현하면 모델을 체크하는 로직은 같은 클래스에 존재하지 않습니다. 이 코드는 안티패턴 (anti-pattern)이고 이런 타입의 모델은 빈약한 모델 (anemic model)이라고 불리웁니다.


캡슐화 원칙 (encapsulation principle)이란? https://ko.wikipedia.org/wiki/캡슐화

빈약한 도메인 모델 (anemic domain model)이란? https://ko.wikipedia.org/wiki/빈약한_도메인_모델

문제 2: 스파게티 코드

enum의 또다른 문제는 값을 가지고 오기위해서 cast를 자주 사용해야하는 문제입니다. 아래 코드 블럭의 예제를 보겠습니다.


명시적 캐스트 (explicit casts)는 좋은 방법이 아닙니다. 이런 캐스트는 코드의 퍼포먼스를 느리게 하고 호환되지 않는 타입을 사용했거나 타입들이 제대로 정의가 되지 않았다는 것을 뜻합니다. 이런 코드들이 앱 코드의 여기저기에 존재한다면 어지럽게 흩어져있는 코드가되어 이해하기 힘들어 관리가 어려워 집니다. 스파게티같이 어지럽게 널려져 있다는 뜻으로 스파게티 코드라고 불리웁니다.


문제 3: 이름의 제약

C#에서 enum은 값에 스페이스가 존재할 수 없습니다. 그래서 아래의 예제는 틀린 코드입니다.


이럴때는 속성 (attributes)을 이용하여 아래와 같이 처리를 할 수 있습니다.


하지만 앱이 다른 여러 로케일 (locales)을 서포트해야 한다면 문제가 생기게 됩니다.


enums 대신에 레코드 타입(record types)을 이용하자

enums 대신 권장되는 방법은 record 입니다. 아래와 같이 레코드 타입을 이용하여 불변 타입 (immutable type)을 생성합니다


불변 객체 (immutable object)는 인스턴스화 하면 변하지 못하는 객체입니다. 그러므로 본질적으로 스레드 안전성 (thread-safety)를 가지고 있고 경쟁 상태 (race conditions)에서 안전합니다. 불변 객체는 그리고 코드를 이해하기 쉽게 만들어서 코드 관리가 쉽습니다.


경쟁 상태 (race conditions)란? https://ko.wikipedia.org/wiki/경쟁_상태

레코드 타입의 아주 큰 이점은 어떠한 확장 메서드라도 모델에 존속하므로 캡슐화를 보존합니다. enum의 내부에는 메서드를 넣을 수 없죠.


그리고 더 나아가면 아래와 같이 레코드 타입은 원하는 방식으로 이름을 지정하는 것도 문제가 없습니다.


Roles를 record 타입으로 지정하였으면 아래와 같이 기존의 enumeration을 사용하듯이 엑세스 할 수 있습니다.


record 객체에 ToString() 오버라이드 메서드를 지정하였으므로 ToString() 메서드를 부르면 콘솔창에 이름이 출력됩니다.


또한 다른 방법으로는 클래스 (class)를 사용하여도 되지만 클래스 보다 가벼운 레코드 타입이 코드의 성능에 더 좋습니다.


참고:


Comments


pngegg (11)_result.webp

<Raank:랑크 /> 구독 하기 : Subscribe

감사합니다! : Thanks for submitting!

bottom of page