JUnit로 검색한 결과 :: 시소커뮤니티[SSISO Community]
 
SSISO 카페 SSISO Source SSISO 구직 SSISO 쇼핑몰 SSISO 맛집
추천검색어 : JUnit   Log4j   ajax   spring   struts   struts-config.xml   Synchronized   책정보   Ajax 마스터하기   우측부분

회원가입 I 비밀번호 찾기


SSISO Community검색
SSISO Community메뉴
[카페목록보기]
[블로그등록하기]  
[블로그리스트]  
SSISO Community카페
블로그 카테고리
정치 경제
문화 칼럼
비디오게임 스포츠
핫이슈 TV
포토 온라인게임
PC게임 에뮬게임
라이프 사람들
유머 만화애니
방송 1
1 1
1 1
1 1
1 1
1

JUnit로 검색한 결과
등록일:2008-03-20 16:27:54
작성자:
제목:사람을 위한 자동화: 빌드 스크립트에서 나는 “악취(smell)” 제거하기 (한글)


일관성 있고, 반복 가능하며, 관리 가능한 빌드를 구현하는 습관

developerWorks
문서 옵션

JavaScript가 필요한 문서 옵션은 디스플레이되지 않습니다.

이 페이지를 이메일로 보내기

이 페이지를 이메일로 보내기


제안 및 의견
피드백

난이도 : 초급

Paul Duvall, CTO, Stelligent Incorporated

2006 년 11 월 21 일

프 로젝트 빌드 스크립트를 관리하는데 어느 정도의 시간을 소비합니까? 아마도 여러분이 생각했던 것 이상의 시간이 들 것입니다. 그럴 필요가 없는데 말입니다. 개발 자동화 전문가 Paul Duvall이 본 시리즈를 통해 기존 빌드 방식을 향상시켜, 일관성 있고, 반복 가능하며, 관리 가능한 빌드를 만드는 방법을 설명합니다.

나는 코드(code)에 대해 논할 때, “냄새(smell)”라는 단어를 사용하는 것을 별로 달가워하지 않는다. 비트와 바이트를 논하는 곳에 사용하기에는 약간 이상하다. “냄새(smell)”라는 단어는 코드가 잘못되었다는 증상을 꼭 집어 표현하는 것은 아니라는 것을 알지만, 조금 우습게 들린다. 하지만, 수년 동안, 내가 보아온 수 많은 빌드 스크립트들이 코를 찌르는 악취가 나기 때문에, 나 역시 이 단어를 사용하지 않을 수 없다.

가끔은, 숙련된 프로그래머 조차도 빌드 스크립트를 만드는데 애를 먹는다. 통합 빌드 파일 작성, 스크립트 코드의 카피&페이스트, 애트리뷰트의 하드 코딩 같은 절차적코 드 작성 방법을 최근에 배운 사람들처럼 군다. 나는 언제나 그 이유가 궁금했다. (빌드 스크립트는 사용자가 결국 사용하게 될 어떤 것으로 컴파일 되지 않기 때문인가?) 하지만, 빌드 스크립트는 사용자가 결국 사용하게 되는 코드를 만들 때 중심이 되고, 그러한 스크립트가 커다란 진흙 덩이라면, 소프트웨어를 효율적으로 구현하는 것은 도전이 된다.

고 맙게도, 여러분은 많은 빌드 방식들을 쉽게 적용하여(Ant, Maven 등), 빌드를 일관성 있고, 반복 가능하며, 관리 가능한 것으로 유지할 수 있다. 더 나은 빌드 스크립트를 구현하는 방법을 배우는 한 가지 효과적인 방법은, 해서는 안될 일이 무엇인지를 파악하고, 왜 그런지를 이해한 다음, 올바른 방법을 파악하는 것이다.) 이 글에서, 나도 그 방식을 취할 것이다. 이 글에서, 반드시 피해야 하는 아홉 가지 일반적인 빌드 방식(smell), 피해야 하는 이유, 이를 픽스하는 방법을 설명하겠다:

  • IDE 전용 빌드(IDE-only build)
  • 카피&페이스트 스크립팅(Copy-and-paste scripting)
  • 긴 타겟(Long target)
  • 대형 빌드 파일(Large build files)
  • 클린업 실패(Failing to clean up)
  • 값 하드 코딩(Hard-coded values)
  • 테스트가 실패했음에도 성공적인 빌드 (Builds that succeed when tests fail)
  • 매직 머신(Magic machines)
  • 스타일의 부족(A lack of style)
시리즈 소개
우리 개발자들은 사용자 측의 프로세스 자동화를 위해 일한다. 하지만, 우리들 대부분이 자신의 개발 프로세스를 자동화 할 기회들을 놓치고 있다. Automation for the people 시리즈에서는 소프트웨어 개발 프로세스를 자동화 하고 자동화를 성공적으로 적용하는 시기방법 을 설명한다.

수년 동안 보아온 빌드 스크립트에서 느꼈던 일반적인 문제점들을 추려보았다. 또한, Maven 같은 일부 툴은 그러한 문제들의 일부분을 없애는데 도움이 되지만, 대부분의 문제들은 어떤 툴을 사용하든지 상관없이 발생하고 있다.

IDE 전용 빌드 피하기

IDE 전용 빌드는 개발자의 IDE를 통해서만 실행될 수 있는 빌드이고, 불행히도 이것은 너무나 일반적인 빌드 오류 중 하나이다. IDE 전용 빌드의 문제는 소프트웨어가 어떤 환경도 아닌, 개발자의 환경에서만 작동하는 “works on my machine” 문제를 지속시킬 수 있다는 점이다. 더욱이, IDE 전용 빌드는 자동화 될 수 없기 때문에, Continuous Integration 환경으로 통합하는 것이 큰 어려움이다. 사실, IDE 전용 빌드는 사람의 개입 없이는 자동화가 불가능하다.

정리해 보자. IDE를 사용하여 빌드를 실행하는 것은 좋은 일이지만, 당신의 IDE가 소프트웨어를 구현할 수 있는 유일한 것이 되어서는 안된다. 특히, 완전히 스크립팅 된 빌드로 팀은 다중 IDE를 사용할 수 있다. 의존성은 IDE에서 빌드로 향하지 그 반대로 향하지 않기 때문이다. (그림 1)


그림 1. IDE와 빌드 의존성
IDE and build dependencies

IDE 전용 빌드는 자동화를 가로막고, 유일한 픽스 방법은 스크립팅 가능한 빌드를 만드는 것이다. 이 방법을 다루는 많은 문서와 책들이 있고(참고자료), Maven 같은 프로젝트들이 빌드를 정의하기 쉽도록 만든다. 빌드 플랫폼을 선택하고, 가능한 빨리 당신의 프로젝트를 스크립팅 가능한 것으로 만들라.




위로


카피&페이스트는 마치 값싼 향수와 같다.

중 복 코드는 소프트웨어 프로젝트의 일반적인 문제이다. 사실, 많은 대중적인 오픈 소스 프로젝트들 조차도 중복 비율이 20-30 퍼센트에 달하고 있다. 코드 중복이 소프트웨어 프로그램을 관리하기 어렵게 만들듯이, 빌드 스크립트에도 중복 코드는 문제가 된다. 예를 들어, Ant의 fileset을 통해 특정 파일을 참조한다고 하자. (Listing 1):


Listing 1. Ant 스크립트의 카피&페이스트
<fileset dir="./brewery/src" >
<include name="**/*.java"/>
<exclude name="**/*.groovy"/>
</fileset>

컴파일, 검사, 문서 생성을 위해서 이 파일 세트를 다른 곳에서 참조해야 한다면, 여러 장소에서 같은 fileset를 사용하게 될 것이고, 그 fileset를 변경하려면(.groovy 파일 제외) 여러 곳에서 변경 작업을 해야 한다. 관리가 불가능하다. 하지만 픽스 방법은 간단하다.

Ant의 patternset type(Listing 2)으로 논리적 이름을 참조할 수 있다. 이것은 우리가 필요로 하는 파일들을 보여준다. 그 fileset에 파일들을 추가(또는 제거)해야 한다면, 단 한번에 해야 한다.


Listing 2. Ant 스크립트의 카피&페이스트
<patternset id="sources.pattern">
<include name="**/*.java"/>
<exclude name="**/*.groovy"/>
</patternset>
...
<fileset dir="./brewery/src">
<patternset refid="sources.pattern"/>
</fileset>

객체 지향 프로그래밍에 익숙한 사람이라면 이 픽스가 익숙할 것이다. 여러 클래스에서 반복적으로 같은 로직을 정의하지 않고, 그 로직을 다양한 곳에서 호출될 수 있는 메소드에 배치하는 것이다. 이 메소드는 하나의 관리 포인트가 되고, 오류를 줄이고 재사용을 가능케 한다.




위로


긴 타겟 냄새를 풍기지 말라!

Martin Fowler는 그의 저서, Refactoring, 코드와 관련된 문제를 “프로시저가 길어질수록, 이해하기 더 어려워진다.”라고 설명하고 있다. 긴 메소드는 너무 많은 책임이 따른다. 빌드에 이것이 적용되면, Long Method에서, 긴 타겟(Long Target) 빌드는 이해하기도, 관리하기도 어려운 스크립트이다. Listing 3은 비교적 긴 타겟을 보여준다.


Listing 3. 긴 타겟
  <target name="run-tests">
<mkdir dir="${classes.dir}"/>
<javac destdir="${classes.dir}" debug="true">
<src path="${src.dir}" />
<classpath refid="project.class.path"/>
</javac>
<javac destdir="${classes.dir}" debug="true">
<src path="${test.unit.dir}"/>
<classpath refid="test.class.path"/>
</javac>
<mkdir dir="${logs.JUnit.dir}" />
<JUnit fork="yes" haltonfailure="true" dir="${basedir}" printsummary="yes">
<classpath refid="test.class.path" />
<classpath refid="project.class.path"/>
<formatter type="plain" usefile="true" />
<formatter type="xml" usefile="true" />
<batchtest fork="yes" todir="${logs.JUnit.dir}">
<fileset dir="${test.unit.dir}">
<patternset refid="test.sources.pattern"/>
</fileset>
</batchtest>
</JUnit>
<mkdir dir="${reports.JUnit.dir}" />
<JUnitreport todir="${reports.JUnit.dir}">
<fileset dir="${logs.JUnit.dir}">
<include name="TEST-*.xml" />
<include name="TEST-*.txt" />
</fileset>
<report format="frames" todir="${reports.JUnit.dir}" />
</JUnitreport>
</target>

긴 타겟(나는 이것 보다 더 긴 것도 봤다.)은 네 가지 프로세스를 수행한다. 소스 컴파일, 테스트 컴파일, JUnit 테스트 실행, JUnitReport 구현이다. 그 모든 XML에 더해질 복잡함은 말할 것도 없이, 많은 책임이 따른다. 이 타겟은 네 개의 논리적 타겟으로 나뉠 수 있다. (Listing 4)


Listing 4. 타겟 추출하기
 <target name="compile-src">
<mkdir dir="${classes.dir}"/>
<javac destdir="${classes.dir}" debug="true">
<src path="${src.dir}" />
<classpath refid="project.class.path"/>
</javac>
</target>

<target name="compile-tests">
<mkdir dir="${classes.dir}"/>
<javac destdir="${classes.dir}" debug="true">
<src path="${test.unit.dir}"/>
<classpath refid="test.class.path"/>
</javac>
</target>

<target name="run-tests" depends="compile-src,compile-tests">
<mkdir dir="${logs.JUnit.dir}" />
<JUnit fork="yes" haltonfailure="true" dir="${basedir}" printsummary="yes">
<classpath refid="test.class.path" />
<classpath refid="project.class.path"/>
<formatter type="plain" usefile="true" />
<formatter type="xml" usefile="true" />
<batchtest fork="yes" todir="${logs.JUnit.dir}">
<fileset dir="${test.unit.dir}">
<patternset refid="test.sources.pattern"/>
</fileset>
</batchtest>
</JUnit>
</target>

<target name="run-test-report" depends="compile-src,compile-tests,run-tests">
<mkdir dir="${reports.JUnit.dir}" />
<JUnitreport todir="${reports.JUnit.dir}">
<fileset dir="${logs.JUnit.dir}">
<include name="TEST-*.xml" />
<include name="TEST-*.txt" />
</fileset>
<report format="frames" todir="${reports.JUnit.dir}" />
</JUnitreport>
</target>

각 타겟은 한 개의 책임이 있기 때문에, Listing 4의 코드는 보기가 더 쉽다. 목적에 따라 타겟을 고립시켜서, 복잡성을 줄일 수 있고, 다양한 콘텍스트에서 타겟들을 사용할 수 있고, 재사용도 가능하다.




위로


큰 빌드 파일 역시 진한 냄새를 풍긴다.

Fowler는 Large Class 역시 문제가 있다고 말한다. 빌드 스크립트에서는 큰 빌드 파일이 문제가 된다. 읽기가 너무 어렵다. 어떤 타겟이 무슨 일을 하는지, 그 타겟의 종속 관계는 어떤지를 알 수 없다. 이것은 관리 문제를 낳는다. 더욱이, 큰 빌드 파일에는 상당한 cut-and-paste 요소들이 들어있다.

빌드 파일의 크기를 줄이려면, 논리적으로 관련된 스크립트의 부분들을 찾고, 그러한 측면들을 주 빌드 파일에서 실행될 수 있는 더 작은 빌드 파일로 추출한다. (Ant에서, ant 태스크를 사용하여 다른 빌드 파일들을 호출할 수 있다.)

나 는 핵심 기능 별로 빌드 스크립트를 나누고, 독립 스크립트로서 실행될 수 있게끔 하는 것을 좋아한다. (빌드 컴포넌트화) 예를 들어, Ant 빌드에 네 가지 유형의 개발자 테스트를 정의한다: 단위, 컴포넌트, 시스템, 함수. 또한, 네 가지 유형의 자동화 인스펙터를 실행한다: 코딩 표준, 의존성 분석, 코드 커버리지, 코드 복잡성. 이들 테스트와 인스펙터를 하나의 통합된 빌드 스크립트(컴파일, 데이터베이스 통합, 전개 포함)에 두는 대신, 테스트와 인스펙터 실행 타겟을 두 개의 개별 빌드 파일로 나눈다. (그림 2)


그림 2. 빌드 파일 추출하기
Extract build files

더 작고, 더 자세한 빌드 파일들은 관리하기도, 이해하기도 더 쉽다. 사실, 이 패턴은 코드에도 적용된다. 마치 패턴을 보고 있는 것 같지 않은가?




위로


클린업(clean-up)

모든 가설들을 엄격히 줄이지 않는 빌드에는 재앙이 따른다. 예를 들어, 당신의 빌드가 “손상된 데이터로 생성된 바이너리를 제거하기” 같은 것을 없애지 않으면, 이전 빌드에서 넘어온 파일에서 에러가 발생한다. 심지어, 이전 빌드에서 온 파일이 있기 때문에 오히려 성공하는 빌드가 될 수도 있다.

다행히, 솔루션은 단순하다. 이전 빌드에서 생성된 모든 디렉토리와 파일들을 제거한다. 이러한 단순한 액션으로 가정을 줄이고 빌드의 성공 또는 실패 상태를 정확히 확인할 수 있다. Listing 5는 delete Ant 태스크를 사용하여 빌드 환경을 청소하여 이전 빌드에서 사용된 파일이나 디렉토리를 제거하는 모습이다.


Listing 5. 클린업
<target name="clean">
<delete dir="${logs.dir}" quiet="true" failonerror="false"/>
<delete dir="${build.dir}" quiet="true" failonerror="false"/>
<delete dir="${reports.dir}" quiet="true" failonerror="false"/>
<delete file="cobertura.ser" quiet="true" failonerror="false"/>
</target>

이전 빌드에서 남겨진 파일들은 불필요한 골칫거리이다. 빌드를 실행하기 전에 빌드가 만들어낸 모든 생성물들을 제거하라.




위로


하드 코딩의 폐해

카 피&페이스트 프로그래밍이 재사용을 막는 것처럼, 하드 코딩된 값 역시 그러하다. 빌드 스크립트에 하드 코딩된 값이 포함되면, aspect가 변경을 필요로 할 경우, 한 곳 이상의 장소에서 그 값을 수정해야 한다. 더욱이, 어떤 부분을 빼먹을 수 있고, 값의 미스-매치와 관련한 미묘한 에러가 생길 수 있다. 내 충고를 받아들여, 다중 빌드 스크립트를 사용하기로 했다면, 하드 코딩된 값은 빌드 관리의 도전 과제가 된다.

Listing 6에서, run-simian 태스크는 많은 하드 코딩된 경로와 값을 갖고 있다. 주로 _reports 디렉토리가 그렇다:


Listing 6. 값의 하드 코딩
  <target name="run-simian">
<taskdef resource="simiantask.properties"
classpath="simian.classpath" classpathref="simian.classpath" />
<delete dir="./_reports" quiet="true" />
<mkdir dir="./_reports" />
<simian threshold="2
" language="
java"
ignoreCurlyBraces="true" ignoreIdentifierCase="true" ignoreStrings="true"
ignoreStringCase="true" ignoreNumbers="true" ignoreCharacters="true">
<fileset dir="${src.dir}"/>
<formatter type="xml" toFile="./_reports/simian-log.xml" />
</simian>
<xslt taskname="simian"
in="./_reports/simian-log.xml"
out="./_reports/Simian-Report.html"
style="./_config/simian.xsl" />
</target>

_reports 디렉토리의 하드 코딩 때문에 Simian 리포트를 또 다른 디렉토리에 넣기가 어렵다. 더욱이, 다른 툴이 스크립트의 다른 곳에서 이 디렉토리를 사용하거나, 누군가가 디렉토리 이름을 잘못 타이핑하면, 다른 디렉토리에 리포트가 나타나게 될 것이다. 그 디렉토리를 가리키는 속성 값을 정의하는 것이 훨씬 더 쉽고 관리도 수월하다. 스크립트 전반에 걸쳐, 그 속성을 참조할 수 있고, 변경할 때에는 그 속성 정의만 변경하면 된다. Listing 7은 리팩토링 된 run-simian 태스크이다:


Listing 7. Using properties
  <target name="run-simian">
<taskdef resource="simiantask.properties"
classpath="simian.classpath" classpathref="simian.classpath" />
<delete dir="${reports.simian.dir}" quiet="true" />
<mkdir dir="${reports.simian.dir}" />
<simian threshold="${simian.threshold}
" language="
${language.type}"
ignoreCurlyBraces="true" ignoreIdentifierCase="true" ignoreStrings="true"
ignoreStringCase="true" ignoreNumbers="true" ignoreCharacters="true">
<fileset dir="${src.dir}"/>
<formatter type="xml" toFile="${reports.simian.dir}/${simian.log.file}" />
</simian>
<xslt taskname="simian"
in="${reports.simian.dir}/${simian.log.file}"
out="${reports.simian.dir}/${simian.report.file}"
style="${config.dir}/${simian.xsl.file}" />
</target>

하드 코딩은 유연성을 떨어뜨린다. 여러분의 소스 코드에 데이터베이스 연결 String을 쉽게 하드 코딩 하듯, 빌드 스크립트에 경로 같은 것을 하드 코딩 하지 말아야 한다.




위로


테스트가 실패할 때 빌드는 성공한다.

빌드는 단순한 소스 코드 컴파일 그 이상이고, 자동화된 개발자 테스트의 실행이 포함되며, 소프트웨어를 계속 작동시키고자 한다면, 실패한 테스트가 빌드에 잠입하게 해서는 안된다. 결국, 믿을 수 없다면, 테스트 하는 것이 상책이다?

Listing 8은 이러한 빌드 관행의 예이다. JUnit Ant 태스크의 haltonfailure 애트리뷰트는 디폴트 값인 false로 설정된다. JUnit 테스트가 실패하더라도 빌드는 실패하지 않음을 의미한다.


Listing 8. 테스트가 실패했음에도 빌드는 성공한다?
<JUnit fork="yes" haltonfailure="false" dir="${basedir}" printsummary="yes">
<classpath refid="test.class.path" />
<classpath refid="project.class.path"/>
<formatter type="plain" usefile="true" />
<formatter type="xml" usefile="true" />
<batchtest fork="yes" todir="${logs.JUnit.dir}">
<fileset dir="${test.unit.dir}">
<patternset refid="test.sources.pattern"/>
</fileset>
</batchtest>
</JUnit>

이러한 빌드 관행을 방지하는 두 가지 방법이 있다. 첫 번째는, haltonfailure 애트리뷰트를 true로 설정하는 것이다. 이렇게 되면 테스트가 실패했는데도 빌드가 성공하는 일은 없을 것이다.

이 솔루션에서 맘에 안드는 유일한 부분은, 테스트가 실패하는 확률을 모르기 때문에, 실패의 패턴을 볼 수 없다는 점이다. 따라서, 두 번째 방법은 테스트가 실패할 경우, 속성을 설정하는 것이다. 모든 테스트를 실행한 후에, 빌드를 실패한 Ant를 설정한다. 두 방식 모두 가능하다. Listing 9는 tests.failed 속성을 사용하는 두 번째 방식이다:


Listing 9. 실패한 테스트
<JUnit dir="${basedir}" haltonfailure="false" printsummary="yes" 
errorProperty="tests.failed" failureproperty="tests.failed">
<classpath>
<pathelement location="${classes.dir}" />
</classpath>
<batchtest fork="yes" todir="${logs.JUnit.dir}" unless="testcase">
<fileset dir="${src.dir}">
<include name="**/*Test*.java" />
</fileset>
</batchtest>
<formatter type="plain" usefile="true" />
<formatter type="xml" usefile="true" />
</JUnit>
<fail if="tests.failed" message="Test(s) failed." />

테스트가 실패하더라도 통과한 빌드는 보안에 대한 잘못된 인식을 심어준다. 테스트가 실패하면, 빌드도 실패한다. 한밤중에 자다가 문제에 직면하는 것 보다 일찍 문제를 파악하는 것이 낫다.




위로


매직 머신(Magic machine)

이 글에서 다루었던 문제들 중에서 가장 고약한 악취가 나는 문제이다. 매직 머신은 하드웨어의 어떤 한 부분이 기업의 소프트웨어 애플리케이션을 구현할 수 있는 유일한 머신이 되기 때문에 나타나는 증상이다. 이러한 상황은 마구잡이로 꾸며낸 이야기가 아니다. 내 오랜 경험을 통해, 많이 보아왔다. 이러한 머신들은 의존성을 상실하거나 불가피한 비트 오류가 발생할 때 신기를 발휘한다.

기 업 인프라스트럭처의 일반 머신이 어떻게 마법에 사로잡히는지는 쉽게 알 수 있다. 시간이 흐르면서, 개발자들은 그 머신의 스크립트에 의존성을 추가하고, 완전한 디렉토리 경로를 참조하거나, 선택된 머신에만 존재하는 툴을 설치한다. 이는 다른 머신에서는 빌드가 실행되지 못하도록 한다. (그림 3)


그림 3. 매직 머신
Magic machine

한 머신에 대한 고정된 레퍼런스, 특정 드라이브(C:)를 포함한 경로, 특정 머신 툴들은 머신을 빠르게 매혹시킬 수 있는 것들이다. C: 드라이브에 대한 레퍼런스를 보거나 특정 툴(grep)에 대한 호출을 볼 때마다 스크립트를 즉각 변경한다. C:\Program Files\ 디렉토리는 모든 머신에 있는 것이라는 생각을 했다면 다시 한번 생각해 보기 바란다.




위로


형편 없는 스타일

주류 언어의 프로그래밍 스타일과 마찬가지로, 빌드 스크립트를 관리할 때도 이와 비슷한 고려 사항들이 있다. 빌드 스크립트에 프로그래밍 스타일을 생각할 때, 다음 사항들을 고려해야 한다:

  • 속성 이름
  • 타겟 이름
  • 디렉토리 이름
  • 환경 변수 이름
  • 들여쓰기
  • 라인 길이

개인적으로, 스타일을 다룰 때 가능한 한 다른 규칙들을 활용한다. 다행히, 한 그룹이 The Elements of Ant Style (참고자료) 이라고 하는 레퍼런스를 만들었다. 여기에서, 필자는 타겟의 이름을 지을 때, 소문자를 사용하며, 하이픈을 사용한 단어 구분, 라인 길이, 들여쓰기 같은 규칙을 기술하고 있다. 어떤 리소스를 선택하든지 간에, 스타일 규칙을 일관성 있게 적용하면 장기적인 빌드 파일 관리에 도움이 된다.




위로


더 나은 빌드를 위해서...

나 는 값싼 향수는 참을 수 있지만, 관리가 불가능한 빌드 스크립트는 도저히 참을 수 없다. 분명, 문제가 많은 코드는 여러분의 귀중한 시간을 잡아먹을 것이고, 엉성하게 디자인 된 빌드 역시 그럴 것이다. 일관성 없고, 반복되지 않으며, 관리가 불가능한 빌드의 조짐이 보인다면 시간을 내서 이를 고쳐야 한다. 여러분의 개발 환경에 장미 향이 퍼질 것이다.

기사의 원문보기



참고자료

교육

제품 및 기술 얻기

토론


필자소개

Paul Duvall은 Stelligent Incorporated의 CTO이다.UML™ 2 Toolkit에 참여했으며, 현재 Continuous Integration: Improving Software Quality and Reducing Risk (Addison-Wesley)를 공동 집필하고 있다.


출처 : http://www.ibm.com/developerworks/kr/library/j-ap10106/index.html