출처: http://freepix.eu/animals/seagull-flying-in-the-sky/


2002년 월드컵이 개최되던 해였다. 


내가 처음으로 사회 생활을 시작했던 때가 말이다.

고향에 있는 대학에서 석사를 마치고 지도교수님의 지도교수님이 창업한 회사에 입사하기로 했다가 약속을 깨고 도망치듯 그렇게 서울로 올라왔다. 먼저 가기로 했던 회사는 대전에 있었는데  그 때 당시 여친이 대전은 너무 멀다며 그나마 가까운 서울로 가라고 성화를 했던게 주된 이유였다. 

시덥지 않은 우여곡절을 겪은 끝에 내가 선택한 회사는 나보다 먼저 졸업해서 회사를 다니던 여후배의 소개로 알게된 곳이었다. 신생 벤처였던 회사는 서울벤처인큐베이터에서 인큐베이팅중이었고 직원이 20명이 되지 않았다. 먼저 가려던 대전 회사의 대안을 급히 찾아야 했던 나에게는 별다른 선택의 여지가 없었기도 했고,  처음에는 몇 달 다니다 마음에 들지 않으면 그만 둘 생각이었기 때문에 별다른 고민 없이 입사를 결정했다.

그 후로 오랫동안 이어진 인연의 시작이었다.

입사전에 먼저 있던 여자후배한테 직원들 실력이 보통이 아니라는 사실을 듣긴 했지만 학부, 대학원시절 한 프로그래밍 한다는 소리를 많이 들었던 나는 사실  후배의 말을 대수롭지 여겼다. 하지만 막상 회사에 들어와 몇 달을 겪으면서 내 생각이 정말 바보같았음을 절실히 느낄 수 밖에 없었다. 몇 명 안되던 회사 동료들이었지만 실력은 하나같이 녹녹치 않았다. 게다가 내가 생던 보도 못한 기술들을 사용하기까지 했다. 

내가 정말 우물안에 개구리였다는 사실을 절실히 깨닫는 순간이었다.  

완전 쫄았던 나는 처음에는 어떻게든 동료들을 따라잡고자 하는 마음으로 회사를 다녔다. 하지만 시간이 좀 지나자 사람들이 좋았고 회사가 좋았다. 끊임없이 지적 자극을 주던 선배들이 좋았고 가끔 회식하고 차라도 끊길라 치면 없는 살림에도 오늘 택시비는 내가 쏜다던 이사님이 좋았다. 회사가 커가면서 하나 둘 생기던  동료 후배들이 좋았고, 그런 선후배 동료들과 동고동락하며 함께하던 프로젝트가 좋았다. 

너무나 멋진 회사에서 너무나 좋은 사람들과 함께한 시간들이 그렇게 흘러갔다. 그 시간 동안 난 결혼을 했고 두 아이의 아빠가 되었으며 이제 그 아이들이 둘 다 초등학생이 되었다. 

그리고 2014년 8월...언젠가 한 번은 올 수 밖에 없던 순간이 찾아 왔다.
잘한 결정인지 아닌지는 시간이 지나봐야 알 수 있겠고, 어떤 결과든 응당 내가 받아야 하는 일이니 후회는 없다.
다만, 좋은 결과로 다시 만날 수 있도록 최선을 다할 뿐이다.

다행인건 내가 그동안 세월을 헛되이 보내지 않았던 모양인지 임원분들과 동료들에게 퇴직 의사를 말씀드렸을 때 정말 많은 분들이 많은 좋은 이야기를 해주셨고, 아쉬워 해주시고, 고마워 해주셔서 너무나 감사했다. 덕분에 가벼운 마음으로 떠날 수 있어 좋다.

나와 내 첫직장이었던 넥스트리소프트 동료들에게 ‘포스'가 함께하길 빈다. :-)

 May the Force be with you!
 

  


저작자 표시 비영리 변경 금지
신고

'Life' 카테고리의 다른 글

넥스트리를 떠나며...  (0) 2014.08.29
떠난 보낸 코드를 바라보는 마음...  (0) 2013.09.25
8월에 읽은 책  (0) 2013.09.10
독서의 적  (1) 2013.08.06
13회 JCO 참석 회고  (0) 2013.02.25
이름 틀린 청첩장  (0) 2011.07.29
by 김영진 김영진 _ 2014.08.29 17:05

자바 기반으로 애플리케이션을 작성 할 때 좋은 점은 셀 수 없을 정도의 다양한 프레임워크, 라이브러리, 도구등의 도움을 받을 수 있다는 점입니다. 현시점을 기준으로 다른 어떤 개발 언어도 자바만큼 많은 지원 도구들을 가지고 있지 않을 겁니다. 사실 너무 많아서 골라 쓰기도 쉽지 않을 정도죠… 그렇게 많은 프레임워크 중에서도 제가 가장 좋아하는 프레임워크가 있는데 바로 jUnit 테스트 프레임워크입니다. 존경해 마지 않는 켄트 백 횽님, 에릭 감마 횽님(두 분 모두 61년생)의 역작이면서 제가 본 가장 직관적이고 우아한 프레임워크 중에 하나 이기도 합니다. 게다가 배우기도 쉬어서 jUnit만 놓고 본다면 배워서 사용하는 데 하루도 걸리지 않을 겁니다.


하지만 보통 아무리 좋은 프레임워크라도 그 자체만으로 좋은 설계나 코딩을 보장해 주지는 않습니다. 프레임워크를 도입한다고 저절로 좋은 품질의 애플리케이션이 만들어지지 않는 다는 이야기입니다. 프레임워크가 해결 하려는 문제를 잘 이해하고 사용하는 것이 중요하기 때문입니다. 그런 이해 없이 남들이 좋다고 해서 사용한다면 제대로 사용하기도 어려울 뿐더러 설령 좋아지는 부분이 있더라고 그 범위가 제한적일 확률이 큽니다.


jUnit도 마찬가지 입니다. 프레임워크가 제공하는 기능은 단순하고 사용하기도 쉽습니다만, 정말 제대로 사용하려면 단위 테스트, TDD(Test Driven Development), CI(Continuous Integration)를 비롯한 다양한 테스트에 대한 개념에서부터 올바른 단위 테스트 작성법까지 정말 많은 것을 알아야 합니다.  


근데 위에 나열한 개념을 다 모른다고 할지라도 jUnit이 주는 특별한 장점이라면 일단 jUnit을 이용하여 단위 테스트를 작성을 시작하는 것 만으로도 어느 정도 품질 향상을 기대할 수 있다는 점입니다. 단위 테스트가 전혀 존재하지 않는 코드와 단 하나라도 테스트가 존재하는 코드는 큰 차이가 있기 때문입니다. 테스트를 작성함으로써 코드에서 이상한 부분을 찾아내고 고치는 주기가 빨라지기도 하고 하나 하나씩 쌓여나간 테스트 코드가 축적되면 나중에 기능을 추가하거나 코드를 리팩토링 하는 데도 아주 많은 도움을 받을 수 있기도 합니다.


제 경우에는 2004년부터 jUnit을 이용해 단위 테스트를 만들고 있으니 꽤 오랫동안 썼다고 할 수 있습니다. 단위 테스트를 작성하는 습관이 든 후로는 단위 테스트를 작성하지 않으면 왠지 불안하기까지 합니다. 하지만 단위 테스트를 열심히 작성하면서부터 자연스레 몇 가지 고민 거리가 생기기도 했습니다.


그 중 하나가 단위 테스트 코드의 유지 보수였습니다. 열심히 작성한 테스트코드였지만 시간이 흘러서 보면 너무 난해한 경우가 많았거든요. 이해하기도 어렵고 고치기도 어려운 테스트 코드… 여러분들이라면 어떻게 하시겠습니까? 가장 쉬우면서도 어리석은 방법은 삭제입니다. 기존 로직에 무언가 새로운 기능을 추가합니다. 테스트를 돌려 봅니다. 테스트 코드가 깨집니다. 여러 테스트 케이스 중에 유독 한 테스트 케이스만 깨집니다. 근데 테스트 코드를 도통 이해하지 못하겠고 시간도 별로 없습니다. 잠시 고민합니다. ‘어쩌지…’


테스트 코드를 삭제하면 금방은 모든 게 편해집니다. 삭제한 테스트 코드를 제외한 모든 테스트 코드가 잘 돌아가니까요. 하지만 그러면서 빚이 쌓입니다. 그리고 그 빚은 며칠 혹은 몇 주 내로 이자까지 합쳐진 채로 내게 돌아오곤 했습니다.


그렇게 테스트 코드를 어떻게 하면 잘 작성할 수 있을까 고민하다가 한 권의 책을 만납니다. ‘제라드 메스자로스’께서 쓰신 xUnit Test Patterns이라는 책이었습니다. 보신 분들은 아시겠지만 엄청난 두께를 자랑하는 책입니다. 게다가 번역서가 나오기 한참 전이라… 잠시 고민하다가 ‘마틴 파울러’ 도장이 찍혀 있는 책은 읽어서 손해 볼게 없다는 생각에 원서로 읽기 시작합니다. 근데 막상 읽어보니 너무 재미있어서 정말 한 장도 빼지 않고 모두 읽었습니다. 그리고 그전까지 고민하던 많은 부분들이 이 책을 읽으며 해소 됐습니다.


 


xUnit Test Pattern (출처 : 아마존)


이 책을 통해 읽기 쉬운 테스트 코드를 작성하는 법에 대해 배웠고 관리 용이한 테스트 픽스처를 만드는 다양한 패턴에 대해 알게 됐습니다. 이 책을 읽기 전과 후에 테스트 코드가 정말 많이 달라졌다고 할 만큼 많은 걸 배우게 된 책입니다. 아직 안 읽어 보신 분께는 꼬~옥 “강추”합니다. 지금은 번역서도 나왔습니다. 

그렇게 시간이 흘렀고 2012년부터 일년 반 동안 네비게이션 관련 프로젝트를 하게 됩니다. 그런데 이 프로젝트에서 테스트 코드를 작성하면서 또 다시 고민거리가 생깁니다. 대상 시스템이 네비게이션 시스템이다 보니 테스트 픽스처를 대부분 복잡한 그래프 형태로 구성해야 했는데 이게 쉽지 않았습니다.


 

그래프 (출처 : 위키피디아)


이런걸 그래프라고 합니다. 자세한 내용은 위키피디아 참조


그래프를 구성하는 각 요소(Link, Node…)가 다음 그림과 같이 컴포지트 구조로 구성되어 있어 픽스처 작성시  최대 3~4단계 이상의 계층을 구성해야 하는 경우도 많았습니다. 


 


결국, 픽스처 작성시 보통 이런 식의 코드들이 많이 나타나게 됩니다.


Path p = new Path();

p.setInOutWeight(100);

p.setId(1);

p.setTurnControlType(1);

p.setTurnType(1);

DirectionLink d1 = new DirectionLink();

p.setNext(d1);

DirectionLink d2 = new DirectionLink();

p.setCurrent(d2);


Link link = new Link();

d1.setLink(link);

d2.setLink(link);


구조를 강조하기 위해 세부사항은 대부분 생략하고 단순히 한 단계의 코드만 작성했는데도 객체 생성 구조가 복잡함을 알 수 있습니다. 여기서 객체 생성의 복잡도를 낮추기 위해 각 객체 생성 부분을 다음과 같이 Creation 메소드로 분리했습니다.


Path p = createPath(1,100);

Link l1 = createLink(...);

l1.setPositive(createDirectionLink(...));

l1.setNegative(createDirectionLink(...))

p.setCurrent(l1.getPostive());

p.setNext(l1.getNegatvie());


하지만 그래도 여전히 그래프 구조를 엮는 코드들이 그대로 남았습니다. 마음에 들지 않았습니다. 게다가 더 큰 문제는 테스트 케이스마다 필요한 Creation메서드의 파라미터 유형이 달라지는 경우가 많았다는 것입니다. 

예를 들어 Link라는 객체가 15개의 속성을 갖고 있는데 테스트 케이스마다 테스트하려는 속성의 조합이 조금 달라지는 경우 입니다. 조합이 몇 가지 없을 경우에는 조합의 수 만큼 creation 메서드를 만들고 재활용하면 되겠지만 우리 경우에는 조합의 수가 너무 많아 적합하지 않았습니다. 경우에 따라서 파리미터 조합의 개수가 유동적으로 변했기 때문에 대응하는 creation 메서드를 모두 만드는 건 원래의 의도와는 다르게 결국 복잡도를 더 높이는 결

과를 초리했습니다.



createPathByA(…)

createPathByB(…)

createPathByC(…)

createPathByD(…)

createPathByE(…)


수 많은 CreationMethod들 중에 입맛에 맞는 메서드를 고르는 것부터 해서 파라미터가 하나만 달라져도 기존 Creation 메서드를 사용할 수 없어 새롭게 만들어야 하는 문제까지... 게다가 주요 도메인 객체에 속성값 명이 변경되기라도 하면 고쳐야 할 코드가 엄청나게 많아지기까지 했습니다.


‘아무리 봐도 이건 좀 아닌데…’ 라는 생각을 할 무렵 또 한 권의 책을 만납니다. 바로 이 책 Growing Object-Oriented Software, Guided by Tests(이하: GOOSGT로 표기)입니다. 이건 켄트 백님 도장이 찍혀 있네요. 그리고 저자인 넷 프라이스는 jMock 프레임워크를 만든 분이기도 합니다. 

 


Growing Object-Oriented Software Guided by Tests (출처: 아마존)


회사 동료 분 책상에 방치되어 있는 이 책을 우연히 보고 허락도 없이 가져다가 읽었는데 “이런 훌륭한 책을 이제서야 읽다니!!!” 하는 생각이 들 정도로 좋았습니다.  


회사 동료 분 책상에 방치되어 있는 이 책을 우연히 보고 허락도 없이 가져다가 읽었는데 “이런  책을 이제서야 읽다니!!!” 라는 생각이…ㄷ ㄷㄷ 아직 번역서도 없어  출판사에 연락을 했더니 다른 분께서 이미 번역 중.. OTZ 그래서 리뷰라도 하겠다고 자청해서 근 6개월에 걸쳐 리뷰를 했드랬죠. 리뷰는 이 때 처음 해봤는데…리뷰하는 것도 쉬운 일은 아니더군요. 그 때 약속한 리뷰 일정을 못 지켜서 담당자 분께 무척 죄송했다능…근데 리뷰를 다 해드리고 나서도 거의 1년이 지나서 책이 나왔습니다. 오래 기간이 걸린 책 출간으로 보면서 책 한 권 출판하는게 참 쉬운 일은 아니구나 하는 생각이 들더군요. 이 책의 한글판은 “테스트 주도 개발로 배우는 객체 지향 설계와 실천”입니다. 이런 책들은 유행을 타지 않는 책이기에 꼭 읽어 보시길 추천합니다.

.

아무튼 이 책에서 고민하던 문제의 해결책을 발견합니다. 바로 테스트 데이터 빌더 (Test Data Builder)이었습니다. 테스트 데이터 빌더는 디자인 패턴에 나오는 빌더 패턴의 일종이라고 보면 됩니다. 위키를 찾아보면 빌더 패턴에 대해 다음과 같이 설명하는데요.



The intent of the Builder design pattern is to separate the construction of a complex object from its representation. By doing so, the same construction process can create different representations.  


복잡한 객체의 생성법과 표현법을 분리해서 똑같은 생성 절차를 통해 다른 표현이 만들어질 수 있게 한다



GOOSGT 책에서도 복잡한 테스트 데이터를 만들 때 효과적인 패턴으로 테스트 데이터 빌더를 이야기 하고 있습니다. 복잡한 픽스처 생성 과정을 테스트 데이터 빌더를 통해서 읽기 쉽게 만들 수 있다는 점과 테스트 코드와 복잡한 도메인 객체 생성 로직간의 커플링을 막을 수 있다는 점, 그리고 Case별로 Creation메소드를 만들지 않고도 딱 원하는 크기의 픽스처를 만들어 낼 수 있다는 점에서 테스트 데이터 빌더는 정확히 우리가 찾고 있던 솔루션이었습니다.


데이트 데이트 빌더의 구조는 비교적 간단한데요. 만약 Order라는 테스트 대상 객체에 대한 픽스처를 만들어야 한다면 OrderBuilder라는 테스트 데이터 빌더 객체를 만들면 됩니다. (GOOSGT책 내용 인용)


public class OrderBuilder {

    private Customer customer = new CustomerBuilder().build();

    private List<OrderLine> lines = new ArrayList<OrderLine>();

    private BigDecimal discountRate = BigDecimal.ZERO;


    public static OrderBuilder anOrder() {

        return new OrderBuilder();

    }

    public OrderBuilder withCustomer(Customer customer) {

        this.customer = customer;

        return this;

    }

    public OrderBuilder withOrderLines(OrderLines lines) {

        this.lines = lines;

        return this;

    }

    public OrderBuilder withDiscount(BigDecimal discountRate) {

        this.discountRate = discountRate;

        return this;

    }

    public Order build() {

        Order order = new Order(customer);

        for (OrderLine line : lines) order.addLine(line);

            order.setDiscountRate(discountRate);

        }

    }

}


그리고 사용은 이런식으로 할 수 있습니다. 원하는 테스트 객체를 만들기 위해 최후에 build() 메서드 호출합니다.

Order order = new OrderBuilder().build();


여기서 눈여겨 보아야 할 것은 빌더 객체가 제공하는 모든 공용 설정 메서드들이 모두 자신의 객체 타입(여기서는 OrderBuilder)을 리턴한다는 것입니다. 이를 통해 빌더 내의 메서드를 “.”지시자를 통해 연속적으로 연결 가능하도록 만들 수 있습니다. 이렇게 하면 객체 생성 로직 작성이 한결 유연해 지고 해당 객체가 어떤 속성을 갖고 생성되는지 명확히 알 수 있습니다.






결과적으로는 계층 구조에 맞춰 적절한 테스트 빌더를 만들고 빌더를 조합함으로써 다음처럼 계층적인 테스트 데이터 구조를 쉽게 만들 수 있게 됩니다. 


new OrderBuilder()

    .withCustomer(

        new CustomerBuilder()

            .withAddress(new AddressBuilder().withNoPostcode().build())

            .build())

    .build();


테스트 빌더를 이용하면 내용이 조금씩 달라지는 다수의 비슷한 테스트 객체를 쉽게 만들어 내는 것도 무척 쉽습니다. 다음의 예처럼 말이죠. 


Order orderWithSmallDiscount = new OrderBuilder()

    .withLine("Deerstalker Hat", 1)

    .withLine("Tweed Cape", 1)

    .withDiscount(0.10)

    .build();


Order orderWithLargeDiscount = new OrderBuilder()

    .withLine("Deerstalker Hat", 1)

    .withLine("Tweed Cape", 1)

    .withDiscount(0.25)

    .build();


마지막으로 빌더를 생성하는 부분을 Factory 메서드로 만들면 우리가 관심 없는 테스트 빌더 객체 생성 로직을 숨기고, 테스트 데이터 생성 부분을 더욱 강조할 수 있습니다.


Order order =

    anOrder().withCustomer(

        aCustomer().withAddress(anAddress().withNoPostcode())).build();


다음은 실제 우리가 테스트 데이터 빌더 패턴을 테스트케이스에서 사용한 코드의 일부 입니다. 

 


코드를 통해 무슨 용도인지는 정확히 알 수 없지만 테스트를 위해 LinkComposite이라는 객체를 만든다는 사실과 이 객체가 목적지와 터미널종류, 안내 이지미 해상도 속성을 갖으며 두 개의 링크로 구성된 LinkContainer를 갖는 다는 사실을 알 수 있습니다.

위와 같이 테스트 데이터 빌더 패턴을 도입한 후로 복잡한 테스트 픽스처를 만드는 작업이 한결 쉬워졌으며, 도메인 객체 와 테스트코드가 분리됨으로써 변경에 따른 테스트 수정 여파도 최소화할 수 있었습니다. 또, 테스트 코드를 읽는 것도 한결 편해졌습니다. 더불어 한동안의 고민거리도 해결되었습니다. 


지금까지 본 바와 같이 여러 단계의 계층구조를 갖는 복잡한 픽스처를 만들어야 한다면 테스트 데이터 빌더를 도입하는 것이 무척 효과적일 수 있습니다. 하지만 테스트 데이터 빌더 패턴을 아무때나 무조건 사용하는 것은 바람직하지 않습니다. 빌더 패턴을 도입하려면 테스트대상 클래스에 대해 각각 빌더를 만들어야 하기 때문에 대상 클래스가 많을 경우에는 빌더를 작성하는데 오버헤드가 있으며, 작성된 빌더 코드에 대한 유지보수 또한 분명히 필요합니다. 따라서, 픽스처 생성 과정이 복잡하지 않고 Creation 메서드 정도로 충분하다면 굳이 이 패턴을 도입할 이유가 없습니다. 앞 부분에도 이야기 했지만 이 패턴이 필요한 정확한 상황을 이해하고 사용하는 것이 중요합니다. 



저작자 표시 비영리 변경 금지
신고
by 김영진 김영진 _ 2014.01.13 17:26

Do you know scale - 마셜 듀크박사와 로빈 피뷔시박사에 의해서 개발된 척도이며 가족사에 관한 20개의 질문으로 구성된다. 이 척도에서 높은 점수를 받은 아이들은 더 높은 수준의 자긍심, 상황통제력을 갖으며 가족 기능을 더 잘 수행하며 낮은 수준의 불안과 행동장애를 보인다고 한다. 또, 교육적 문제나 감정/행동 장애에 직면했을 때 더 잘 극복한다고 한다.

1.부모님이 어떻게 만났는지 알고 있나요? (예/아니오)
2.엄마가 자란 곳은 어디인지 알고 있나요? (예/아니오)
3.아버지가 자란 곳은 어디인지 알고 있나요? (예/아니오)
4.(외)조부모님이 자란 곳은 어디인지 알고 있나요? (예/아니오)
5.(외)조부모님이 만난 곳은 어디인지 알고 있나요? (예/아니오)
6.부모님은 어디에서 결혼을 하셨는지 알고 있나요? (예/아니오)
7.여러분이 태어날때 무슨일이 있었는지 알고 있나요? (예/아니오)
8.여러분의 이름을 왜 그렇게 지었는지 알고 있나요? (예/아니오)
9.형(남동생)이나 누나(여동생)이 태어날 때 있었던 일을 알고 있나요? (예/아니오)
10. 가족 중에 여러분과 외모가 가장 닮은 사람은 누구인지 알고 있나요? (예/아니오)
11. 가족 중에 여러분과 행동이 가장 닮은 사람이 누구인지 알고 있나요? (예/아니오)
12. 부모님이 어렸을 적에 겪었던 질병이나 부상에 대해 알고 있나요? (예/아니오)
13.좋은(혹은 나쁜) 경험으로부터 부모님이 배운 교훈에 대해 알고 있는게 있나요? (예/아니오)
14.부모님이 학창 시절 때 겪었던 일 중에 알고 있는 것이 있나요? (예/아니오)
15.가계에 역사(족보, 시조)에 대해 알고 있나요? (예/아니오)
16.부모님이 젊었을 때 가졌던 직업에 대한 알고 있는게 있나요? (예/아니오)
17.부모님이 젋었을 때 받았던 받은 상에 대해 알고 있는게 있나요? (예/아니오)
18.엄마가 다녔던 학교 이름을 알고 있나요? (예/아니오)
19.아버지가 다녔던 학교 이름을 알고 있나요? (예/아니오)
20.충분히 웃지 않아 성격 나쁜 얼굴로 "굳어진" 친척에 관해 알고 있나요? (예/아니오)

하지만 인과관계에 대한 착각은 금물이다. 높은 점수를 받으면 그럴 확율이 높다는 것이지 높은 점수를 받기 위해 가족사를 달달 외운다고 그런 능력이 생기는 건 아니라는 얘기… 


평소에 아이들에게 내 어릴적 이야기나 부모님께 들은 이야기같은 가족사들에  들려주는 것이 좋을 듯 하다. 이야기를 들려주는 과정에서 일어나는 정서적인 교감이 아이들을 더 강하게 만드는 걸 테니 말이다.

더 자세한 내용은 --> 요기 참고

저작자 표시 비영리 변경 금지
신고
by 김영진 김영진 _ 2013.09.27 17:42

티스토리 툴바