본문 바로가기

Homo Design

테스트케이스, 유스케이스, 그리고 인터페이스 식별

분석 활동은 주어진 환경에서 시스템이 가지는 인터페이스를 식별하는 작업을 주로 합니다. 하지만, 방법론 상에서는 이러한 식별 과정에 대한 언급보다는 주로 그 결과만을 이야기하고 있습니다. 식별 과정은 다소 분석/설계자의 경험적인 지식이 크게 좌우되기는 하지만, 유스케이스를 통해서 어느 정도는 해당 업무 비전문가라도 인터페이스 식별은 가능합니다. 다만, 그러한 식별 과정에 대한 지식은 가지고 있어야 되겠죠.

유스케이스 활용에 대해서 부정적인 의견도 있긴 하지만, 이를 잘 활용하면 인터페이스 식별에 크게 도움이 됩니다. 유스케이스는 다이어그램을 그리는 행위보다 비즈니스의 경우의 수를 정리하는 과정이라고 보면 됩니다. 예를 들어, 계좌이체를 하는 비즈니스는 그 행위가 은행에서 직원을 통해서 수행하는 경우와, ATM에서 고객이 직접 수행하는 경우, 혹은 스마트폰이나 인터넷으로 수행하는 경우 등 다양한 방법들이 있습니다. 계좌이체에 대한 방법은 다양하지만, 그 업무의 본질은 어느 한 계좌에서 다른 계좌로 돈을 전송한다라는 것은 동일합니다. 즉, 계좌이체라는 유스케이스는 수많은 경우의 수가 있지만, 이를 하나의 사용 방법으로 유스케이스를 통해서 정의를 하는 것입니다.

그렇다면, 유스케이스에서는 비즈니스를 어떻게 기술하는지 살펴보면, 우선 액터(사용자)와 시스템 간의 상호작용을 기술하게 됩니다. 즉, 사용자가 어떠한 이벤트를 발생하면, 시스템이 어떠한 응답이나 반응을 한다와 같은 내용으로 작성합니다. 이와 같이 업무 흐름을 작성하다보면, 너무나도 긴 흐름들이 발생할 것이고, 혹은 유사한 흐름들이 발견되는데 이때 유스케이스를 구조화시켜서 적절한 수준에서 (크기가 어느 정도 유사한 수준) 정리를 합니다. 어쨌든, 이러한 유스케이스 흐름에서 시스템의 응답이나 반응이 결국은 인터페이스가 됩니다. 즉, 인터페이스가 이 시점에 식별이 되는 것입니다.

유스케이스는 다소 추상화 관점이 높은 수준에서 작성되기 때문에 구체적인 시스템에 사용 방법은 이러한 유스케이스를 구체화시켜서 작성하면 됩니다. 예를 들어, '액터는 이체할 해당 계좌를 선택하고, 전송할 계좌와 금액을 입력하고, 계좌이체를 수행하면, 시스템은 송수신 계좌와 이체금액을 입력받고 송신 계좌에서 금액을 차감하고 수신 계좌에 금액을 합산하고 그 결과를 액터에게 알려준다.'와 같은 유스케이스의 흐름에 대해서 구체화시키면 다음과 같습니다.

'직원 ID가 1000034인 사용자가 송신 계좌번호 ID가 123456789, 수신 계좌번호의 은행코드는 54930, 수신 계좌번호는 1234583373, 금액 1000000을 입력하고, 전송을 수행하면, 시스템은 송신 계좌번호 ID와 수신 계좌번호의 은행코드 및 계좌번호, 금액을 입력받아서 ... 처리한 후에 그 결과값으로 송신 계좌번호 344839202, 수신 계좌번호 1234583373, 은행명 OO은행, 금액 1,000,000원, 계좌이체 성공이라는 메시지를 전송한다.'


유스케이스 흐름의 특수화된 형태를 자세히 살펴보면, 테스트케이스와 유사하다는 것을 알 수 있다. 즉, 이를 테스트코드로 변환한다면 다음과 같습니다.

Employee sender = new Employee("1000034");
TransferArg arg = new TransferArg();
arg.setSenderAccountId("123456789");
arg.setBankCodeOfReceiver("54930");
arg.setAccountOfReceiver("1234583373");
arg.setAmountOfTransfer(1000000);

TransferService service = new TransferService();
TransferResult result = service.execute(sender, arg);

assertEquals("344839202", result.getAccountNoOfSender());
assertEquals(arg.getAccountOfReceiver(), result.getAccountOfReceiver());
assertEquals("OO은행", result.getBankNameOfReceiver());
assertEquals(arg.getAmountOfTransfer(), result.getAmountOfTransfer());
assertEquals("계좌이체 성공", result.getSuccessMessage());


임의로 코드를 작성해보았지만, 코드 내용은 유스케이스의 흐름을 검증하기에 충분합니다. 물론, 해당 테스트 값들은 다양하게 변경하여 TransferService의 구현 내용을 더 풍부하게 만들 수도 있겠지만, 지금은 분석 활동 중이라는 것을 잊지 마시기 바랍니다. 아무리 테스트 데이터를 다양하게 하더라도 해당 인터페이스는 크게 변화가 없을 것입니다. 위의 예제는 직원이 계좌이체를 수행하는 경우를 테스트케이스로 만들어 보았지만, 고객이 직접 자신의 계좌에 대해서 계좌이체를 하는 테스트케이스 역시 만들 수 있을 것입니다. 이 경우에는 물론 인터페이스가 다른 형태로 식별될 것입니다.

어찌되었든 간에, 유스케이스를 작성하고 이에 특수화된 경우의 수들을 만들어내면 바로 테스트케이스가 만들어집니다. 유스케이스를 작성을 하고 인터페이스를 식별할 수도 있겠지만, 이 경우에는 인터페이스의 오퍼레이션들이 구체적인 형태가 아니라 좀 더 추상화 수준이 높은 인터페이스가 식별될 것입니다. 만일 좀 더 분석/설계 기간이 길고 구현 기간이 충분하다면 추상화 수준을 높이고 점점 시간이 흐르면서 구체화시키는 방법을 사용할 수도 있겠지만, 대부분의 경우 프로젝트에서 그러한 시간을 구하기가 좀처럼 쉽지는 않습니다. 따라서, 구체적인 인터페이스를 식별하려면 위와 같은 테스트케이스를 작성하여 인터페이스 식별이 가능합니다. 다만, 유스케이스의 경우의 수는 분석을 수행하는 기간 동안에 모두 식별되기는 힘들겁니다. 또한, 추후 비즈니스 정책이나 요구사항이 변경되면서 이후 단계에서 특별한 경우의 유스케이스 용례(usage)가 식별되는 경우도 있기 때문에 이러한 과정은 반복을 통해 수행해야 할 것입니다.

이와 같은 방법은 비단 액터와 시스템 간의 상호작용에서만 사용될 수 있는 기법은 아닙니다. 컴포넌트와 컴포넌트, 시스템과 시스템, 혹은 컴포넌트 내의 두 객체 간에도 이러한 기법을 사용해서 인터페이스(오퍼레이션) 식별이 가능합니다. 다만, 명확하기 식별되는 경우는 이러한 과정을 거칠 필요는 없겠죠. 인터페이스를 식별하기가 까다롭거나 애매한 경우에는 바로 그 용례를 정의해보고 거기에 대한 구체화된 사용 방법을 적어서 인터페이스 식별이 가능합니다.

이와 같은 방법은 시스템의 내부에서 바라보는 뷰를 시스템의 외부에서 바라보는 뷰로 전환하는 것이 필요합니다. 즉, 인터페이스를 정의할 때에는 반드시 그 대상 객체를 둘러싸고 있는 환경을 먼저 식별해야 한다는 것입니다. 테스트케이스를 작성하기 어려운 점이 바로 그것입니다. 기존 코드를 만드는 방식은 시스템 내부에서 외부를 바라보는 뷰로 개발을 하는 방식이어서 외부에서 바라보는 환경을 고려하지 않거나 혹은 외부 영향 요소로 판단하여(가정하여) 작업을 합니다. 그렇다 보니, 외부의 변화에 대해서 쉽게 대처하기 힘든 시스템이 설계될 가능성이 높습니다.

테스트 케이스는 이러한 개발 방식에 변화를 주어서 외부에서 시스템을 바라보는 뷰를 갖게끔 하는 장점이 있으며, 이 둘 간의 조화가 어느 정도 이루어져야 시스템이 외부 변화에 대해서 어느 정도는 대처할 수 있는 구조로 설계될 수 있습니다.

테스트 케이스로 인터페이스를 식별하는 방법은 유스케이스로 인터페이스를 식별하는 방법과 크게 다르지 않으며, 외부에서 시스템을 바라보는 뷰를 확장시켜 줍니다.
반응형