필자는 Damian Conway의 Perl Best Practices:의 다음과 같은 문구를 인용하여 이 글을 시작하겠다. "'오브젝트 지향'에 대한 Perl의 접근방식은 거의 너무 Perl적이다(Perlish). 이를 수행하는 방법은 엄청나게 많다... 가능한 구현, 구조 및 시맨틱의 조합이 너무나 많기 때문에, Perl 00의 동일한 스타일을 정확하게 사용하는 두 개의 관련 없는 클래스 계층을 찾는 것이 매우 드물다."
Perl 언어의 설계 측면에서 이러한 내재된 유연성은 당연히 Perl 코드의 유기적 축적을 초래하였다. 이는 기술적으로 실행되기는 하지만 변경하기에는 손상되기 쉽고 이해하기에는 어려웠다. 문제가 심각해지는 것은 원래의 개발자가 다른 프로젝트나 다른 회사로 이동하여 더 이상 없을 수 있는 경우이다. 레거시 코드에 대한 부담 이외에도 프로덕션 요구사항은 변경될 수 있고, 또는 더 새로운 공급업체 API가 Python에서만 사용 가능할 수도 있다. 이 때에 Perl을 Python으로 포트하는 기념비적인 위업이 시작된다.
이러한 결정에 도달하면 문제를 해결하기 위해 최선의 정책을 선택해야 한다. 다행히 전체 테스트 커버리지를 갖춘 잘 쓰여진 오브젝트 지향 Perl의 코드 기반이 있는 경우, Perl에서부터 Python으로 유닛 테스트를 포트한 다음에 적절한 Python 코드가 새롭게 포트된 Python 유닛 테스트를 패스하도록 만드는 것만큼 간단할 수 있다. 잘 문서화되고 읽기 가능한 코드를 쓰는 유능한 Perl 프로그래머들이 많다고 하더라도 이들은 가능한 것만큼 일반적이지는 않다. 아마도 Perl 코드가 정확히 어떻게 작동하는지 몰라서 작동하는 것을 관찰할 수 밖에 없는 상황이 될 것이다. 여기에서 Perl을 Python으로 포트하기의 애로점이 실제로 시작된다.
금기 사항: 새 Python 코드에서부터 Perl 코드 호출
이를 수행하기 위한 권장 방법을 설명하기 전에 먼저 금기 사항에 대해 살펴보자. 도전과제에 직면했을 때 가장 저항이 적은 경로를 선택하는 것은 인간의
본성이다. 10년 간 유기적으로 성장하고 테스트하지 않은 Perl 코드를 Python으로 포트하는 것은 어려운 문제이기 때문에, 가장 명백한 솔루션은
그 Perl 코드 전부를 다시 쓰는 것과 관련하여 방법을 찾는 것으로 보인다. 이렇게 생각하다 보면 Python에서 Perl 인터프리터를 임베드할 수 있는 perlmodule이라는
모듈까지 생각하게 된다. 이는 새 Python으로부터 이전 Perl 코드를 호출하여 수행하는 것처럼 단순해 보인다.
이는 실제로는 매우 좋지 않은 생각이다. 왜냐하면 시작할 때보다 이제 더 큰 문제가 발생하기 때문이다! 이해하지 못하는 레거시 코드가 생겼고, 이해하지 못하는 코드를 호출하는 새 코드가 생겼다. 이는 또다른 카드에서 현금 선지급을 사용하여 다른 신용 카드 대금을 지불하는 것과 같다. —필연적인 일을 미루고 기술적 부채(technical debt)를 늘리는 것일 뿐이다(기술적 부채에 대한 자세한 정보는 참고자료의 링크 참조). 설상가상으로 테스트하기 어렵거나 테스트가 불가능한 사소한 버그를 통합하여 새 코드가 "감염될" 것이다. 마침내 그 프로젝트에 나중에 합류한 신규 개발자는 테스트하지 않은 Perl과 부적절하게 테스트한 Python이 위협적으로 혼합된 코드 기반을 사용하여 작업하게 될 것이다.
스펙을 새로 작성하기 위해 nose로 레거시 코드 기능적 테스팅
Working Effectively With Legacy Code이라는 책의 저자인 Michael Feathers는 "기존 코드에 대해 테스트를 쓰려고 시도하면 거의 모든 사람은 그 코드가 테스트하기에 얼마나 엉망인지를 느끼게 된다." 레거시의 테스트하지 않은 Perl을 Python으로 포트하는 것에 대해 생각할 때에 아마도 이와 같이 느낄 것이다.
심리적이며 기술적인 중요한 단계 중 하나는 포트하려고 시도하는 중인 Perl 코드의 최종 결과를 정확하게 포착하는 기능적 테스트를 작성하는 것이다. 예를 들어, 대형 로그 파일을 구문 분석하고 콤마로 분리된 값 보고서를 생성하는 Perl 스크립트를 포트하는 경우, 쓰고 있는 새 코드에서 이것이 실제로 발생하는지 확인하는 간단한 실패의 기능적 테스트를 쓸 수 있을 것이다.
이 새 예제를 따라 하려면 nose를 설치해야 할 것이다. Python easy_install 도구가 이미 설치되어 있는 경우 easy_install nose 명령을 간단하게 실행할 수 있다. 그렇지
않은 경우, 먼저 setuptools 설치 지시사항을 따라 setuptools를 설치할 수 있다.
이를 사용하여 준비가 되면, 여기에 예제 nose 테스트가 있다.
목록 1. 의도적으로 실패하는 nose 기능적 테스트
#!/usr/bin/env python
"""First pass at porting Perl to Python"""
import os
def test_script_exists():
"""This test intentionally fails"""
assert os.path.exists("myreport.csv")
|
계속 진행하여 실제로 이 테스트를 실행하면 다음과 같이 표시되어야 한다.
목록 2. 테스트 결과
linux% /usr/local/bin/nosetests
F
======================================================================
FAIL: test_failing_functional.test_script_exists
----------------------------------------------------------------------
Traceback (most recent call last):
File "/usr/local/Python/2.5/site-packages/nose-0.10.4-py2.5.egg/nose/case.py",
line 182, in runTest
self.test(*self.arg)
File "/usr/home/ngift/tests/test_failing_functional.py", line 7, in test_script_exists
assert os.path.exists("myreport.csv")
AssertionError
----------------------------------------------------------------------
Ran 1 test in 0.037s
FAILED (failures=1)
|
이 실패 테스트에서 확인 가능한 대로 이 파일을 작성하기 위해 아무 것도 하지 않았기 때문에 어설션은 실패했다. 이는 처음에는 무의미한 것처럼 보이지만, 레거시 코드의 어두운 배후에서 가능한 한 많은 것을 맵핑 아웃하는 프로세스에서 하나의 단계이다.
이전 코드의 기능적 스펙을 가능한 한 많이 채워 넣어서 기능적 테스트를 쓰면, 모듈형의 테스트 가능하고 잘 쓰인 Perl 부분에 대해 실패하는 유닛 테스트를 작성하는 것까지 식별할 수 있는지 확인할 가치가 있을 것이다. 합리적인 스펙이 모양을 갖추기 시작할 때까지 코드의 이러한 부분에 대해 실패하는 테스트를 더 많이 쓸 수 있다.
실제로 가장 어려운 부분인 마지막 단계는 작성한 이 테스트를 패스하는 Python 코드를 쓰는 것이다. 불행히도 묘안은 없다. 레거시의 테스트하지 않은 코드를 Perl이나 다른 언어에서 포트하는 것은 말 그대로 어렵지만, 실패하는 테스트를 쓰는 것은 큰 도움이 될 수 있으며, 합리적인 전략이다.
Guido Van Rossum의 기사인 "Strong Versus Weak Typing"의 다음 글을 인용하여 마무리하자. "버그를 모두 없애지는 못할 것이다. 코드를 읽고 쓰기에 더 쉽고 소스 코드를 검토할 독자의 팀에게 더 분명하게 만드는 것이 훨씬 더 가치있을 수 있다...."
궁극적으로 읽기 가능하고 테스트 가능한 코드를 작성하는 것은 조심스럽게 말한다면, Python과 같이 새 언어로 레거시 코드를 포트하는 것의 주요 목표 중 하나이다. 이 이상을 수용하면 그 프로세스의 두려움과 고통을 어느 정도 없앨 수 있다. 행운을 빈다!
교육
- Nose에 대한 전체 튜토리얼은 An Extended
Introduction to the nose Unit Testing Framework를 읽어보자.
- Nose에 대한 최신 업데이트는 Nose
테스팅 사이트를 확인하자.
- 입력에 대한 Guido van Rossum의 통찰에 대한 자세한 내용은 "Strong Versus Weak
Typing"을 읽어보자.
- perlmodule 모듈에 대한 자세한 내용은 CPAN을 확인하자.
- 위키피디아의 Technical debt 항목에서 성급한 아키텍처와 개발 결정의 비용을
설명하는 이 용어에 대한 간단한 배경 설명이 나와 있다.
- Working Effectively with Legacy Code에 대한 서적도 저술되어 있다.
- Pythoscope 사이트에서는 이 유용한 Python용 유닛 테스트 작성기에 대한 자세한 정보가 나와 있다.
- Damian Conway의 저서인
Perl Best
Practices를 반드시 읽어보자.
-
Linux에서는
수백 개의 기술자료 목록과 함께, Linux 개발자와 관리자를 위한
다양한 다운로드, 토론 포럼 및 다른 참고자료를 찾을 수 있다.
-
developerWorks 기술 행사 및 웹 캐스트를 통해 다양한 IBM 제품 및 IT 산업 주제에 대한 최신 정보를 얻을 수 있다.
-
무료 developerWorks Live!
briefing을 통해 최신 IBM 제품 및 도구에 대한 정보뿐만 아니라 IT 업계의 최신 경향까지도 빠르게 확인할 수 있다.
-
developerWorks on-demand demos에서는 입문자를 위한 제품 설치 및 설정부터 숙련된 개발자를 위한 고급 기능까지 망라된 다양한 데모를 제공한다.
-
Twitter의 developerWorks를 팔로우(follow)하거나
developerWorks에 대한 Linux 트윗(tweet)의 피드를 구독하자.
제품 및 기술 얻기
-
자신에게 가장한 적합한 방법으로 IBM
제품을 평가해 보자. 시험판 제품을 다운로드하거나, 온라인으로 제품을 사용해 보거나, 클라우드 환경에서 제품을 사용하거나,
SOA Sandbox에서
SOA(Service Oriented Architecture)를 효과적으로 구현하는 방법을 배울 수 있다.
토론
-
developerWorks community에 참여한다. 개발자가 운영하고 있는 블로그, 포럼, 그룹 및 위키를 살펴보면서 다른 developerWorks 사용자와 의견을 나눌 수 있다.

Noah Gift is the co-author of Python For UNIX and Linux System Administration by O'Reilly, and is also working on Google App Engine In Action for Manning. He is an author, speaker, consultant, and community leader, writing for publications such as Red Hat Magazine, O'Reilly, and MacTech. His consulting company's website is http://www.giftcs.com, and much of his writing can be found at http://noahgift.com. You can also follow Noah on Twitter.
He has a Master's degree in CIS from Cal State Los Angeles and a B.S. in Nutritional Science from Cal Poly San Luis Obispo. He is an Apple- and LPI-certified sysadmin, and has worked at companies such as Caltech, Disney Feature Animation, Sony Imageworks, Turner Studios, and Weta Digital. In his free time, he enjoys spending time with his wife, Leah, and their son, Liam, composing for the piano, running marathons, and exercising religiously.