대메뉴 바로가기 본문 바로가기

데이터 기술 자료

데이터 기술 자료 상세보기
제목 오래도록 발전하는 개발자로 살아가기 : 입에 쓴 보약, TDD와 BDD
등록일 조회수 6882
첨부파일  

오래도록 발전하는 개발자로 살아가기

입에 쓴 보약, TDD와 BDD



개발자로서 발전이 없다고 느껴질 때, 자신의 업무와 더불어 발전할 수 있는 방법이 있다. 바로 Test Driven Development(이하 TDD)와 Behavior Driven Development(이하 BDD)가 그 방법이다. 이 두 가지 방식은 모든 프로젝트에서 100%의 효과를 보장하지는 않지만 대부분의 프로젝트에서 품질 향상, 설계 개선, 유지보수의 접근성에 효과적이라는 정보들을 쉽게 접할 수 있다. 이에 대해 필자가 좀더 덧붙인다면 개발자에게 주관적, 객관적 코드 영감을 주고 개선에 영향을 끼친다는 장점이 있다.



TDD의 접근은 매우 어려운 것이 사실이다. 생산 코드를 먼저 만들어내는 정서와 반대로 테스트 코드를 먼저 만들면서 개발한다는 것 자체는 매끄러운 쌀밥과는 다른 까끌한 현미밥을 먹는 듯한 느낌을 받게 한다. 물론 TDD의 진입장벽을 허물기 위해 고안된 BDD를 통해 TDD의 접근 방식이 좋아졌다고 하지만 개인차가 존재하고, 적지 않은 학습과 습관을 요구한다. 또한 아직 많은 개발자들이 20% 가량 일정이 더 소요된다는 사실 때문에 TDD/BDD의 도입을 꺼려하고 있는 것이 현실이다. 그리고 프로젝트 마감일을 가장 중요하게 여기는 SI성 프로젝트들이 우리나라 소프트웨어 시장의 대부분을 차지하고 있기 때문에 TDD/BDD의 도입이 더디게 진행되는 것도 우리의 현실이다.

하지만 앞서 피력했던 것처럼 TDD와 BDD는 매력적이면서 파격적인 장점을 가지고 있기에 좋은 소프트웨어 개발자에게는 반드시 알아야 할 덕목 중에 하나임은 틀림없다. 따라서 필자는 이와 같은 XP의 최대 덕목인 TDD와 TDD를 응용해 코드 생산의 다른 시점을 제시하는 BDD에 대해 비교·분석하고 실험을 통해 독자들이 도입 의지를 불태울 수 있기를 바라며 이 글을 시작한다.

화성에서 온 TDD, 금성에서 온 BDD?

테스트 코드를 작성하고 테스트로 검증된 코드를 가지고 실제 코드를 작성하자는 TDD는 그 선구자 격인 켄트 벡(Kent beck)을 필두로 많은 발전을 해왔다.

먼저 개발하고 테스트하는 기존 방식이 아닌 테스트 코드를 작성하고 검증된 코드를 실제 코드로 반영하자는 개념은 어떻게 보면 개발자들에게는 혁명적인 발상이었고, 많은 개발자들의 호응을 거쳐 현재는 애자일의 대표적인 개발 방법론이 됐다. 하지만 설계 계선, 자동화된 테스트 유닛, 검증된 코드로 인한 안정성 등 수많은 장점을 가지고도 여전히 많은 개발자들에게는 혼란과 의심을 남기고 있었다.

일례로 당시 TDD에 대한 코칭을 하던 BDD의 아버지 댄 노스(Dan north)는 TDD에서 다음과 같은 벽에 마주쳤다.

1. 프로세스의 어디서부터 시작해야 하는가(Where to start in the process).
2. 무엇을 테스트하고 또 무엇을 하지 말아야 하는가(What to test and what not to test).
3. 한 번에 얼마만큼 테스트해야 하는가(How much to test in on go).
4. 테스트를 어떻게 명명해야 하는가(What to call the tests).
5. 테스트가 실패하는 이유에 대해 어떻게 이해해야 하는가(How to understand why a test fails).

BDD의 핵심은 댄 노스가 이러한 이슈들을 처리하는 동안 함께해온 단위 테스팅(Unit testing) 및 인수 테스팅(Acceptance testing)에 대한 접근의 재해석에서 비롯됐다. 개발자들은 전통적인 TDD 소프트웨어 개발 방법론에서 다음과 같은 흐름을 따른다.

1. 첫 번째로 유닛을 위한 테스트 셋을 정의한다(define a test set for the unit first).
2. 유닛을 구현한다(the implement the unit).
3. 마지막으로 유닛에 대한 구현이 테스트를 통과하는지 검증한다(finally verify that the implementation of the unit makes the tests success).

이 정의는 높은 수준의 소트프웨어 요구사항이지만, 반대로 이 수준의 기술적 상세내역을 포함해 심지어 어떠한 관점에서의 테스트에서도 각 유닛들에게 기대되는 행위들에 대해 명시적이지 않고 또 명세화돼 있지 않았다. 이에 반해 BDD는 소프트웨어 내의 각 유닛에 대해 기대되는 행위들을 명세화하면서 출발한다.

이에 댄 노스와 그의 동료들은 하나의 템플릿을 만들어 채택했는데 그것은 다음과 같다.

1. 특정 값이 주어지고(Given)
2. 어떤 이벤트가 발생했을 때(When)
3. 그에 대한 결과를 보장해야 한다(Then).

프로그래밍적으로 풀어서 쓰면 어떤 조건에서 혹은 어떤 파라미터가 들어왔을 때 테스트하고자 하는 프로세스가 실행되면 그에 상응하는 결과가 나와야 한다는 것이다. 여기서 각각은 And로 이어져 복수(複數)가 될 수도 있다. 여기까지의 설명을 통해 특정 유닛에 대한 행위 및 요구사항을 정리할 수 있겠다는 느낌을 가지길 바란다.

댄 노스는 TDD 코칭을 하던 시절 ‘Test’라는 단어를 사용하지 않는 편이 사람들이 좀더 TDD의 원리를 이해하는 데 도움될 것이라 생각했고, Test라는 개념보다는 ‘Behaviour’라는 개념을 썼을 때 테스트 유닛의 의도가 좀더 명확해진다는 것을 깨달았다. 그리고는 모든 테스트 메소드명에 ‘should’를 넣기 시작했다.

should는 영문법에서 어떤 행위를 기대한다는 조동사다. 이를 사용함으로써 개발자는 더 이상 테스트 메소드를 작성할 때 어떻게 명명해야 하는지 혼란을 느끼지 않아도 되고, 이로써 테스트 유닛에 기대되는 행위들이 명확해진다. 또한 작성하는 코드가 기대하는 행위에 맞지 않는다면 이는 리팩토링의 징조가 되기 때문에 리팩토링을 통해 모든 구현 코드의 의도가 명확해진다.

‘여기까지의 내용을 통해 TDD와 BDD가 도대체 어떻게 다른 것인가?’라는 의문이 생긴다. 필자는 BDD를 알기 전에 이미 Given, When, Then의 흐름으로 테스트 코드를 작성하고 있었고 Mock 프레임워크의 한 종류인 mockito를 이용해 특정 메소드가 실행됨으로써 검증해야 할 행위를 테스트하고 있었다.

이에 대해 댄 노스는 다음과 같이 말했다.

“당신이 조그마한 개발팀에 있고 당신과 일하는 대상이 모두 개발자들이라면 그것이 굳이 BDD일 필요는 없다. 왜냐하면 BDD는 TDD 사용자들의 공식화된 좋은 습관들 위에 형성됐기 때문이다. 하지만 BDD는 개발자의 언어로 이뤄지는 TDD에 반해 테스트 시나리오를 읽는 대상이 좀더 넓혀진 개념이다.”

그렇다면 과연 좋은 습관은 어떤 것이며 시나리오를 읽는 대상이 넓혀진 개념이라는 것은 무엇을 말하는 걸까? 그것은 다음과 같다.

1. Outside-in 개념으로 작성하고 비즈니스 또는 조직적인 목표(?)로 시작하라(Working outside-in, starting from a business or organizational goal).
2. 요구사항을 분명히 한 예제를 이용하라(Using examples to clarify requirements).
3. 유비쿼터스 랭귀지를 이용해 개발하라(Developing and using a ubiquitous language).

첫째, 만약에 컴퓨터에게 어떻게 요구사항을 체크해야 하는지 설명해야 할 일이 있다면 일반적으로는 그것들을 모두 이해해야 한다. 하지만 그렇지 않다면 코드를 작성하기 전에 그것들에 대해 정리를 하는 것이 훨씬 효율적이다.
둘째, 예제는 요구사항에 대해 정말 이해하고 있는지 확인하는 가장 좋은 방법이다. BDD는 행위를 표현하는 시나리오를 이용하는 것을 장려함으로써 이를 공식화하게 한다. 또한 이렇게 작성된 예제들은 비즈니??에 이르면서 협업의 중요성이 나날이 강조되고 있다. 개발자와 비 개발자 모두 쉽게 이해할 수 있는 표현을 사용한다는 것은 양쪽 모두에게 상당한 이익을 가져다준다. BDD에서 강조되는 점은 협업이다. BDD에서는 유비쿼터스 랭귀지라는 언어로 테스트 시나리오를 작성하는데 이는 DDD에서 차용한 개념이다. 유비쿼터스 랭귀지는 소프트웨어 개발팀원들 모두에게 공유될 수 있는 준공식 랭귀지(semi-formal language)로 이렇게 생성된 문서들을 비즈니스 사용가들과 분석가들, 테스터들까지도 이해할 수 있게 된다는 것을 의미한다.
다음은 대표적인 BDD 프레임워크인 JBehave에서의 테스트 시나리오의 예시다.

Scenario 1 :
Refunded items should be returned to stock.
환불 품목은 재고품으로 반환돼야 한다.
Given a customer previously bought a black sweater from me.
고객은 전에 나에게 검은 스웨터를 샀다.
And I currently have three black sweaters left in stock.
그리고 나는 현재 3개의 검은 스웨터를 재고로 가지고 있다.
When he returns the sweater for a refund.
그가 환불을 위해 스웨터를 돌려줄 때.
Then I should have four black sweaters in stock.
그러면 나는 4개의 검은 스웨터를 제고로 가져야 한다.

Given, When, Then의 흐름으로 각 유닛에 대한 시나리오를 기술하는 JBehave는 앞의 예시에서도 볼 수 있듯이 특정 유닛에게 기대되는 행위들이 잘 명세화돼 있다. 그것도 누구나 알아볼 수 있는 언어로 말이다. 이런 방법으로 BDD는 프로젝트 내에서 서로 다른 업무를 가지고 있는 구성원들 간의 커뮤니케이션 용도로 발전했다. JBehave의 실제적인 코드는 다음 챕터에서 보도록 한다.

같은 방법, 다른 시선

이제부터는 간단하게 친구 신청/확인 기능을 TDD와 BDD로 작성해보며 이 둘을 살펴보려 한다. 제한된 공간 안에 TDD와 BDD를 설명해야 하기 때문에 바로 구현으로 들어가도 될 만큼 작은 기능을 대상으로 선정했다. 간단히 기능 정의를 하고 시작하자.

1. 친구 신청/확인에 필요한 기능은 다음과 같다.
- 사용자는 친구를 맺기 위해 상대방에게 친구 신청을 할 수 있다.
- 사용자는 다른 사용자와의 친구 여부를 확인할 수 있다.

2. 간단하게 클래스로 만들어 볼 수 있는 대상은 2개 정도다.
- 사용자
- 친구 신청/확인

기능이 크지 않으므로 사용자 클래스 하나를 두고 이 안에서 친구 신청 및 확인 기능을 함께 구현하도록 한다. 정리하면 사용자 클래스 하나에 친구 신청, 친구 확인 기능이 필요하다.

TDD

TDD로 이 내용을 구현해보자. 가장 먼저 무엇을 테스트해 보는 것이 좋을까? 친구 여부를 확인하기 위해서는 친구 신청을 먼저 구현해야 한다. 그런데 친구 신청을 했는지 확인하려면 친구 여부 확인이 먼저 필요한 것 아닐까? 이런 경우는 그냥 둘 다 함께 확인할 수 있는 테스트 코드를 작성하면 된다. 먼저 앞에서 정의한 기능을 User 클래스의 메소드로 선언한다.

<리스트 1> User.java @Getter @Setter public class User { private String name; public User(String name) { // TODO } public void sendFriendRequestTo(User receiver) { // TODO } public boolean isFriendWith(User receiver) { // TODO } }

<리스트 1>을 확인하기 위해 작성한 코드는 <리스트 2>와 같다.

<리스트 2> UserTest.java - User.java 확인 public class UserTest { @Test public void sendRequestTo() { // Given User userA = new User("A"); User userB = new User("B"); // When userA.sendFriendRequestTo(userB); // Then userA.isFriendWith(userB); } }

A라는 이름의 사용자와 B라는 이름의 사용자가 전제조건(Given)으로 주어져 있고, A가 B에게 친구 요청을 던진 경우(When), A가 B와 친구가 됐는지를 확인(Then)하고 있다. 이제 User의 내부를 빠르게 구현해보면, <리스트 3>과 같은 코드가 나온다.

<리스트 3> User 내부 구현 @Getter @Setter public class User { private String name; private List<User> friends; public User(String name) { this.name = name; this.friends = new ArrayList<User>(); } public void sendFriendRequestTo(User receiver) { friends.add(receiver); } public boolean isFriendWith(User receiver) { return friends.contains(receiver); } }

<리스트 3>의 코드를 보면 B의 입장에서 A가 친구인지의 여부가 의심될 것이다. 이런 경우라면 <리스트 4>와 같이 Test 케이스를 추가해 확인해 볼 수 있다.

<리스트 4> UserTest.java - Test 케이스 추가 public class UserTest { ... 생략 @Test public void sendRequestTo_상대방에게_요청을_받은_경우() { // Given User userA = new User("A"); User userB = new User("B"); // When userB.sendFriendRequestTo(userA); // Then Assert.assertTrue("친구 요청을 받으면 친구가 되야 한다.", userA.isFriendWith(userB)); } }

필요하다면 <리스트 5>와 같이 메소드에 설명을 붙여 케이스를 추가할 수 있다.

<리스트 5> 메소드에 설명을 붙여 케이스 추가 isFriendWith_친구신청_하지_않은_경우 sendRequestTo_중복으로_친구신청을_한_경우 ...

우선 새롭게 추가한 테스트 케이스가 실패함을 확인할 수 있다. 이는 단방향이 아니라 양방향의 관계를 작성하는 것이 익숙하지 않은 경우 종종 발생하는 문제다. User 클래스는 <리스트 6>과 같이 고쳐서 Test 코드를 성공으로 돌려놓을 수 있다.

<리스트 6> User.java - User 클래스 수정 public class User { ... 생략 public void sendFriendRequestTo(User receiver) { friends.add(receiver); receiver.getFriends().add(this); // 동기화 코드가 필요함 } ... 생략 }

<리스트 6>은 친구 중복에 관한 처리 등을 비롯해 테스트 케이스 추가와 코드 보완, 리팩토링이 필요하지만 코드 소개의 목적이 TDD와 BDD의 비교이므로 일단 이것으로 TDD에 대한 간단한 소개를 마친다.

BDD

이번에는 똑같은 기능을 BDD로 작성해보도록 하자. BDD를 작성하는 데 사용한 프레임워크는 JBehave다. 총 5단계를 거쳐 작성하게 되는데 순서는 다음과 같다.

1. 스토리를 작성한다.
2. 시나리오를 실행시킬 Step 클래스(POJO)를 작성한다.
3. Embeddable 클래스를 작성해 관련된 설정을 지정한다.
4. 시나리오를 실행시킨다.
5. 결과를 확인한다.

가장 먼저 스토리를 작성한다. 앞서 정의한 기능을 토대로 <리스트 7>과 같이 작성해볼 수 있다.

<리스트 7> friend_request_story.story Narrative : 사용자는, 친구를 맺기 위해, 다른 사용자에게 친구 신청을 할 수 있다. Scenario : 친구 요청 Given &#39;A&#39;라는 사용자가 있다. And &#39;B&#39;라는 사용자가 있다. When &#39;A&#39;가 &#39;B&#39;에게 친구 신청을 한다. Then &#39;A&#39;는 &#39;B&#39;와 친구가 된다. Scenario : 친구 요청 (받는 경우) Given &#39;A&#39;라는 사용자가 있다. And &#39;B&#39;라는 사용자가 있다. When &#39;B&#39;가 &#39;A&#39;에게 친구 신청을 한다. Then &#39;A&#39;는 &#39;B&#39;와 친구가 된다. And &#39;B&#39;는 &#39;A&#39;와 친구가 된다. // 두꺼운 글씨로 작성된 부분은 꼭 지켜줘야 하는 문법(JBehave 혹은 Grekin Language)이다.

크게 두 가지로 구성되는데 Narrative 부분은 테스트 대상이 되는 스토리를 설명하는 부분이다. 이 형식은 애자일 개발 방법에서 많이 사용하는 사용자 스토리(User Story) 작성 템플릿으로, 역할(Role) - 기능(Feature) - 목적(Benefit)의 요소로 구성돼 있다.

- As a [역할]
- I want [기능]
- So that [목적]

이 형식이 기본 템플릿인데, 한글로 작성했기 때문에 순서를 바꿔 작성했다.
다음으로 Scenario 부분이다. Given에 주어진 조건을 적고, When에 테스트하고 싶은 어떤 행위를 기술하면 된다. 그리고 마지막 Then에는 기대하는 결과를 작성하면 된다. 기본적인 형식은 다음과 같다.

- Given [주어진 조건]
- And [주어진 다른 조건] ...
- When [행위 또는 사건]
- Then [결과]
- And [다른 결과] ...

스토리를 작성하는 방법에 대해 자세히 알고 싶으면 인터넷에서 ‘http://dannorth.net/whats-in-a-story’ 페이지를 참조하도록 한다. Step 클래스 작성에서는 friend_request_story와 이름 구조를 맞춰줘야 한다. FriendRequestStep이라는 이름으로 <리스트 8>과 같은 Step 클래스를 작성했다.

<리스트 8> FriendRequestStep.java public class FriendRequestStep { private UserRepository repository; @BeforeStories public void setUp() { repository = new UserRepository(); } @Given("&#39;$userName&#39;라는 사용자가 있다.") public void givenThereIsUser(String userName) { User user = new User(userName); repository.save(user); } @When("&#39;$senderName&#39;가 &#39;$receiverName&#39;에게 친구 신청을 한다.") public void whenSendFriendRequest(String senderName, String receiverName) { User sender = repository.findByName(senderName); User receiver = repository.findByName(receiverName); sender.sendFriendRequestTo(receiver); } @Then("&#39;$senderName&#39;는 &#39;$receiverName&#39;와 친구가 된다.") public void thenTheyAreFriend(String senderName, String receiverName) { User sender = repository.findByName(senderName); User receiver = repository.findByName(receiverName); Assert.assertTrue(sender.isFriendWith(receiver)); } // DB 관련된 사항이 결정되지 않았다고 가정하고 도우미 클래스를 만든다. private class UserRepository { private final List<User> userList; public UserRepository() { userList = new ArrayList<User>(); } public void save(User user) { userList.add(user); } public User findByName(String userName) { for (User user : userList) { if (user.getName().equals(userName)) { return user; } } return null; } } } User 데이터들을 관리하는 모듈이 필요한데 여기서는 DB Access(DAO, Repository 사용 여부 등) 관련 사항이 결정되지 않아서 UserRepository라는 Mock을 사용했다. 이런 식으로 아직 구현되지 않은 모듈이 있는 경우에도 원래 구현하려던 기능에 집중해 코드를 작성할 수 있다. 추후 DB 관련 사항이 결정되면 UserRepository를 실제 모듈로 대체할 수 있을 것이다.

@BeforeStory 부분은 시나리오를 실행하기 전에 환경적인 부분을 설정하는 곳이다. 물론 데이터를 미리 설정하는 공간으로 활용할 수도 있지만, 필자는 주로 환경적인 부분을 만드는 데 이를 활용해 @Given과의 용도를 구분한다.

@Given, @When, @Then은 Story 파일에서 Given, When, Then을 매핑하는 어노테이션이다. 그리고 각 어노테이션 안에 Story의 각 문장을 매핑하는 값들이 있다. 달러($) 표시로 시작하는 부분은 Story 파일에서 마음대로 값을 지정할 수 있는 부분을 의미한다. 이런 특징으로 인해 하나의 매핑 메소드에 여러 개의 문장을 다양한 값으로 대응시킬 수 있다. 이는 JBehave가 주는 꽤나 좋은 이점이다. 프레임워크 없이 작성했다고 하면 우리는 일일이 똑같은 테스트 메소드를 추가해 그 안에 Given이나 When 등을 작성해야 했을 것이다. 실제로 Given에서 표현한 2개의 문장은 하나의 메소드로 모두 수행할 수 있다.

<리스트 9> 2개의 문장을 하나의 메소드로 수ven &#39;A&#39;라는 사용자가 있다. And &#39;B&#39;라는 사용자가 있다. 만약 1명의 사용자를 더 늘려 시나리오를 테스트하고 싶거나, 또 다른 시나리오를 추가하려는 경우 이런 재사용은 점점 이점으로 다가올 것이다. 흔히 Step 클래스는 Story의 Type이며, Story는 이 시스템의 구체적 행위이자 인스턴스라고 표현하기도 한다. 이제 Embeddable 클래스를 작성하자. 여기서 설정 코드를 작성하는 것은 생략하도록 한다. 이 설정에서는 Story를 Step에 매핑하는 방식이나 결과를 어떤 형태로 보여줄 것인지 등을 결정할 수 있다. 시나리오 실행으로 JUnit을 통해 결과를 확인할 수 있다. <리스트 9>의 경우 <리스트 10>과 같은 결과를 확인할 수 있다.

<리스트 10> 결과 확인 (BeforeStories) Running story friends/friend_request_story.story (friends/friend_request_story.story) Scenario : 친구 요청 Given &#39;Jobs&#39;라는 사용자가 있다. And &#39;Gates&#39;라는 사용자가 있다. When &#39;Jobs&#39;가 &#39;Gates&#39;에게 친구 신청을 한다. Then &#39;Jobs&#39;는 &#39;Gates&#39;와 친구가 된다.

코드를 통해 느껴본 TDD와 BDD

지금까지 친구 신청/확인 기능을 구현하는 TDD와 BDD에 대해 살펴봤다. TDD와 BDD의 의미 있는 차이라고 한다면, Story 파일의 존재다. 이것은 비 개발자와 소통하는 동시에 시스템의 행위를 보존해주는 도구로 사용될 수 있다. 분명 도표나 그림 등을 통해 더 많은 것을 쉽게 표현할 수도 있기 때문에 시나리오로 모든 것을 표현하는 데는 한계가 있다. 그럼에도 불구하고 필자는 코드를 작성하기 전에 이런 시나리오들을 간단히 작성해 보는 것을 좋은 개발 시작 지점으로 삼을 수 있었다. JBehave는 이것을 프레임워크에서 지원해주며, 이것이 BDD가 가진 주요 철학 중 하나다.

더불어 프레임워크를 통해 TDD의 반복되는 코드 작업을 줄여주는 이점을 취할 수도 있다. 프레임워크를 사용하지 않는 경우, 여러 케이스를 표현하기 위해 중복되는 코드들이 나오기 마련이다. 이를 SetUp(JUnit에서는 @Before 어노테이션 사용) 부분에 넣으면 반복 작업은 줄지만 가독성이 떨어지고 점점 코드가 복잡해질 여지가 있다. 그래서 어느 정도의 반복 작업을 하게 되는 것이다. 그런데 JBehave는 Step을 Type으로 작성해 여러 시나리오들을 좀더 수월하게 테스트해볼 수 있었다. 이는 테스트 코드 관리의 부담을 줄여주는 하나의 방법이 될 수 있다.

그 외에도 In-Out 방식과 시나리오 형태로 테스트 주도 개발을 할 수 있다는 점은 BDD(정확히는 JBehave 프레임워크)가 강제하는 형식이다. 물론 Out-In 방식이 유리한 경우도 있고, 시나리오가 아닌 단순 스펙 확인 방식이 좋은 경우도 있다. BDD 프레임워크가 이런 유연성을 제한하는 것은 분명 한계일 것이다. 그럼에도 불구하고 BDD가 가진 철학들은 한 번은 적용해 볼 만한 내용이다.

칼로 물 베기

앞서 이야기한 것처럼 BDD와 TDD는 개발자에게 요구사항을 구현하기 위한 다양한 시각을 제공한다. 하지만 구현체를 어떤 방식으로 접근하는가에 대한 방법만을 제외하면 구현 방식은 매우 흡사하다 못해 똑같다고 볼 수도 있다. 이쯤 되면 독자는 ‘BDD와 TDD를 굳이 나눠야 하는가?’라는 생각이 들 것이다. 그런 의구심들은 수많은 사람들을 통해 BDD와 TDD의 다른 점을 두고 다양한 논쟁거리와 이슈로 재기돼 왔고, 대부분은 TDD의 한 종류로 BDD를 볼 수 밖에 없다고 결론지어지고 있다. TDD는 단위 테스트에 기반하는데 단위 테스트의 행위기반 테스트가 BDD의 기둥과 같은 개념인 Given, When, Then과 유사한 처리 과정을 갖기 때문이다.

BDD를 접할 때 가지는 기대감은 TDD와 유사할 수 있다. 하지만 BDD가 아주 새롭거나 도입 후 TDD만큼 현존하는 개발론에 지대한 영향을 미칠 정도의 파급효과를 갖지는 못한다. 그럼에도 불구하고 BDD가 아직까지 존재하는 이유는 뭘까? 그것은 앞에서 잠깐 언급했듯이 고객의 입장에서 사고하게 만드는 것과 TDD의 촉매제 역할을 도모할 수 있다는 점 때문이다. 또한 TEST라는 의미의 엔지니어적 사고에서 벗어나 Should라는 사고의 전환과 이를 통한 당위성에 대한 기준으로 구현체를 만들어간다는 것만으로도 BDD는 존재할 만한 가치가 있는 개념이다.

개발자의 입장에서는 TDD와 BDD의 모호성으로 혼돈을 일으킬 수도 있고, 어떤 것이 더 효과적인 방법인가에 대해 고민할 수도 있다. 하지만 고민은 잠깐 접어두자. TDD와 BDD는 목적이 될 수 없는 존재이고, 단순히 목적을 이루기 위한 방법이기에 우선 본인의 목적을 이루기 위해 상대적으로 조금이라도 편한 방법을 택하거나 혼용할 것을 추천한다.

정리하며

앞서 제시된 장점들에도 불구하고 많은 회사의 사업 환경과 개발 환경에서 TDD와 BDD의 도입은 망설여지고 있다. 경영 측면에서 TDD/BDD와 ROI에 대한 논의는 ‘해봐야 알 수 있다’는 쪽으로 귀결되고 있기 때문이다. 그리고 개발자 역시 프로젝트의 상황과 개인이 가진 구현체에 대한 안목으로 TDD와 BDD를 모든 구현체 개발에 반드시 적용해야 한다는 것은 다소 무리가 있다. 때로는 여건과 환경에 따라 빠르게 목적을 달성하고 이후 단위 테스트와 리팩토링 및 설계 조정을 하는 방법을 채용하기도 한다. 이와 같은 방법은 모듈 간 협업이나 자명한 알고리즘 구현에도 TDD/BDD보다 더욱 빠르게 개발할 수 있다는 장점을 가진다.

이런 장벽이 있음에도 불구하고 필자가 TDD와 BDD의 도입을 독려하는 가장 큰 이유는 구현체 성숙도가 높아진다는 점 때문이다. 구현체의 성숙도라는 것은 목적 코드 작성 시에 일어날 수 있는 예외사항 추정과 다른 모듈과의 연계성 고려, 문제해결을 위한 패턴화 능력 등의 성숙을 말한다. 구현체의 성숙도가 높아지는 이유는 다음과 같다.

첫째, TDD와 BDD는 그 자체로서도 매우 좋은 개발 방법이지만 개발 방법론에서 빠져서는 안 될 리팩토링과 설계라는 주요 요소와도 매우 밀접한 연관이 있다는 점.
둘째, 고객의 요구사항을 반영하는 구현체에 대한 다양한 시선(Bottom Up/Top Down, white/black box 등)으로 분석하고 구현한다는 점.
셋째, 단순히 몇 시간만의 학습으로 그 진가를 발휘할 수 없고 학습과 더불어 자신의 프로젝트에 녹아 들어가야 진정한 효과를 발휘한다는 점.

이와 같이 TDD와 BDD의 주변에는 더불어 발전할 수 있는 덕목들이 산재해 있다. 이렇게 산재된 덕목들은 결국 다른 주요 SW 개발 방법론을 학습하는 데 필요한 시야를 확보할 수 있다는 것을 의미한다. 따라서 TDD와 BDD는 지금은 먹기 싫은 쓴 약이지만 결국은 먹고 나면 자신의 개발 건강을 튼튼히 하는 보약과 같은 존재라고 이야기하고 싶다.











출처 : 마이크로소프트웨어 5월호

제공 : DB포탈사이트 DBguide.net