창천향로
4k
2017-04-09 01:08:18
11
5213

enum 활용사례 3가지


안녕하세요? 이번 시간엔 enum 활용사례를 3가지정도 소개하려고 합니다.
모든 코드는 Github에 있기 때문에 함께 보시면 더 이해하기 쉬우실 것 같습니다.
(공부한 내용을 정리하는 Github와 세미나+책 후기를 정리하는 Github, 이 모든 내용을 담고 있는 블로그가 있습니다. )

최근에 레거시 프로젝트를 개편하면서 enum을 적극 사용하였습니다.
혹시나 비슷한 고민이 있으신분들에게 참고가 될까 싶어 포스팅하게 되었습니다.
이런식으로 해결할 수도 있네? 정도로 봐주시면 될것 같습니다.
그럼 시작하겠습니다!

사례1 - code 관리용 테이블 대체하기

프로젝트를 진행하다보면 code 들을 관리하기 위한 테이블을 별도로 만드는 경우가 빈번합니다.

코드테이블

(출처 : 실천하는삶님의 블로그)

이 경우가 무조건 나쁜것은 아니지만, 무조건 옳다고 할수도 없다고 생각합니다.
제가 생각하기에 code 테이블로 code를 관리하는 것의 문제는 다음과 같습니다.

  • 관련된 테이블은 조회를 할때마다 항상 join으로 code용 테이블에 있는 데이터를 함께 가져와야 한다.
    • 해당 code 테이블의 값을 기준으로 비지니스 로직을 구현하고, 컬럼 값을 할당하는 여러 테이블이 존재할 경우 그 테이블들을 조회할때마다 항상 code 테이블도 같이 조회해야하는 경우가 발생합니다.
  • 컴파일 단계에서 체크할 수 있는 것이 없다.
    • 오타 혹은 해당 row에 잘못된 code 값이 있어도 체크를 할 수 없습니다.
  • 코드레벨에서 확인할 수 있는 것이 없다.
    • 바로 위에서 연결된 내용인데, 이 타입일 경우 이 코드와 저 코드가 필요하다는 내용을 코드만으론 확인할 수 없습니다.
    • code table 또한 여러개로 분리된 경우 작성된 쿼리를 통해 여러 테이블을 다 찾아서 확인해야합니다.
    • 히스토리관리가 안되어있을 경우 신규 인력이 개발에 투입되려면 많은 문서가 필요합니다.

예를 들어 한 그룹사의 계열사 관리를 해야한다고 가정하겠습니다.
계열사의 정보는 DB에 관리되겠지만, 계열사에 필요한 code 정보들은 enum으로 분리하여 관리하면 위에서 제시한 문제들을 해결할 수 있습니다.

example1

테스트 코드를 작성하게 되면,

example1 test

Entity 클래스에 선언한 @Enumerated(javax.persistence.EnumType.STRING)을 entity의 필드에 선언하시면, 해당 enum 타입의 name이 DB에 저장됩니다.
(여기서는 WOOWA_SISTERS, WOOWA_CHILDREN 등이 DB에 저장되며, DB에서 조회하면 Affiliate.Code.WOOWA_SISTERSAffiliate.Code.WOOWA_CHILDREN으로 값이 할당됩니다.)
이렇게 enum으로 풀게되면 Affiliate.Code을 여러 Entity 클래스에서 join select 없이 사용할 수 있으며, 어떤 계열사일때 어떤 code가 사용될지 바로 알 수가 있습니다.
단, 대전제는 enum으로 관리되는 데이터가 빈번하게 변경(추가/제거) 되지 않아야하는 것과 이 code enum을 다른 테이블에서도 빈번하게 사용해야 한다는 것입니다.
이럴 경우 enum을 고려해보심을 추천드립니다.

사례2 - 타입별 다른 연산식 처리하기

예를 들어 매출금을 계산하는 프로그램이 필요하다고 가정하겠습니다.
매출금의 경우 원금액, 공급가액, 부가세 이 3가지로 분류할 수 있는데, 한번의 결제가 발생하면 결제된 총 금액을 원금액으로, 원금액을 1.1로 나눈 금액을 공급가액으로, 공급가액의 10%를 부가세로 분류해야 합니다.

영수증

(영수증을 보시면 쉽게 확인할 수 있습니다.)

보통 이런 경우 가장 쉽게 진행할 수 있는 방법은 매출액 타입을 String 혹은 enum으로 분리하고 서비스 코드에서 타입에 따라 if 분기처리 하는 것입니다.

이런 경우가 제가 생각하기엔 전형적인 데이터와 로직이 분리된 사례라고 생각합니다.
매출타입별 연산식에 대한 책임은 누가 갖고있어야 할까요?
서비스 코드일까요?
각 매출타입이 갖고있어야 하지 않을까요?
A 타입은 a식으로 계산해야하고,
B 타입은 b식으로 계산해야한다라는건
A와 B가 책임져야하는 부분이 아닐까요?

이전에는 이 사례를 코드로 풀려면 익명클래스 혹은 인터페이스 구현체를 사용해야되서 지저분해질수 밖에 없었습니다.
하지만 Java8이 되면서 Function 인터페이스가 등장하게 되어 function을 값으로 사용할 수 있게 되었습니다.
(물론 내부적으로는 인터페이스의 구현체를 사용하나, 겉으로 드러나는 코드상으론 다른 함수형 언어처럼 function을 갖고 있는것처럼 보이는것입니다.)

example2

이 코드를 사용하게 되면

example2 test

이렇게 각 타입은 거래 금액(txAmount)에 대해 본인의 계산식을 실행만 시키면 됩니다.
어느 코드에서든 특정 금액에 대해 타입별 계산금이 어떻게 되는지는 이제 그 타입에 직접 물어보면 되는 것입니다.

꼭 이 상황에서 enum을 써야하느냐 보다는, 타입별로 다른 연산식을 적용해야 할 경우엔 이렇게 사용하면 좋다 정도로 보시면 될것 같습니다.

사례3 - 각 타입을 그룹화 하기

마지막 사례는 그룹화를 해야할때 입니다.
예를 들어 결제방식에 따라 다른 수수료를 부가해야하는 과정이 필요하다고 가정하겠습니다.
아래와 같이 결제방식에 따른 수수료 그룹이 있는 것입니다.


이 경우 각 결제방식이 어느 그룹에 포함되는지에 대한 분기로직이 필요합니다.
이 분기로직을 어디에 두는것이 좋을까요?
서비스 클래스에 두자니 서비스 클래스가 담당하기엔 책임이 모호합니다.
또한 그룹별 로직이나 추가 메소드가 필요한 경우와 그룹을 select box로 표시해야하는 경우 등까지 고려하면 클래스나 데이터베이스로 해결하기엔 코드가 너무 분산됩니다.
이때 enum을 활용해보겠습니다.

example3_2

(결제수단 enum)
위와 같이 결제수단들이 있고 이를 그룹화하는 enum을 생성하겠습니다.


보시는것처럼 PaymentGroupPaymentOption의 배열을 갖고 있습니다.
특정 PaymentOption이 있을때 이 값이 어느 그룹에 포함될지는 PaymentGroup에 직접 물어보면(findGroup) 됩니다.
각 그룹별로 추가 기능이 필요한 경우엔 PaymentGroup에 추가하면 됩니다.
또한 select box로 그룹 리스트를 출력해야하는 경우엔 PaymentGroup.values()을 사용하면 아주 쉽게 대응할 수 있습니다.
(이걸 만약 Group이란 인터페이스를 두고, 하위 클래스들의 구현체로 해결(추상화)하려고하면 select box에서 막히게 됩니다.
추가로 코드 작성량 또한 enum에 비교해 크게 많아지는 단점이 있습니다.)

자 그럼 테스트 코드를 작성해서 확인을 해보겠습니다.

example3 test

결제수단에 따라 PaymentGroup에게 물어보고 값을 가져오는 것을 명확하게 확인할 수 있습니다.
앞으로 결제수단의 그룹에 관련된 일PaymentGroup에게 맡기면 되겠죠?

후기

이번 포스팅은 enum에 대한 이야기를 했지만, 핵심은 객체간 책임을 확실히 분리하자로 보시면 될것 같습니다.
모든 데이터를 데이터베이스에 의존하게 되면 객체를 중심으로 코드 작성과 설계가 정말 힘든것 같습니다.
그 일에 대한 책임을 갖고 있는 객체가 상태(값)와 행위(로직)를 갖고 있는것이 좋다고 생각합니다.
코드레벨에서 많은 것을 검증과 확인이 가능해야 좀 더 유지보수하기가 쉽지 않을까 생각합니다.
enum으로 조금이나마 답답함을 해소할 수 있었던 것 같습니다.
긴 글 끝까지 읽어주셔서 감사합니다!

15
8
  • 댓글 11

  • vernum
    960
    2017-04-11 01:04:02

    Enum + function 도 괜찮은 구성이 됩니다.

    0
  • 으음 글쎄요.

    위에 1번 부터보면 이넘으로 관리하면 항목이나 상태 추가시마다 컴파일과 서버반영을 다시 해야하죠.. 그리고 개발작가 직접해야하고요.

    공통코드는 그래서 생긴겁니다. 사용자가 자기 필요한것을 관리화면에서 직접 추가제거 할수 있지요.

    join을 해야한다는 것도 sql에대한 이해부족에서 나오는 것입니다.


    저도 enum을 종종쓰지만 공통코드보다 이걸쓰자고는 못하겠네요. DB가 없거나 어쩔수 없는 환경에서는 쓰면 좋겠지요.


    3번은 공통코드 그룹코드가 아닌가 싶고요.


    2번은 인터페이스로 가는게 맞을것 같네요. 용도와 다른 사용이 아닌가 싶고요. 글로벌 환경에서 국가별로 부가세가 다르면 어찌하실 건가요?. 매출계산 클래스를 만들어야하는 것을 이넘타입에 때려 박는건 좀 생각해봐야 될 문제인것 같네요.


    포스팅은 이쁘게 잘 되어있는데 실제 그렇게들 쓰고있는 사례가 있는 내용인 것인지 아니면 생각해서 만든 사례인 것인지 궁금하네요...

    본의아니게 팁을 올리셨는데 태클걸어서 죄송합니다;

    0
  • 창천향로
    4k
    2017-04-12 07:29:12 작성 2017-04-12 08:25:55 수정됨

    @노는개발자

    앗 댓글 감사합니다!

    태클 절대 아니시구요 오히려 정성스럽게 댓글 작성해주셔서 제가 더 감사합니다!


    1번의 경우 말씀하신대로 추가제거가 빈번한 경우엔 데이터베이스를 쓰는게 맞으나, 거의 변경이 없는데도 코드 테이블로 관리하는건 너무 DB중심이 아닌가 하는 생각이였습니다.

    모든걸 DB를 통해야만 한다면 자바가 하는건 단지 쿼리 실행과 view layer와 통신하는게 전부가 아닐까 하는 생각이였습니다.

    2번의 경우 말씀하신대로 글로벌환경인 경우엔 저렇게 하지 않겠지만 서비스를 시작할때 글로벌 환경을 고려하는건 오버엔지니어링이 아닐까 하는 마음에 작성하였습니다

    실제로 국내에서 글로벌 환경을 고려하면서 개발해야하는 회사가 많을까 하는 생각이였습니다

    반대로 여쭤보고 싶은건 국내에서 사용하는 시스템임에도 항상 글로벌 환경을 고려하면서 개발하시는가요?


    3번의 경우도 1번과 연결되는데요,

    변경되는 일이 거의 없는 경우에도 로직의 중심을 DB에 두어야 했을때 발생하는 손해가 더 큰 경우라고 생각되어서

    코드레벨로 올렸습니다.

     

    친절하게 답변 써주셔서 감사합니다!

    주제도 코드도 좀 오해가 있을만했다고 생각이 들었습니다 ㅠ

    좀 더 적절한 주제로 올리겠습니다

    감사합니다!

    0
  • SteveH
    1k
    2017-04-12 14:14:16

    두분의 글과 댓글로 많은 견해를 지식을 배우고 갑니다.감사합니다.

    0
  • LichKing
    14k
    2017-04-12 14:31:19 작성 2017-04-12 14:33:42 수정됨

    노는개발자 님

    저희도 enum 활용해서 코드관리 많이 하고있습니다. 보통 코드를 사용하다보면 A코드일땐 이걸하고, B코드일땐 저걸하는 로직들이 존재하기때문에 여기저기 비슷한 유형의 분기문이 많아지게되는데요. enum을 이용해서 그런 분기문이나 로직들을 enum이 갖고있는 형태로 몰아넣어서 사용하고있습니다.

    DB에 common code 같은 형태로 관리할경우엔 보통 애플리케이션 내에서 코드를 문자열로 사용하게되어 타입안정성도 많이 떨어지는데 enum으로 관리하면 그런염려도없어 개발할때 매우 편리하구요.

    저희같은경우 내부적으로 common code로 코드를 관리하다가 창천향로님처럼 enum을 도입하자고 발표하시는 분이 계셔서 그럼 한번 해볼까 해서 enum을 적극적으로 사용하게됐는데, enum으로 관리하다보니 편해서 이제 common code는 다 지우자는 주장도 나오고있어요.

    다만 말씀하신대로 enum으로 관리할때 가장 큰 약점은 변화에 취약하다는 점인데 저희 회사에서는 그것보다 enum으로 개발할때 얻는 이익이 압도적으로 크다고보고있습니다.

    실제 그렇게 쓰고있는데가 있냐고 말씀하셔서 저희 얘기도 한말씀 드렸습니다

    1
  • Courage
    2k
    2017-04-12 14:33:04

    2,3 번의 경우 별도 의견은 없으나,

    1번의 경우는 저도 공통코드 대신 사용보다는 소스코드 내에서 상수로 사용되는 것들을 enum으로 정의하여 사용하곤 합니다.

    예전에 클래스를 만들어 상수만 정의했던 것들을 enum으로 변경 했다고 보시면 이해가 더 빠를듯 합니다.

    좋은 정보 제공 감사합니다.

    0
  • 서비스지향개발자
    7k
    2017-04-13 01:21:28

    LichKing 네~ㅎ 저도 이넘을 씁니다. 코드도 깔끔하고 기분이 좋아요..

    그런데 DB연결 없을때나 간단하게 짤때 관리코드가 많지 않을때 썼는데 음 메인으로 쓰는데도 있긴 하는군요..

    if문이 많으면 디자인패턴도 고려해보시면 더 재밌을것 같네요.


    Courage 저도 이넘 쓰는거 좋아합니다. 단지 공통코드는 조인도 해야하고 좋지 않으니 이넘을 쓰자는건 아닌것 같아서요.. 저랑 같은 의견이시네요. 

    다시한번 생각하게 되는군요. 

    0
  • ko10041004
    841
    2017-04-13 08:23:38

    유지보수상 공통코드 사용이 목적이라면 ehcache를 쓰는 방법이 더 나아보입니다.

    0
  • 전재형
    4k
    2017-04-14 21:25:42

    function 연계나, 로직 분기(그룹화?)를 위해 enum을 써본적이 없었는데. 좋은 것같아요 !

    저도 적극적으로 사용할 예정입니다 ㅎㅎ


    DB에 공통 코드를 넣는 것에 대해서는

    공통 코드의 그룹 코드를 따로 알아야 하고.

    DB 명세를 보고, 어떤 코드를 어떻게 조인해야 하는지 알기도 힘든 부분이 있어서

    불리점이 많은 것같아요.

    문서화가 잘 안되어 있으면, 찾아가기도 피곤하고..

    막상 되어 있어도, 문서까지 두번이상 거쳐서 원하는 코드를 획득해야 함으로...

    0
  • 전재형
    4k
    2017-04-14 21:27:27

    그리고, enum 에도  lombok 기능을 다 붙여 쓸수 있어요. 

    getter constructor :))

    0
  • byeworld
    2k
    2017-04-15 12:15:39 작성 2017-04-15 12:21:31 수정됨


    사례1 - code 관리용 테이블 대체하기

    사례2 - 타입별 다른 연산식 처리하기

    사례3 - 각 타입을 그룹화 하기

    enumeration 타입에 대해 세가지 관점에서 결론을 내리셨군요.. 


    우선 (사례 2)와 (사례 3)은 충분히 동의하고 공감할 수 있는 내용입니다.

    (사례 2)와 (사례 3)은 객체지향 언어에서 함수 오버로딩으로 인해

    이전 다른 언어(ex. C언어)보다 enum형의 활용이 줄어들어 저런 활용을 하지 않는 경향이 있죠. 


    1) 사용해야 하는 타입을 enum을 통해 알려주면, 
    
    2) 1)에서 알려준 enum에 따라 맞는 데이터 타입을 선택하고, 
    
    3) 2)에서 선택된 데이터에 맞는 연산을 선택


    이와 같은 활용이 enum의 주요 활용방식 중에 하나였습니다. (enum에 따른 선택/분기)


    함수 오버로딩에 의해 1) enum으로 알려줄 필요.가 없어지고 

    자동으로 2)와 3)이 일어나는 것이 객체지향 언어들의 특성이죠.. (enum활용의 감소..)

    (객체지향이란 것이 [자료형과 연산의 결합]이란 측면에서 분리해서 볼 필요는 없다 싶습니다.)


    내용을 보다보니  enum+function 인터페이스는 자세히 보게되는군요.. 

    Java를 통해 프로그래밍을 입문하신 분들은 위와 같은 내용은 모르실 수 있다는 생각이 듭니다. 


    사례 1의 코드 관리는 그렇게 하지 말라고 DB에 넣어서 코드 관리 하는 것인데 

    반대로 생각하시는 것 같습니다. 

    작은 프로그램이라면 DB에 코드로 관리하는 것이 비효율적이겠지만, 

    시스템화되고 규모가 커질수록 프로그램에서 코드를 관리하는 것은 복잡성과 혼란을 증가시킵니다.

    또한 Enterprise관점, 시스템 분산과 시스템간 협업, 전사 관리 등을 고려하게 될 경우 지양해야하는 방식입니다. 


    추가로 위에 3가지 관점 이외에 메모리 관리 관점이 있기는 한데, 

    요즘과 같은 환경이 아닌 옛날 플로피 디스크 쓰던 시절의 고려사항이니

    별로 중요하진 않은것 같습니다. 

    0
  • 로그인을 하시면 댓글을 등록할 수 있습니다.