Upload
tedypicker
View
882
Download
4
Embed Size (px)
Citation preview
레거시 코드 활용 전략
4 장 봉합 모델5 장 레거시 코드를 위한 도구
아꿈사 http://cafe.naver.com/architect1
최유림http://blog.naver.com/sikyungelove
천공 카드 프로그래밍
• 데이터를 표현하기 위해 규칙에 따라 구멍을 뚫어 사용하는 종이 카드로서 초기의 저장매체
• 천공 위치에 구멍을 뚫거나 뚫지 않음으로서 하나의 비트를 나타날 수 있음
http://ko.wikipedia.org/wiki/%EC%B2%9C%EA%B3%B5_%EC%B9%B4%EB%93%9C
봉합모델레거시 코드를 위한 도구정리가 안되지만 맞는 얘기들
봉합모델레거시 코드를 위한 도구정리가 안되지만 맞는 얘기들
봉합 (Seams)
• 프로그램 안에서 동작을 변화시킬 수 있는 위치
• 동작을 변화시키기 위해 코드를 편집할 필요는 없음
bool CAsyncSslRec::Init(){
if(m_bSslInitialized){return true;
}m_smutex.Unlock();m_nSslRefCount++;
m_bSslInitialized = true;
FreeLibrary(m_hSslDll1);m_hSslDll1 = 0;FreeLibrary(m_hSslDll2);m_hSslDll2 = 0;
if(!m_bFailureSent){m_bFailureSent = true;PostReceiveError(SOCKETCALLBACK, SSL_FAILURE);
}
CreateLibrary(m_hSslDll1, "syncesel1.dll");CreateLibrary(m_hSslDll2, "syncesel2.dll");
m_hSslDll2->Init();m_hSslDll2->Init();
return true;}
•프로그램 안에서 동작을 변화시킬 수 있는 위치•동작을 변화시키기 위해 코드를 편집할 필요는 없음
bool CAsyncSslRec::Init(){
if(m_bSslInitialized){return true;
}m_smutex.Unlock();m_nSslRefCount++;
m_bSslInitialized = true;
FreeLibrary(m_hSslDll1);m_hSslDll1 = 0;FreeLibrary(m_hSslDll2);m_hSslDll2 = 0;
if(!m_bFailureSent){m_bFailureSent = true;PostReceiveError(SOCKETCALLBACK, SSL_FAILURE);
}
CreateLibrary(m_hSslDll1, "syncesel1.dll");CreateLibrary(m_hSslDll2, "syncesel2.dll");
m_hSslDll2->Init();m_hSslDll2->Init();
return true;}
동작을 변화시키지만코드는 편집하지 않도록 함-> 코드와 테스트 분리 가능
어떻게 하면동작을 변화시키지만코드는 편집하지 않도록 할까 ?
봉합 (Seams) 의 종류
• 프로그래밍 언어에 따라 서로 다름
• 프로그램 텍스트가 시스템 코드로 변형되는 시점에 따라 다름
• 컴파일 단계 : 전처리 봉합
• 링크 단계 : 연결 봉합
• 실행 단계 : 객체 봉합
전처리 봉합
• C, C++ 은 컴파일 하기 전에 매크로 전처리가 실행됨
• 전처리란 컴파일 전에 처리하는 매크로
• # 로 시작 ex. #include <stdio.h>
• 전처리기를 통하여 동작을 변화시키지만 코드는 편집하지 않도록 정의
• 많은 봉합의 기회 제공
• 전처리기 사용시 코드의 명료성이 떨어짐
• #define 으로 정의되는 매크로는 단순히 텍스트 치환만 수행
• 불명확한 버그를 숨기기 위한 매크로를 만들기 쉬움
bool CAsyncSslRec::Init(){
if(m_bSslInitialized){return true;
}m_smutex.Unlock();m_nSslRefCount++;
m_bSslInitialized = true;
FreeLibrary(m_hSslDll1);m_hSslDll1 = 0;FreeLibrary(m_hSslDll2);m_hSslDll2 = 0;
if(!m_bFailureSent){m_bFailureSent = true;PostReceiveError(SOCKETCALLBACK, SSL_FAILURE);
}
CreateLibrary(m_hSslDll1, "syncesel1.dll");CreateLibrary(m_hSslDll2, "syncesel2.dll");
m_hSslDll2->Init();m_hSslDll2->Init();
return true;}
#include 문을 코드에 추가-> PostReceiveError 매크로 정의-> 테스트용 전처리기 사용-> 전처리 봉합
연결 봉합
• 링커는 컴파일러를 통해 만들어진 여러 Object File
들을 하나의 실행파일로 만듦
• C, C++ 은 독립된 링커 존재
• 자바는 링크 작업이 보이지 않음
• 연결 봉합을 이용하는 가장 쉬운 방법은 바꾸고자하는 클래스나 함수를 위해서 별도의 라이브러리를 만드는 것
• 테스트 해야 할 때 클래스나 함수 제작에 연결하지 않고 , 새로 생성된 클래스나 함수에 연결되도록 빌드 스크립트를 변경하면 동작을 변화시키지만 코드는 편집하지 않음
• 제 3 의 라이브러리를 호출하는 코드 베이스를 가진 경우 유용함
bool CAsyncSslRec::Init(){
if(m_bSslInitialized){return true;
}m_smutex.Unlock();m_nSslRefCount++;
m_bSslInitialized = true;
FreeLibrary(m_hSslDll1);m_hSslDll1 = 0;FreeLibrary(m_hSslDll2);m_hSslDll2 = 0;
if(!m_bFailureSent){m_bFailureSent = true;PostReceiveError(SOCKETCALLBACK, SSL_FAILURE);
}
CreateLibrary(m_hSslDll1, "syncesel1.dll");CreateLibrary(m_hSslDll2, "syncesel2.dll");
m_hSslDll2->Init();m_hSslDll2->Init();
return true;}
PostReveiveError 가 전역함수-> 라이브러리 생성-> 동작 제거를 위해 라이브러리 연결-> 테스트 할때는 테스트 라이브러리와 연결-> 실제 시스템 빌드시 , 제작 라이브러리와 연결
객체 봉합
• 객체지향 프로그램은 어떤 메소드가 실행될지 정의하지 않음
• 같은 이름을 가지는 메소드가 하나 이상 있을 수 있음
• 주변 코드 변화 없이 어떤 메소드가 호출될지 변경할 수 있는 경우가 객체 봉합
bool CAsyncSslRec::Init(){
if(m_bSslInitialized){return true;
}m_smutex.Unlock();m_nSslRefCount++;
m_bSslInitialized = true;
FreeLibrary(m_hSslDll1);m_hSslDll1 = 0;FreeLibrary(m_hSslDll2);m_hSslDll2 = 0;
if(!m_bFailureSent){m_bFailureSent = true;PostReceiveError(SOCKETCALLBACK, SSL_FAILURE);
}
CreateLibrary(m_hSslDll1, "syncesel1.dll");CreateLibrary(m_hSslDll2, "syncesel2.dll");
m_hSslDll2->Init();m_hSslDll2->Init();
return true;}
PostReveiveError 를 오버라이드 하는테스트용 하위 클래스 객체 생성-> 동작을 변화시키지만 코드는 편집하지 않도록 함
봉합모델레거시 코드를 위한 도구정리가 안되지만 맞는 얘기들
리팩토링
• 소프트웨어의 내부 구조에 가해지는 변화
• 소프트웨어에 존재하는 동작을 변경하지 않음
• 이해하기 쉽게 변경
• 직접 할 수도 있고 , 도구를 이용 할 수도 있음
자동화된 리팩토링 도구
• 언어마다 리팩토링 도구들이 존재
• 통합개발 환경으로 구성되거나 분리됨
리팩토링 검증
• 리팩토링 과정에서 버그가 생길 수 있음
• 자동화된 리팩토링 도구에 버그가 있을 수 있음
• 리팩토링 후 동작을 변경시키지 않는지 검증해야 함
리팩토링 예제public class A{ private int alpha = 0; private int getValue(){ alpha++; return 12; } public void doSome-thing(){ int v = getValue(); int total = 0; for(int n=0; n<10; n++){ total += v; } }}
v 를 제거하라 !!
public class A{ private int alpha = 0; private int getValue(){ alpha++; return 12; } public void doSomething(){ int total = 0; for(int n=0; n<10; n++){ total += getValue(); } }}
리팩토링 예제public class A{ private int alpha = 0; private int getValue(){ alpha++; // alpha 1 증가 return 12; } public void doSomething(){ int v = getValue(); int total = 0; for(int n=0; n<10; n++){ total += v; } }}
리팩토링 후동작이 변경됨
public class A{ private int alpha = 0; private int getValue(){ alpha++; // alpha 10 증가 return 12; } public void doSomething(){ int total = 0; for(int n=0; n<10; n++){ total += getValue(); } }}
테스트 루틴을 두어 리팩토링 후 수행 !!
ex) alpha 값 검증
어떻게 검증할까 ?
xUnit 단위 테스트 프레임워크
• 소프트웨어의 함수나 클래스 같은 서로 다른 구성
원소 ( 단위 ) 를 테스트 하는 프레임워크
• 같은 테스트 코드를 여러번 작성하지 않음
• Java 단위 테스트는 jUnit
C++ 단위 테스트는 CppUnit
.NET 단위 테스트는 Nunit
• 오픈소스로 사용 가능
Junit
• xUnit 의 자바버전
• TestCase 라는 클래스로 하위 클래스화함으로써 테스트 루틴을 작성
import junit.framework.*;
public class FormulaTest extends TestCase{ // testXXX() 형식의 메소드
public void testEmpty(){ // 테스트 하려는 내용 ( 빈 경우 테스트 ) // 확인코드 // 코드포함가능 ( 새로운 객체 생성 , value 메소드 이용 )assertEquals(0, new Formula("").value());
}public void testDigit(){
// 값이 일치하면 테스트 통과 , 아니면 테스트 실패assertEquals(1, new Formula("1").value());
}}
메소드 별로 서로 영향을 주지 않기 위해
각 테스트 메소드 용으로 완전히 분리된 객체 생성
public class EmployeeTest extends TestCase{private Employee employee;
// 테스트 메소드가 실행되기 전에 각 테스트 객체에서 실행됨protected void setUp(){
// 각 테스트에서 사용할 객체 생성employee = new Employee("Fred", 0, 10); TDate cardDate = new TDate(10, 10, 2000);employee.addTimeCard(new TimeCard(cardDate, 40));
}
public void testNormalPay(){ //setUp 에서 생성된 하나의 employee 가 한번만 시간카드의 봉급 계산하는지 검사assertEquals(400, employee.getPay());
}
public void testOvertime(){TDate newCardDate = new TDate(11, 10, 2000);employee.addTimeCard(new TimeCard(newCardDate,
50));//setUp 에서 생성된 하나의 employee 가
초과 근무 조건을 발생시키는지 검사assertTrue(employee.hasOvertimeFor(newCardDate));
}}
이거 합시다 ~( 이미 했지만 ~)
Fit
• 통합 테스트 프레임워크
• 입력값 , 출력값을 표로 작성
• HTML 형식으로 저장
• 작성한 표가 테스트 루틴이 됨
• 테스트를 통과하면 표의 셀을 녹색으로 표시
• 테스트를 실패하면 표의 셀을 빨간색으로 표시
Fit
• 고객 ( 기획자 ) 이 표를 작성
• 고객과 개발자 간의 의사소통이 쉬움
• 처음에는 모두 실패
• 개발 완료시 , 테스트 모두 통과
Fitnesse
• 위키에 적용된 FIT
• 빠르고 쉽고 테이블을 디자인 가능
• 웹 형태로 제공되어 누구나 쉽게 테스트케이스의 공유 및 작성 , 테스트가 가능
!define TEST_SYSTEM {slim} !define COLLAPSE_SETUP {true}!define COLLAPSE_TEARDOWN {true} !|Create Programs ||Name |Channel|DayOfWeek|TimeOfDay|DurationInMin-utes|id? ||House|4 |Monday |19:00 |60 |$ID=|
NTAF
• 테스트 흐름이 획일적인 Fitnesse 의 단점을 보완
• If, Loop, Parallel 등 키워드 추가
• 로그 확인 , 변수 연산 등 가능
봉합모델레거시 코드를 위한 도구정리가 안되지만 맞는 얘기들
정리가 안되는 맞는 얘기들
• 재사용 가능하도록 작은 조각들로 쪼개 프로그램을 작성하는 것이 좋음
• 자주 독립적으로 재사용되는 모듈화는 어려운 일
• 테스트하면 코드를 다른 각도에서 볼 수 있게 됨
• 테스트 루틴을 작성하려 할 때서야 기존의 코드가 얼마나 엉성하게 짜여져 있는지 알게 됨
비트 경제 (?) 와 공짜 테스트
나는 개발자개발 해서 돈을 벌지 ~
그런데 내가 왜 개발하면서테스트 코드를 만들어야지 ?
테스트는 QA 가 알아서 할텐데뭣하러 공짜로 ?
공짜가 아니야 ~개발 단계에서 작성하는 테스트 코드는QA 가 진행하는 테스트보다비용과 시간이 절감되지
기획과 합심하면 QA 쯤이야그 화폐를 우리 손으로 ~
QA 는 개발자가 할 수 없는 테스트를 찾아서 ..
레거시 코드 활용 전략4 장 봉합 모델5 장 레거시 코드를 위한 도구
감사합니다