메뉴 바로가기 검색 및 카테고리 바로가기 본문 바로가기

한빛출판네트워크

IT/모바일

유지보수가 어렵게 코딩하는 방법(5) : 테스트, 언어선택

한빛미디어

|

2011-07-22

|

by HANBIT

16,583

저자 : 로에디 그린(Roedy Green)
원문 : Canadian Mind Products

이전기사

이 글은 농담일 뿐이다! 혹시라도 이 글의 내용을 있는 그대로 받아들인 이가 있다면 정중히 사과한다. 캐나다 사람은 농담에 :-)를 삽입하는 것을 세련되지 못한 행동으로 받아들인다. 내가 유지보수할 수 있는 코드를 작성하는 방법에 관해 지껄일 때면 사람들은 별로 관심을 가지지 않았다. 어느 날 일을 그르치는 얼간이 같은 행동을 얘기해야 사람들이 더 반응을 보인다는 사실을 알게 됐다. 유지보수 할 수 없는 디자인 패턴을 확인하므로 더 효과적으로 악의적인 혹은 부지불식간에 일어나는 나쁜 일을 예방할 수 있다

테스트

나는 에러를 수정하는 모뎀이 있으므로 따로 프로그램을 테스트할 필요가 없다.
- Om I. 바우드(Om I. Baud)

프로그램에 버그를 남겨둠으로써 유지보수 프로그래머에게도 재미있는 일거리를 제공해야 한다. 잘 만든 버그라면 어디서 어떻게 발생했는지에 관한 단서를 남기지 않는다. 버그를 남겨두는 가장 게으른 방법으로는 우리 코드를 절대 테스트 하지 않는 방법도 있다.

절대 테스트하지 마라

에러나, 기기 크래쉬, OS 결함을 처리하는 코드는 절대 테스트 하지 않는다. OS가 반환하는 코드도 검사하지 않는다. OS가 반환하는 코드는 실행에 아무 도움이 되지 않으며 우리 테스트 시간만 오래 걸리게 한다. 게다가 우리 코드가 디스크 에러, 파일 읽기 에러, OS 크래쉬와 같은 모든 경우를 적절하게 처리하는지 어떻게 일일이 테스트 할 수 있겠는가? 도대체 왜 컴퓨터 시스템을 신뢰할 수 없는 것처럼 생각하고 교수대 같은 것이 제대로 동작하지 않는지 테스트해야 하는지 이해할 수가 없다. 최신 하드웨어에서는 에러가 발생하지 않는다. 그러나 아직도 누군가는 테스트 전용 코드를 구현하는가? 정말 지치는 일이 아닐 수 없다. 사용자가 우리 프로그램의 문제에 대해 불평한다면 사용자가 잘 알 수 없는 OS나 하드웨어 탓으로 떠넘기자.

세상이 무너져도 성능 테스트를 하지 않는다

프로그램이 좀 느리다고? 고객에게 더 빠른 컴퓨터를 사라고 말하자. 성능 테스트를 수행했다면, 문제가 일어나는 지점을 찾았을 것이다. 아마 문제를 해결하려면 알고리즘을 변경해야 할 것이고 제품 전체를 완전히 다시 설계해야 하는 경우도 생길 수 있다. 이런 일을 누가 하고 싶어 하겠는가? 게다가 고객사에 성능 문제가 불쑥 나타난다는 것은 이국적인 곳으로 공짜 여행을 할 수 있는 기회일 수 있다. 정말로 우리가 해야 할 일은 여권을 항상 가까이에 소지하면서 상황을 예의주시하는 것이다.

절대 테스트 케이스를 만들지 않는다

코드 커버리지나 패스 커버리지 테스트를 절대 수행하지 말자. 자동화 테스트는 겁쟁이나 하는 짓이다. 우리 루틴의 90%에 해당하는 기능을 찾고, 전체 테스트의 90%를 이러한 기능 확인에 사용한다. 결과적으로 이 기법으로는 우리 코드의 약 60% 정도만 검증을 한 셈이 되고 40% 정도의 노력을 절약할 수 있다. 절약한 시간으로 프로젝트의 백엔드 계획을 수립할 수 있다. 멋진 "마케팅 기능"이 동작하지 않는다는 사실을 알아차리기 전에 회사를 조용히 떠날 수 있다면 정말 짜릿한 경험이 될 것이다. 크고 유명한 소프트웨어 회사는 이런 방식으로 코드를 테스트한다. 따라서 위 방법을 사용할 것을 추천한다. 어떤 이유에서인지 모르겠지만 혹시 위 계획을 실천하지 못하고 아직 회사에 남아있어야 한다면 계속해서 이 문서를 읽기 바란다.

겁쟁이 전용 테스트

용감한 코더는 이 단계를 건너뛴다. 상사를 무서워하고, 직장을 잃을까 두려워하고, 고객의 불평 메일을 받거나 고소당하는 일을 걱정하는 프로그래머가 너무 많다. 이러한 두려움 때문에 행동에 제약을 받고, 생산성이 떨어진다는 것은 누구나 아는 사실이다. 연구에 따르면 테스트 단계를 없애는 것이 관리자로 하여금 출시일을 미리 결정할 수 있게 하는 등 계획 단계부터 많은 도움이 된다. 두려움이 없어진다면 혁신과 실험정신이 창궐할 수 있다. 프로그래머는 코드를 생산하는데 주력할 수 있고, 헬프 데스크와 기존 유지보수 그룹이 협력해서 디버깅을 담당할 수 있다.

우리의 코딩 능력을 온전히 믿는다면 테스트 따위는 더 이상 불필요하다. 논리적으로 생각해보면 테스트라는 것은 감정적인 자신감 결여 문제일 뿐, 기술적으로 문제를 해결하지도 않는다는 점을 바보라도 눈치챌 수 있다. 자신감 결여 문제는 테스트 과정을 완전 제거하고 프로그래머를 자신감 회복 과정에 보냄으로 해결할 수 있다. 테스트를 하려면 프로그램을 변경할 때마다 테스트를 해야 한다. 프로그래머를 자신감 향상 프로그램에 보내는 것과 테스트를 하는 것 어떤 것이 바람직한 일인가? 비용 절감 효과는 상상을 초월할 것이다.

디버그 모드에서만 동작하는 코드

TESTING을 1로 정의했다면,

#define TESTING 1 
아래와 같이 TESTING이 1인 경우에만 수행되는 별도의 코드 섹션을 가질 수 있다.
#if TESTING==1 
#endif 
별도의 코드 섹션에 다음과 같은 꼭 필요한 코드를 넣고 빼는 것은 우리의 자유다.
x = rt_val; 

누군가 TESTING 을 0으로 재설정하면 프로그램은 동작하지 않는다. 조금만 창의력을 발휘하면 이 기법으로 로직을 망가뜨리고 컴파일러 기능 장애를 초래할 수 있다.

언어 선택

철학이란 언어를 사용해 우리의 지성이라는 마력에 대항하는 싸움이다.
- 루트비히 비트겐슈타인(Ludwig Wittgenstein)

컴퓨터 언어는 바보 같은 동작을 방지하는 방향으로 점차 변화하고 있다. 최첨단 언어에 의존하는 것은 남자답지 못한 행동이다. 우리가 사용할 수 있는 가장 오래된 언어 사용을 고집하자. 가능하다면 8진법 기계어(필자는 Hans와 Frans처럼 계집애 같은 남자가 아니다. 나는 남성미 넘치는 사람으로 IBM의 레코드 장비(펀치 카드)의 플러그보드에 직접 케이블을 꽂아가며 코딩을 하거나 종이 테이프에 펀치를 이용해 구멍을 뚫어 코딩을 했던 사람이다)를, 안 된다면 어셈블러, 안 되면 포트란이나 코볼이라도, 안 되면 C, 안되면 베이직, 안되면 C++ 을로 시도하자.

포트란(FORTRAN)

포트란으로 코드를 작성하라. 상사가 그 이유를 묻는다면 포트란에는 유용한 라이브러리가 많아서 시간을 절약할 수 있다고 대답하자. 포트란으로 작성한 코드를 유지보수 할 수 있는 확률은 0이다. 따라서 유지보수 할 수 없는 코드 가이드라인을 지키기가 아주 쉬워진다.

Ada를 피하라

여기서 설명한 기법 중 약 20%는 Ada에 사용할 수 없다. Ada 사용은 적극 피해야 한다. 관리자가 우리를 압박하면 다른 사람 모두 Ada를 사용할 수 없다고 주장하고, lint나 plummer 같은 우리의 커다란 도구 스위트와 맞지 않는다는 점을 부각하자.

ASM 사용

모든 공통 유틸리티 함수를 asm으로 변환하자.

QBASIC 사용

모든 중요 라이브러리 함수는 QBASIC으로 남겨둔 다음, 큰 메모리 -> 중간 메모리 모델로 매핑을 처리하는 asm 래퍼를 만들자.

인라인 어셈블러

인라인 어셈블러를 우리 코드 여기저기에 흩어놓는 것도 재미있다. 어셈블러를 이해하는 사람은 이제 거의 없다. 몇 라인의 어셈블러 코드로도 유지보수 프로그래머를 얼려버릴 수 있다.

C를 호출하는 MASM

C에서 호출하는 어셈블러 모듈이 있다면 특별한 이유가 없더라도 가능한 한 자주 어셈블러에서 C로 다시 호출하게 하자. 어셈블러만의 코드 난독화의 매력을 줄 수 있는 goto, bcc 등을 충분히 활용하는 것도 잊지 말자.

유지보수 도구를 피하라

풍부한 코딩을 피하고 다른 언어에 조잡하게 만들어진 인터페이스를 사용하지 말아야 한다. 이들은 철저히 유지보수 프로그래머의 직업을 쉽게 도와줄 목적으로 설계되었다. 마찬가지로 제품이 생산되기도 전에 버그를 잡을 수 있게 설계한 Eiffel나 Ada도 피해야 할 대상이다.

발상의 전환

지옥은 다른 사람이다.
- 진 폴 사르트르(Jean-Paul Sartre), 출구가 없다(No Exit), 1934년

지금까지 어떻게 유지보수 프로그래머를 좌절시키고 당황시킬 수 있는지에 대한 팁을 살펴봤다. 유지보수 할 수 없는 코드를 작성하는 일을 방해하려는 직장 상사의 노력을 어떻게 저지하는지 또는 저장소의 코드가 어떤 형식를 유지할 것인지에 대한 선동을 할 수 있는지 살펴봤다.

직장 상사는 무엇이 좋은 것인지 안다

직장 상사가 자신의 20년 포트란 경력이 현재 프로그래밍에 훌륭한 도움이 될 수 있다고 생각한다면 그대로 그 사람의 제안을 받아들이자. 결과적으로 상사는 우리를 신뢰할 것이고 이는 우리의 경력에도 도움이 된다. 결과적으로 프로그램 코드를 난독화하는 새로운 기술을 얻을 수 있을 것이다.

헬프 데스크 조직을 와해시키라

코드에 버그가 창궐할 수 있게 하는 확실한 방법 중 하나는 유지보수 프로그래머에게 이 소식이 전해지지 않게 하는 것이다. 그러려면 헬프 데스크를 와해시켜야 한다. "전화 주셔서 감사합니다. 직원 연결은 "1"번을 음성 메일은 삐 소리 후 남겨주세요"라는 멘트가 나오는 자동응답기를 이용하고 직접 전화에 응답하지 말자. 이메일로 도움을 요청한 경우에도 문제 추적 번호를 할당하기 전에 무시되게 해야 한다. "당신의 계정에 문제가 있습니다. 현재 담당자가 없어 처리가 곤란합니다"와 같은 답변은 모든 문제에 거의 공통적으로 적용할 수 있는 문구다.

그 입 닫으라

Y2K와 같이 걱정스런 문제를 바짝 경계할 필요는 없다. 심각한 일이 벌어질 날이 다가오는 걸 알았더라도 실제 문제가 발생할 때까지는 공개적 토론을 삼가야 한다. 친구나, 직장동료, 우리의 발견을 가로챌 경쟁자에게 절대 말하지 말자. 어떤 경우에라도 이 사건에 힌트를 제공하거나 사건을 기회로 이용하지 말아야 한다. 전문 용어로 암호화한 메모를 위 관리자에게 전달해 둠으로써 면책할 구멍을 마련할 수 있다. 가능하면 추신으로 본문과 전혀 관련이 없는 사업 현황을 걱정하는 평문 메모를 덧붙이는 것도 좋은 방법이다. 이제 우리가 해야 할 일은 모두 했으므로 밤에는 두 다리 뻗고 잘 수 있을 것이다. 그리고 어느날 우리가 퇴직한 이후에 어마어마한 급여를 제시하며 우리를 애타게 찾을 그날을 기다리자.

헛소리로 당황시켜라

때로는 대형 망치가 다른 도구보다 미묘할 수 있는 것처럼, 미묘함이란 생각하면 생각할수록 놀라운 것이다. FooFactory라는 같은 클래스를 만들고 실제 이 클래스는 오브젝트 생성과는 전혀 관련 없는 클래스지만 주석에 GoF의 생성 관련 패턴(가짜 UML 디자인 문서를 가리키는 http 링크 등을 사용하면 효과적이다)을 추가하자. 이렇게 함으로 유지보수 프로그래머가 망상에 빠지게 할 수 있다. 좀 더 미묘하게 싱글톤을 반환하는 것이 아니라 새로운 인스턴스를 반환하는 protect 생성자와 메소드 Foo f = Foo.newInstance()를 만들 수도 있다. 이로 발생할 수 있는 부작용은 상상을 초월한다.

월간 서적 클럽

월간 서적 클럽에 가입하라. 저자 중에 책을 쓰느라 너무 바쁜 나머지 코드를 직접 작성하지 못하는 저자를 찾아보자. 다이어그램만 잔뜩 있고 예제 코드가 없는 책이 있는지 동네 서점에서 찾아보자. 이런 책을 훑어보면서 잘 알려지지 않은 유식한 단어를 알아둔 다음 건방진 애송이가 들어오면 이들 단어로 한 방 먹여줄 수 있다. 물론 우리의 코드로도 인상을 줄 수 있어야 한다. 우리의 어휘를 이해하지 못하는 사람은 아마도 그것은 우리가 너무 똘똘하고 알고리즘이 심오하기 때문이라고 생각할 것이다. 알고리즘을 쉽게 유추할 수 있게 설명하는 일은 피해야 한다.

직접 만들어라

우리는 늘 시스템 수준 코드를 직접 만들어보길 원한다. 지금이 바로 시스템 수준 코드를 작성해 볼 기회다. 표준 라이브러리는 무시하고 자신만의 라이브러리를 만들어보자. 이것이 우리의 이력서를 빚낼 것이다.

자신만의 BNF 만들기

항상 자신만의 명령어 문법을 독창적이고 알려지지 않은 BNF 표기법으로 문서화하자. 학습 열의를 떨어뜨릴 수 있으므로, 유효한 커맨드와 유효하지 않은 커맨드와 같은 샘플을 추가하면서까지 문법을 설명하는 것을 피해야 한다. 대충 그린 철로-다이어그램 정도면 충분하다. 각각의 심볼(문법에서 한 구문을 가리키는)이 명확히 무엇을 나타내는지 알 수 없게 해야 한다. 활자체, 컬러, 대문자 등과 같이 두 사물을 구분하는데 도움이 되는 어떤 시각효과도 사용하지 않는 것이 바람직하다. 우리의 명령어 자체의 BNF 표기에서는 항상 같은 구두 문자를 사용해야 한다. (...), [...], {...} , "..."와 같은 경우 독자는 구두점이 명령어의 일부인지, 꼭 있어야 하는 것인지, 옵션 사항인지 구별하기가 힘들어진다. 결국 우리가 만든 BNF를 이해하지 못할 정도로 멍청한 자들은 우리 프로그램을 사용할 자격을 상실하는 것이다.

자신만의 할당자를 만들어라

동적 저장소 디버깅이 복잡하고 시간이 많이 걸린다는 것은 모두가 아는 사실이다. 각 클래스에서 저장소를 낭비하지 않는다는 사실을 일일이 확인하는 것보다는 큰 저장소의 공간을 할당해주는 자신만의 저장소 할당자를 만드는 것이 바람직하다. 저장소를 직접 해제하는 것보다는 주기적으로 시스템을 리셋해서 힙(heap)을 정리하게 만들어야 한다. 리셋을 이용하면 시스템에서 처리해야 할 일(모든 저장소 누수를 확인하지 않아도 되므로)이 크게 줄어든다. 따라서 사용자가 시스템을 주기적으로 리셋하는 것을 까먹지 않는 한 힙 메모리 공간을 모두 사용하는 일이 없다. 프로그램을 배포한 다음에 이와 같은 동작을 바꾸려고 낑낑대는 이들의 모습이 떠오르지 않는가?

색다른 언어를 이용한 트릭

베이직 프로그래밍은 뇌를 손상시킨다.
- 에스거 비버 딕스트라(Edsger Wybe Dijkstra)

SQL 별칭

테이블 이름 별칭을 하나 혹은 두 개 문자로 정하자. 물론 관련이 없는 기존 테이블 이름을 별칭으로 사용하는 것도 바람직하다.

SQL 외부 조인

다양한 외부 조인 문법을 사용하므로 모두를 긴장시키자.

자바스크립트 범위

자바스크립트 코드의 함수는 호출자의 범위에 있는 모든 지역 변수에 접속할 수 있다는 사실을 "최대한 활용"하자.

비주얼 베이직 선언

다음과 같은 선언보다는

dim Count_num as string 
dim Color_var as string 
dim counter as integer 
아래와 같이 선언하는 것이 바람직하다
Dim Count_num$, Color_var$, counter% 

사람을 미치게 하는 비주얼 베이직

텍스트 파일에서 데이터를 읽을 때 실제 필요한 데이터 보다 15개 문자를 더 읽은 다음 실제 텍스트는 아래와 같이 삽입할 수 있다.

ReadChars = .ReadChars (29,0) 
ReadChar = trim(left(mid(ReadChar,len(ReadChar)-15,len(ReadChar)-5),7)) 
If ReadChars = "alongsentancewithoutanyspaces" 
Mid,14,24 = "withoutanys" 
and left,5 = "without" 

델파이/파스칼 전용 기법

함수나 프로시저를 사용하지 말자. 대신 label/goto 문으로 코드 여기저기를 점프할 수 있다. 이 기법은 유지보수 프로그래머를 정신을 빼 놓을 수 있다. 처음에는 이와 같은 기법에 적응할 시간을 좀 주고 갑작스럽게 마구잡이로 점프를 시작하는 것도 좋은 방법이다.

If문과 unless문을 계속 이어 나간다. 특히 아주 긴 줄일 경우에 효과적이다.

Lisp

LISP는 유지보수 할 수 없는 코드를 작성하는 우리에게는 환상적인 언어다. 아래와 같은 환상적인 코드를 감상해보라!

(lambda (*<8-]= *<8-[= ) (or *<8-]= *<8-[= )) 

(defun :-] (<) (= < 2)) 

(defun !(!)(if(and(funcall(lambda(!)(if(and "(< 0)(< ! 2))1 nil))(1+ !)) 
(not(null "(lambda(!)(if(< 1 !)t nil)))))1(* !(!(1- !))))) 

비주얼 폭스프로(Foxpro)

이 팁은 비주얼 폭스프로에서만 적용할 수 있는 내용이다. 변수의 상태가 undefined인 경우에는 값을 할당해야 변수를 사용할 수 있다. 이와 같은 상황은 변수의 형식을 확인할 때 발생한다.

lcx = TYPE("somevariable") 
위 코드의 수행 결과 lcx는 ‘U’ 또는 undefined가 된다. 그러나 변수에 범위를 할당하면 변수를 정의하는 효과가 나타나므로 논리적인 FALSE값을 갖게 할 수 있다. 간단하지 않은가?
LOCAL lcx 
lcx = TYPE("somevariable") 
그럼 lcx 값은 ‘L’ 혹은 논리값(결국 FALSE)을 갖는다. 유지보수 할 수 없는 코드를 작성하는데 이러한 특징이 얼마나 도움이 될지 상상해보라.
LOCAL lc_one, lc_two, lc_three... , lc_n 

IF lc_one 
DO some_incredibly_complex_operation_that_will_neverbe_executed WITH 
make_sure_to_pass_parameters 
ENDIF 

IF lc_two 
DO some_incredibly_complex_operation_that_will_neverbe_executed WITH 
make_sure_to_pass_parameters 
ENDIF 

PROCEDURE some_incredibly_complex_oper.... 
* 여기에 추가하는 많은 코드는 절대 실행되지 않는다.
* 우리의 메인 프로시저 코드를 여기에 잘라내서 붙인다면 재미있지 않겠는가! 
ENDIF 

다음회 계속
TAG :
댓글 입력
자료실

최근 본 상품0