블로그 이미지
소프트웨어 개발 경험을 공유하고 싶은 재밌게 사는 소프트웨어 엔지니어입니다^^

카테고리

Chungha Story (45)
Agile Experience (22)
My Family (4)
Life Style (8)
Programming (8)
Android (2)
Total167,033
Today1
Yesterday1
낡은 코드(legacy code)에 관심을 갖다

저는 최근에 낡은 코드에 관심을 갖게 되었습니다. 낡은 코드에서 효과적으로 일하기 위해서는 테스트를 만드는 것이 가장 먼저 해야 할 일입니다. 테스트가 없다면 어떤 코드도 쉽게 수정하기 어려울 것입니다. 그런데 막상 테스트를 만들려고 해보니 막막했습니다. 어디서 부터 테스트 해야 할까?

낡은 코드는 큰 것부터 테스트 하자

처음에는 막막했습니다. 뭐부터 해야할 지.. 함수단위, 라이브러리단위로 테스트 하자니 그 양이 엄청 났습니다. 또한 테스트 하기 쉽게 작성된 코드들이 아니기 때문에 더욱 어려웠습니다. 저는 블로그를 통해서 조언을 얻었습니다.(ParkPD님, 박형근님 감사합니다.) 그 첫번째가 "큰 것 부터 하라"였습니다. 그래서 먼저시스템 테스트를 만들기로 하였습니다.
다른 팀원들이 테스트를 쉽게 만들수 있게 도와주자

저는 낡은 시스템의 테스트 케이스를 검토하고 아래 그림과 같은 상상을 했습니다. 저희 회사 제품의 시스템 테스트 케이스는 시간적인 순서를 갖는 제어 메시지를 주고 받는 형태로 작성 될 수 있습니다. 저는 이것을 시나리오라고 하고 제어 메시지에 내용은 제어정보라고 정의하였습니다. 제어정보와 시나리오를 처리하는 것은 mock 클라이언트라고 정의하였습니다.

내가 상상한 시스템 테스트

개발자 혹은 테스터는 간단한 스크립트언어로 시나리오와 제어정보를 작성합니다. 독립적으로 작성된 시나리오와 제어정보를 조합하여 많은 테스트 케이스를 만들수 있습니다. 이런 것을 팀원들에게 제공한다면 테스트 케이스 작성에 큰 도움이 될 것 같았습니다. (저희 팀에는 유지보수 중인 낡은 시스템이 많습니다.^^;;)

의존성 주입 (Dependency Injection)

의존성 주입이란 객체간의 의존성을 미리 코드에 정의하는 것이 아니라 실행 시간에 주입하는 것을 의미합니다. 의존성 주입은 제어 역전(Inversion of Control)이라고도 합니다. 즉, 제어를 역전시킴으로써 바이너리를 교체하지 않고 제어를 가능하게 하는 것입니다. 이 개념은 자바의 Spring이라는 프레임워크에서 핵심 기능으로 사용되고 있습니다.
C++에서 의존성 주입을 하기 위해서는 작성된 라이브러리의 API를 동적으로 호출하고 파라미터를 실행시간에 입력하는 코드(wrapper code)를 생성해야 합니다. 즉, 이 코드(wrapper code)는 텍스트 파일에 기술된 100이라는 문자열을 정수형 변수에 넣을 수 있어야 합니다.

시스템 테스트 프레임워크

저는 절친 팀원 한분과 시스템 테스트를 위한 mock 클라이언트 작성을 도와주는 프레임워크를 만들기로 하였습니다. 물론 팀장님이 인가해주셨습니다. (매출과 관계없는 일을 싫어하는 회사 특성상 이례적인 경우일수 있습니다.) 위의 그림을 바탕으로 여러가지 시도를 해봤습니다.
  • 시도1) 시나리오 및 제어정보 스크립트 인터프리터 + 의존성 주입용 코드 생성기 + 스크립트 생성기
    시나리오와 제어정보 스크립트 언어를 정의하고 Lex와 Yacc를 이용하여 인터프리터를 만들었습니다. 의존성 주입을 위해 gccxml을 이용하여 API를 XML로 만들고 그것을 분석하여 코드 생성기를 만들었습니다. 스크립트 작성을 용이하게 할수 있도록 도와주는 스크립트 생성기도 작성하였습니다.
    문제는 의존성 주입용 코드 생성기였습니다. 생각보다 예외 상황이 많았습니다. 그리고 우리가 정의한 스크립트 언어는 점점 복잡해졌습니다. 패스~

  • 시도2) Python + Swig
    반성합니다. 시도1을 할때 충분히 조사하지 못했습니다.
    Swig는 Python과 C++을 연결시켜주는 코드 생성 라이브러리입니다. 매우 훌륭합니다.
    그러나, Python에서 API를 호출하는 것은 쉬웠지만 거꾸로 이벤트를 알려줄때 C++의 데이터가 Python 데이터로 맵핑 되는 작업이 매우 난해했습니다. 패스~

  • 시도3) 시나리오 작성용 인터페이스 및 시나리오 실행 환경 + 의존성 주입 라이브러리
    욕심을 버렸습니다. 시나리오는 C++로 작성하기로 하였습니다. 시나리오간에 의존성을 줄이고 이벤트를 알려줄 수 있는 인터페이스를 정의하였습니다. 해당 인터페이스를 구현하여(shared object or DLL) 시스템 프레임워크 월드에 주입하면 자동으로 해당 시나리오가 실행되도록 하였습니다. 제어정보와 시나리오 정보는 의존성 주입 라이브러리(Autumn Framework)를 사용하였습니다. 전체적으로 매우 심플하고 다양한 시나리오(기능 시험, 성능시험 등)를 XML로 정의할 수 있게 되었습니다. 콜!

    "시도3"의 전체 구성 그림

현재 저희는 시나리오 인터페이스를 구현하여 여러가지 테스트 케이스를 쌓고 있습니다^---^ 저희가 사용하는 인터페이스는 아래와 같습니다. 매우 간단합니다. 이 인터페이스를 구현하고 XML을 정의하면 자동으로 테스트 됩니다.
이제 남은 숙제는 지속적 통합을 위한 서버 구축(CI)입니다. C++과 리눅스 환경에서 효과적인 CI 서버 구축을 위한 방법을 알고 계신분은 조언부탁드립니다.^^ 감사합니다.
Posted by 윤청하

댓글을 달아 주세요

  1. 2010.01.06 17:50 신고 윤청하  댓글주소  수정/삭제  댓글쓰기

    [xper에서 주넥님께 받은 피드백입니다.]
    C++, Make 환경에서 CIServer구축의 경우,
    OpenSource인 CruiseControl, ANT를 이용해서 구축한 경험이 있습니다.
    CruiseControl, ANT 이외에 Web. dashboard를 위해서 JDK, Tomcat을 추가 설치하였구요.
    ANT build.xml script에서 make target을 호출해주는 식으로 엮었구요.
    자세한 것은 아래 참고하시면 됩니다.
    http://cruisecontrol.sourceforge.net/gettingstarted.html
    관련되어 도움이 되었던 서적은
    "지속적인 통합 : 소프트웨어 품질을 높이고 위험을 줄이기" 폴 M 듀발 저, 최재훈 역, 위키 북스 출판사.
    였습니다.

  2. 2010.01.06 17:50 신고 윤청하  댓글주소  수정/삭제  댓글쓰기

    [xper에서 김영현님께 받은 피드백입니다.]
    저는 SWIG(http://www.swig.org)을 사용해 기존의 레거시 코드(C++로 작성된)의 인터페이스를 LUA에 제공해 LUA를 사용해 테스트 코드를 만들었습니다. 유닛 테스트는 아니지만, 그래도 없는 것 보다는 낫다는게 그때 생각이었습니다. ^^;;

  3. 2010.01.06 17:51 신고 윤청하  댓글주소  수정/삭제  댓글쓰기

    [xper에서 강석천님께 받은 피드백입니다.]
    리팩토링을 진행하고 계신 과정이라면, 해당 어플리케이션에 테스팅을 위한 커맨드라인 인터페이스를 추가하는 것도 추천해봅니다.
    (혹은 로깅..) 그리고 커맨드 입력 대비 아웃풋 결과에 대해 이미 아는 정보를 텍스트 화일로 유지하였다가 이를 비교하는 방법
    이 있습니다. 어느정도 발전이 되면 스크립팅 & 텍스트 비교에 대한 자동화가 가능합니다.
    하지만 이는 깨지기 쉬운 테스트케이스 (단순 텍스트 비교이므로)이므로, 길게 진행하기엔 그리 좋진 않습니다.

  4. 2010.01.06 17:53 신고 윤청하  댓글주소  수정/삭제  댓글쓰기

    [xper에서 송홍진님께 받은 피드백입니다.]
    전 크루즈컨트롤 보다는 허드슨을 추천해 드립니다.
    http://minddiary.com/2008/05/10/comparing-two-continous-build-tools-cruise-control-and-hudson/