프로젝트를 진행 중 여러 조건을 체크하여 프로세스를 수행해야 하는 상황이 있었다. 조건을 체크하기 위한 로직(본문에서는 ‘CheckeLogic’로 명명) 들이 적게는 10개에서 수십 개까지 늘어날 수도 있다. 문제는 이 CheckLogic만이 아니었다. 한가지의 대상에게만 적용하여 체크하는 것이 아니라 CheckLogic이 적용되는 항목의 수가 100여개, 중복까지 합하면 400여개, 또한 이 항목값에 따라 조건체크가 달라지는데 가질 수 있는 값도 7가지였다.

어떤 상황인지 이해를 돕기 위해 프로세스의 개념을 조금 더 자세하게 설명하자면, 비즈니스의 개념과 목적을 포함하고 있는 항목(본문에서는 ‘ProcessItem’로 명명) 들이 있다. 이 ProcessItem들은 상태(본문에서는 ‘State’라고 명명)라는 데이터를 가지는데 A~G라는 7개의 State가 있다. A~G의 State는 특정 CheckLogic을 수행하여 true일 경우 State의 값이 전이된다.

개발방향에 대한 고민

이처럼 ProcessItem들은 100개, CheckLogic은 수십 개, State는 7개가 되는 상황에서 보통의 조건 로직처럼 단순 if else 구문이나 switch case 구문으로 개발을 할 경우 3가지를 조합하면 나올 수 있는 경우의 수가 적어도 1000 이상은 되기 때문에 소스코드의 양도 많아지고 너무 지저분해질 뿐 아니라 중복되는 것이 많을 거라는 생각이 들었다

개발방향에 대한 제안

그래서 이것을 어떻게 해결할까 고민하다가 나온 한가지 방법이 ProcessItem과 State, CheckLogic의 상황에 맞는 조합 구성정보(본문에서는 ‘StateConfigXml’로 명명)를 XML로 관리하고 Java로직에서는 단순하게 CheckLogic을 수행하면 XML의 정보를 읽어 들여 그 조건에 맞는 CheckLogic이 수행되는 것을 생각하였다.

또한 동일한 CheckLogic이 여러 ProcessItem의 특정 State에서 사용되기 때문에 중복을 줄이고자 StateConfigXml에 CheckLogic 정보를 다 나열하지 말고 분리하여서 다른 XML에서 관리 하기로 했다.(본문에서는 ‘CheckLogicXml’로 명명) 이렇게 하면 StateConfigXml에서 CheckLogic의 중복되는 정보를 일일이 다 가지고 있지 않고 StateConfigXml과 CheckLogicXml을 매핑시켜줄  최소한의 정보만을 가지게 된다.

그리고 Java 로직에서는 각 조건에 맞는 CheckLogic을 공통적으로 수행하기 위해 Java의 Reflection을 사용하여
해당 CheckLogic의 클래스를 찾아서 로직 메소드를 수행하기로 하였다.

StateCheck에 대한 구성정보 XML

이러한 XML 정보들을 예시로 작성해 보겠다.
아래의 <소스코드 1>은 id가 ITEM01001인 ProcessItem에 대한 StateConfigXml을 기술 한 것이다.

소스코드 1. StateConfigXml

ProcessItem의 State가 StateA일 때 StateB로의 전이조건은 CheckLogicOne이고, StateE일 때는 StateF로의 전이조건 CheckLogicFive, StateD로의 전이조건 CheckLogicThree, CheckLogicSeven이 있다. 마지막의 경우처럼 State의 전이조건은 CheckLogic이 여러 개인 경우도 있다.

다음 아래의 <소스코드 2>는 CheckLogic들의 정보를 관리하는 CheckLogicXml 파일이다.

소스코드 2. CheckLogicXml

CheckLogicXml에서는 <소스코드 1>의 StateConfigXml에 기술 된 CheckLogic의 Id와 매핑 되어 각 CheckLogic의 클래스 정보를 가지고 있다. CheckLogic을 수행할 때 RunTime시 이곳의 클래스 정보를 참조하여 수행하게 된다.

StateCheck에 대한 구성정보 클래스 구조

이번엔 두 개의 XML을 파싱한 뒤 데이터를 담을 클래스의 구조를 아래의 <UML 1>로 보도록 하겠다.

<UML 1. StateConfigXml, CheckLogicXml>

XML의 구성정보를 담을 객체의 클래스는 총 5개로 최상위의 StateCheckConfig부터 시작해서 최하위의 CheckLogicConfig까지 하위의 객체를 리스트로 가지고 있는 계층구조이다.

아래의 <UML 2>는 CheckLogic 영역의 클래스 구조를 나타내고 있다.

UML 2. CheckLogic

CheckLogic Interface에는 execute라는 메소드가 명세 되어 있고 각 CheckLogic 클래스는 이를 implements 하여 execute( ) 메소드를 구현하고 있다. CheckLogic의 execute( )를 수행할 때는 구현 클래스를 직접 참조하는 것이 아니라 Interface를 통해서 참조하게 될 것이다.

이로써 ProcessItem의 특정 State에 따른 CheckLogic 수행 구성정보를 완성하였고, XML을 파싱 하여 객체에 담는 것은 SAXParser를 사용하였다. 파싱 하는 내용은 생략하도록 하겠다.

StateCheck 구성정보로 CheckLogic 수행

이제 이 구성정보를 가지고 CheckLogic을 수행하여 true인지 false인지 판별하고 true일 경우 상태전이를 수행하는 Java의 로직이 남았다.

아래의 <UML 3>는 Java 로직 영역의 클래스 구조를 나타내고 있다.

<UML 3. Java Logic>

위의 <UML 3>를 보면 StateCheck를 호출하는 서비스 영역과 StateCheck에 필요한 정보조회 및 StateCheck 후 업데이트를 수행하는 StateManger, StateCheckConfig의 정보를 조회하여 해당 CheckLogic의 로직을 수행한 후 결과를 리턴 해주는 CheckManager, StateCheckConfig의 구성정보를 관리하고 조회하는 StateConfigXmlLoader 영역이 있다. 메소드 호출 간에 필요한 데이터는 StateCheckBDT에 저장하여 주고 받는다. 각 클래스 간의 역할을 구분 지어서 나누도록 하였다.

CheckManager의 checkState( ) 메소드는 해당 ProcessItem의 현재 State값에 맞는 StateCheckConfig를 StateConfigXmlLoader를 통해 조회하여 구성정보에 매핑 되는 CheckLogic을 수행하게 된다.

아래의 <소스코드 3>은 Java의 Reflection을 사용하여 CheckLogic을 수행하는 소스코드이다.

<소스코드 3. CheckLogic Execute>

StateConfigXmlLoader를 통해 StateCheckConfig에서 조회한 클래스 정보로 해당 클래스가 구현하고 있는 execute( ) 메소드를 수행한다. 이 후에는 StateManager에서 상태전이를 판별하여 상태전이가 일어났을 경우에는 DB데이터를 업데이트 하게 된다.

마치며

지금까지 StateConfigXml과 CheckLogic의 XML 구조를 만들어 작성하고 XML의 데이터를 객체화 하는 것. 객체화 된 구성정보를 가지고 StateCheck를 수행하는 것. 또한 Interface와 Reflection을 이용하여 여러 CheckLogic을 수행하는 것을 진행하였다. 이것으로 여러 상황에서의 조건체크에 대한 개발방향을 고민하고 적용해 보았다.

하지만 위의 개발방향에서의 문제점이 몇 가지 있었다.

첫 번째는 오타.. Java가 아닌 XML의 양이 많기 때문에 저 많은 내용 중 1개만 오타가 있어도 찾기가 힘들고 그로 이한 에러가 자주 발생하였다. 더욱이 본인이 적용한 것은 StateCheck를 백그라운드로 진행하기 때문에 에러가 나도 로그를 유심히 보지 않으면 못보고 지나치고 어플리케이션에 치명적이지도 않기 때문에 놓칠 수가 있다. 그래서 이 문제는 DTD를 작성하여 어느 정도 감소시켰다.
XML 엘리먼트의 속성값이 오타가 나는 것까진 체크할 수 없지만 XML의 작성 틀을 구성하고, 꼭 필요한 속성이 빠지거나 오타가 나는 경우는 빨간 줄이 체크되기 때문에 어느 정도 수월했다.

두 번째는 유지보수 문제이다.. XML의 구조와 필요한 데이터를 찾는 프로세스가 조금 복잡하여 이것을 만든 본인도 가끔씩 생각이 필요할 때가 있다. 이것을 향후 운영자가 수정할 일이 있을 경우 또는 에러가 발생하는 상황에서 찾기가 힘들 것 같다..

본 글에서 적용해본 방법이 장점도 있고 단점도 있지만 적지 않은 정보들을 사람이 읽기 편하도록 가독성을 높이고, 각 목적에 맞는 역할을 구분 지어서 나눈 것도 객체지향에서의 장점으로 볼 수 있다고 생각한다. 물론 이것이 정말 객체지향적으로 잘 만들어졌냐는 또 다른 문제이겠지만..

지금까지 다룬 상황에 대해 이제 2년차가 되는 개발자가 해본 고민과 나름의 해결 방안이었다.. 물론 선배 개발자분이 큰 틀도 잡아주시고 많은 제안과 조언이 있었기 때문에 부족하지만 잘 해낼 수 있었던 것 같다.

글을 마치며 마지막으로 전체의 클래스 구조를 나타내었다.

<UML 4. StateCheck 전체>

개발환경

  • Java  : JavaSE 1.5
  • IDE  : Eclipse 3.4 Ganymede
  • WebServer  : Apache Web Server 2.2
  • WepAplicationServer  : Weblogic 9.3
  • OperatingSystem  : Windows7