IBM®
메인 컨텐츠로 가기
    Korea [국가변경]    이용약관
 
 
   
        제품    서비스 & 솔루션    고객지원 & 다운로드    회원 서비스    
메인 컨텐츠로 가기

한국 developerWorks  >  자바 | 오픈 소스  >

사람을 위한 자동화: 매우 중요한 부분을 병렬로 개발하기

서브버전에서 나누기(Branching), 태깅, 병합하기(Merging)

developerWorks
문서 옵션

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

영어원문

영어원문


제안 및 의견
피드백

난이도 : 초급

Paul Duvall, CTO, Stelligent Incorporated

옮긴이 : 백기선 dwkorea@kr.ibm.com

원문 게재일 : 2008 년 10 월 07 일
번역 게재일 : 2008 년 12 월 09 일

대부분의 개발 팀이 버전 관리 시스템을 사용하여 코드 변경을 관리하지만 여러 개발자가 동일한 코드에 병렬적으로 손을 대면 상당한 골칫거리가 됩니다. 이번 사람을 위한 자동화 연재에서는 자동화 전문가 Paul Duvall이 무료로 사용할 수 있는 오픈 소스 Subversion 버전 관리 시스템을 사용하여 소스 코드를 효율적으로 태깅하고, 나누고, 병합하는 방법을 보여줄 것입니다.

대부분의 개발 팀은 소스 코드 나누기를 할 때 둘 중 하나를 선택한다. 나누기를 전혀 하지 않거나 (더 심하게는) 나눠둔 브랜치가 넘쳐나서, 개발자가 어디에 변경을 적용해야 하는지 분명하지 않다. 또는 병합해야 할 변경 사항을 찾기가 너무 힘들어 위험하게 소프트웨어 배포 바로 전까지 미루기도 한다.

용어

트렁크(trunk)(헤드(head)라고 하기도 한다)는 메인라인 개발을 지칭할 때 사용한다. 브랜치는 메인라인 개발과 다른 변경 사항을 적용할 때 사용할 코드라인 복사본을 지칭한다. 태그(tag)(레이블(label)이라고 부를 때도 있다)는 개발 주기 나중에 다시 찾아 볼 수 있도록 코드라인을 식별하기 위한 코드라인의 타임 스탬프다.

완벽한 세상에서는 항상 트렁크에서 작업을 한다. 이렇게 하면 둘 이상의 코드라인 사이의 변경을 병합하는 복잡함에서 벗어날 수 있다. 하지만 실제 소프트웨어 개발을 할 때는 미래에 배포할 것을 개발하기도 하고 어떤 때는 이미 사용 중인 버전에 긴급한 패치를 개발하기도 한다. 개발 중에 새 코드를 방해하지 않도록 배포 버전에 대한 소스 코드 복사본에 접근할 필요가 있다.

하지만 팀이 코드라인을 나누려고 할 때 문제가 발생한다. 몇몇은 아마도 배포를 지연시키고 개발자를 병목 현상에 빠트리는 브랜치를 만들지 않기로 결정하기도 한다. 아니면 개발자들이 자주 병합하지 않아서 병합 충돌과 병목 현상을 겪고, 그리고 인해 배포가 지연되기도 한다. 브랜치가 늘어나면 프로젝트 저장소 내비게이션이 힘들어지며, 개발자들이 잘못된 코드를 부주의하게 바꾸기도 한다.

팀 개발이 병렬로 이뤄질 때, 코드를 가능한 자주 메인라인(트렁크)으로 다시 가져오는 것이 중요하다. 병합한 코드를 트렁크로 자주 되돌리는 것이 가능하지 않다면, 테스트를 실행하여 어떤 병합 충돌이 발생할지 확인하여 코드가 커밋될 때 병합으로 인한 고통이 다소 줄어들 수 있다. 개발을 병렬로 효율 있게 하려면, 서브버전 내부에 태그와 브랜치를 사용할 수 있다. 이 것은 오픈 소스며 무료로 사용할 수 있는 소스 코드 관리 시스템이다. 태깅(taggin)을 사용하여 팀은 소스 코드 이전 버전으로 안전하게 돌아갈 수 있다.

다음 주제를 다루면서 SVN에서 어떻게 병렬로 개발할 수 있는지 설명하겠다.

  • 트렁크에서 SVN 배포 태그를 만드는 방법
  • 배포 태그를 기반으로 SVN 브랜치 만들기
  • 메인라인(트렁크)으로 변경사항을 병합하는 기술
  • 지속적인 통합(CI)을 개발 중인 브랜치에 실행하는 방법과 주기적으로 트렁크에 대한 병합을 테스트하는 방법
  • 변경사항을 브랜치에서 트렁크에 적용하는 방법
  • 브랜치 소스 코드에 태깅하는 예제

이 연재에 대하여

개발자로서 우리는 사용자를 위해 프로세서를 자동화하는 일을 한다. 그렇지만 많은 개발자가 개발 과정을 자동화하는 기회를 간과한다. 그 때문에 사람을 위한 자동화는 소프트웨어 개발 과정을 자동화하는 실용적인 방법을 살펴보고 자동화를 언제, 어떻게 성공적으로 적용할지 가르치는 데 전념한 연재다.

그림 1은 몇몇 동시적인 코드라인에 대한 기본적인 작업 흐름도를 보여준다.


그림 1. 병렬적인 개발
병렬적인 개발을 위한 전형적인 개발 타임라인

그림 1에서, SVN 트렁크 버전 1.0.0과 1.1.0 사이에서 왕성한 개발이 이뤄지고 있다. 개발자 한 무리는 버전 1.0.1 브랜치에서 작업을 하는 반면 다른 개발자는 메인라인에서 개발을 하고 있다.

다양한 개발자가 각기 다른 코드라인에서 작업을 할 때 여러 전략과 기술을 사용할 수 있다. 이 기사에서는 내가 SVN을 사용한 프로젝트에서 흔히 쓴 흔히 접근 방법을 보여줄 것이다.

병렬 개발에 사용할 서브버전 설정하기

SVN 서버를 설치하고 설정하는 것은 본 기사의 범위를 벗어난다. 독자들이 SVN 서버에 접근할 수 있다고 가정하고, 다음 과정을 통해 준비를 해야 한다.

  1. SVN 클라이언트 소프트웨어를 작업 환경에 다운로드한다.
  2. 표준 로컬 디렉터리를 작업 환경에 생성한다.
  3. 디렉터리를 SVN 저장소에 추가한다.
  4. 디렉터리를 SVN 저장소에 커밋한다.

여러분의 운영체제에서 사용할 SVN 클라이언트 소프트웨어는 Tigris.org 웹 사이트(참고자료 참조)에서 다운로드해 작업 환경에 설치하라. SVN을 작업 환경의 시스템 경로에 설정하여 사용 가능한 상태로 만들라. svn co URL을 사용하여 자장소에 대한 SVN 체크아웃을 수행하라.

다음으로 로컬 디렉터리 세 개를 만든다.

  • branches: 메인라인 개발 이외의 소프트웨어를 유지 관리할 때 사용한다.
  • tags: 소프트웨어를 출시하고 나중에 변경사항을 확인할 때 사용한다.
  • trunk: 메인라인 개발에 사용한다.

Listing 1은 이 디렉터리들을 윈도(Windows®), 매킨토시, 그리고 *nix 기반 시스템에서 만드는 방법을 보여준다.


Listing 1. 서브버전에 추가할 로컬 디렉터리 만들기
$ mkdir branches
$ mkdir tags
$ mkdir trunk

이제 디렉터리들을 운영체제에 만들었고, 이것들을 SVN addcommit 명령어를 사용하여 SVN에 추가하고 커밋할 수 있다. 내가 Listing 1에서 생성한 디렉터리에서 Listing 2에 보이는 명령어를 실행한다(사용자 정보는 적절하게 수정하라).


Listing 2. 로컬 저장소를 원격 SVN 저장소에 추가하고 커밋하기
$ svn add *.*
$ svn commit -m "Setting up standard SVN branches, tags and trunk directories" \
  --username tjefferson --password Mont!cello 

Listing 1과 2에 있는 것을 수행한 후 SVN 저장소는 그림 2와 비슷하게 보일 것이다.


그림 2. 저장소에 만들어진 표준 SVN 디렉터리
저장소에 만들어진 표준 SVN 디렉터리

내 기본 SVN 저장소들을 만들어둔 다음, 이제 배포 태그를 만들 준비가 되었다.




위로


트렁크를 기반으로 배포 태그 만들기

태그의 목적은 코드라인 복사본을 특정 순간에 유일하게 식별하여 나중에 이 버전으로 다시 되돌아 오기 위함이다. 그림 3에서 brewery-1.0.0이라는 태그를 1.0.0 배포에 추가한 것을 볼 수 있다(태그는 언제든지 만들 수 있지만 태그는 보통 소프트웨어를 배포할 때 생성한다).


그림 3. SVN 트렁크에 유일한 태그 생성하기
트렁크를 기본으로 배포된 태그

트렁크가 배포한 소프트웨어를 담고 있다고 가정하고, 첫 번째 작업은 SVN 태그를 트렁크를 기반으로 만드는 것이다. Listing 3은 이 배포 태그를 어떻게 만드는지 예제를 보여준다.


Listing 3. 트렁크를 기반으로 배포 태그 만들기
<path id="svn.classpath">
  <fileset dir="${lib.dir}">
    <include name="**/*.jar" />
  </fileset>
</path>	
<taskdef name="svn" classpathref="svn.classpath" 
  classname="org.tigris.subversion.svnant.SvnTask"/>

<target name="create-tag-from-trunk">
  <svn username="jhancock" password="S!gnhere">
    <copy srcUrl="https://brewery-ci.googlecode.com/svn/trunk"
      destUrl="https://brewery-ci.googlecode.com/svn/tags/brewery-1.0.0"
	  message="Tag created by jhancock on ${TODAY}" />
  </svn>
</target>

Listing 3은 서브클립스 오픈 소스 프로젝트(다운로드하려면, 참고자료를 참조)가 제공하는 SVN 앤트 태스크를 사용하고 있다. SVN 앤트 태스크는 JAR, svnant.jar, svnClientAdapter.jar, svnjavahl.jar로 제공된다. 이것들은 앤트 스크립트를 실행할 때 반드시 클래스패스에 있어야 한다. Listing 3의 첫 번째 부분은 클래스패스를, 두 번째 부분은 taskdef를 사용하여 SVN 앤트 태스크를 정의한다. 마지막으로, 트렁크에서 tags 디렉터리로 SVN copy를 수행한다. 이때 이 배포에 대한 유일한 식별자로 brewery-1.0.0을 준다.

보안상 실패

처음으로 SVN 서버를 보안 소켓 계층을 거치는 하이퍼 텍스트 프로토콜(HTTPS)로 사용하면, 반드시 보안 확인을 허락해야 한다. 만약 앤트 스크립트에서 처음으로 이런 보안 SVN 서버로 접속을 하면, 충분한 결과 정보없이 실패할 것이다. 따라서 반드시 첫 번째 SVN 커맨드를 명령행에서 실행하여 서버에 접근해야 한다. 그런 다음에는 이 서버에 접속할 때 계속해서 SVN 앤트 스크립트를 작업 환경에서 실행할 수 있다.

Listing 3의 스크립트를 실행하여 새 태그를 만든 다음에는, SVN 저장소가 그림 4와 비슷하게 보여야 한다. 저장소의 최상위 레벨은 (Listing 2에서 만든) tags 디렉터리다. 그 아래에 새로운 태그(디렉터리)가 Listing 3에서 만들어졌다. brewery-1.0.0으로 여기에 트렁크 복사본을 담고 있다.


그림 4. 트렁크 기반으로 태그 만들기
트렁크 기반으로 태그 만들기

서브버전에 있는 태그 내용을 수정하는 것이 가능하지만 절대로 그렇게 하지 않는 것이 좋다.




위로


배포 태그 기반으로 브랜치 만들기

배포 태그 기반으로 브랜치를 만드는 것은 트렁크를 기반으로 태그를 만드는 것과 기술적으로 비슷하다. 둘 다 SVN의 copy 명령어를 사용한다. 태그에 기반을 둔 브랜치를 만들고 싶을 것이다. 태그는 수정될지도 모르는 현재 개발 중인 코드가 아니라 출시한 코드의 복사본이기 때문이다. 그림 5는 1.0.0 배포 태그를 기반으로 1.0.1 브랜치를 만드는 모습을 보여준다.

버전 네이밍

버전 네이밍 스키마를 사람들은 보통 별것 아닌 걸로 생각하지만 여러 프로젝트에서나 한 버전에서 다음 버전으로 넘어갈 때 다른 버전 네이밍 스키마를 사용할 때 조금 성가시다. 다양한 버전 매기기 패턴을 사용할 수 있다. 그 중에서 나중에 배포할 것을 적절하게 다룰 수 있는 유연하면서도 간단한 것 하나를 선택하라. 내가 사용하는 간단한 패턴은 메이저 버전.마이너 버전.패치 버전을 사용한다. 버전 1.1.2는 이런 버전 매기기 패턴을 기반으로 한 것이다. 몇몇 팀은 버전에 빌드 번호를 붙이기도 한다.


그림 5. 1.0.0 배포 태그 기반으로 브랜치 1.0.1 만들기
1.0.0 배포 태그 기반으로 브랜치 1.0.1 만들기

Listing 4는 SVN copy 명령을 SVN 앤트 태스크로 호출하여 brewery-1.0.0 태그에서 branches 위치로 모든 파일을 복사한다.


Listing 4. 배포 태그에서 브랜치를 생성하는 앤트 스크립트
<target name="create-branch-from-tag">
  <svn username="sadams" password="b0stonM@ss">
    <copy srcUrl="https://brewery-ci.googlecode.com/svn/tags/brewery-1.0.0"
      destUrl="https://brewery-ci.googlecode.com/svn/branches/brewery-1.0.1"
	  message="Branch created by sadams on ${TODAY}" />
  </svn>
</target>

Listing 4의 스크립트를 실행한 뒤, SVN 저장소는 그림 6과 비슷하게 보일 것이다.


그림 6. 배포 태그에서 브랜치 만들기
배포 태그에서 브랜치 만들기
트렁크 기억하기

어떤 팀은 모든 개발을 브랜치에서 시작하여 브랜치 개발을 극대화한다. 트렁크에서 상호 배타적으로 개발하는 것이 덜 복잡하며 더 관리하기 편하다는 것을 기억하라. 브랜칭을 하는 이유는 병렬적으로 별도의 개발을 하기 위한 것이다. 여러분이 하는 모든 작업마다 브랜치를 만드는 실수를 하지 말라.

브랜치를 만들 때 항상 태그를 사용하는 것을 기억하고 SVN 앤트 태스크를 사용하면, 반복 작업을 손쉽게 관리할 수 있고 소스 코드의 이전 버전으로 쉽게 돌아갈 수 있다.

브랜치에 CI 실행하기

CI 프로세스는 일반적으로 저장소의 메인라인인 트렁크에 적용한다. 여러 개발자가 브랜치에 수행한 작업의 변경을 통합하고 메인라인에 병합을 확인하려고 브랜치에도 CI 프로세스를 확장하여 적용할 수 있다.

그림 7은 SVN의 위치를 보여준다. 이 허드슨(Hudson) 설정 페이지에서 호출할 앤트 태스크를 정의할 수 있다.


그림 7. 브랜치를 빌드하고 트렁크에서 병합을 확인하는 허드슨 CI 서버
브랜치를 빌드하고 트렁크에서 병합을 확인하는 허드슨 CI 서버

허드슨 같은 CI 서버를 실행하여 병합을 확인하면 개발 주기 중 나중에 발생할 시스템의 잠재적인 병합 충돌을 미리 알 수 있다.




위로


브랜치에서 트렁크로 변경사항 병합하기

브랜치를 만드는 주요 이유 중 하나는 메인라인 개발에 혼란을 막는 것이다. 하지만 브랜치에서 작업을 모두 마쳤다면, 그 변경사항은 반드시 트렁크에 다시 병합해야 한다. 그림 8은 버전 1.0.1에서 현재 개발 중인 소프트웨어 1.1.0 버전 메인라인으로 다시 병합하는 것을 보여준다.


그림 8. SVN 타임라인
브랜치를 트렁크에 병합 변환

Listing 5에서, 서브버전의 merge 명령어를 사용하고 있다. svn merge를 병합 목적지 URL과 병합할 대상이 되는 URL을 로컬 디렉터리 위치와 함께 실행하고 있다.


Listing 5. SVN의 merge 명령어를 사용하여 브랜치 개발을 트렁크에 병합하기
$ svn merge https://brewery-ci.googlecode.com/svn/trunk \
 https://brewery-ci.googlecode.com/svn/branches/brewery-1.0.1 \
 /dev/brewery --username pduvall --password password!

SVN 앤트 태스크는 merge 명령어를 제공하지 않는다. 따라서 merge 명령어는 명령행에서 실행해야 한다. 아니면 앤트의 exec 태스크를 사용하여 실행할 수도 있다.

Listing 5의 명령을 실행한 결과로 그림 9와 비슷한 것을 볼 수 있을 것이다.


그림 9. 브랜치를 트렁크에 병합한 결과
브랜치를 트렁크에 병합한 결과

병합이 성공적이라면, Listing 6처럼 변경 사항을 서브버전에 커밋할 필요가 있다. 명령행에서, svn commit을 메시지 설명과 트렁크 SVN URL로 실행할 수 있다.


Listing 6. 병합한 변경사항을 트렁크에 커밋하기
<target name="commit-branch-to-trunk">
  <svn username="gwbush" password="IL0veTHEG00g!e">
    <commit dir="${basedir}"
      message="Committing changes from brewery-1.0.1">
    </commit>
  </svn>
</target>

병합이 힘들어지지 않도록 가능한 자주 브랜치에서 트렁크로 변경사항을 병합하라. 그렇게 해야 별도의 코드라인이 너무 오래 동안 커지지 않는다.




위로


브랜치를 기반으로 태그 만들기

특정 브랜치를 기반으로 배포를 준비한다면 SVN 태그를 만든다. 앞서 살펴봤던 것과 비슷한 접근 방법을 취한다. 그림 10은brewery-1.0.1 브랜치를 기반으로 한 brewery-1.0.1 태그 생성을 보여준다.


그림 10. 브랜치를 기반으로 태그 만들기
브랜치를 기반으로 태그 만들기

특정 브랜치에서 개발이 종료될 때, 서브버전에 태그를 남길 필요가 있다. Listing 7은 브랜치를 기반으로 태그를 만드는 예제를 보여준다.


Listing 7. 브랜치를 기반으로 SVN 태그 만들기
<svn username="jbartlett" password="newHampsh!re">
  <copy srcUrl="https://brewery-ci.googlecode.com/svn/branches/brewery-1.0.1"
    destUrl="https://brewery-ci.googlecode.com/svn/tags/brewery-1.0.1"
    message="Branch created by jbartlett on ${TODAY}" />
</svn>

특정 브랜치를 기반으로 태그를 만들어 두면 개발 주기 나중에 이 버전으로 다시 돌아올 수 있다.




위로


병렬 개발 잘 수행하기

병렬로 개발하는 것은 뇌 수술이 아니다. 하지만 프로젝트의 요구를 기반으로 한 계획과 지속적인 개선 없이는 관리하는 것이 매우 힘들어진다. 딱 한 가지만 기억해야 한다면, 모든 길이 결국에는 메인라인으로 돌아와야 한다는 것을 기억하라. 브랜치를 메인라인 개발을 방해할 수도 있는 소스 코드를 위한 임시 홈으로 여기라. 마지막으로 유념해야 할 것은 병합 테스트를 조기에 자주 하라는 것이다. 아마도 서브버전보다 병렬 개발을 더 잘 지원하는 버전 관리 시스템이 있을지도 모른다. 하지만 내 경험상 개발을 할 때 팀에 익숙한 정책을 세우는 것이 기술이 문제를 어떻게 해결하는가보다 훨씬 더 중요했다.



참고자료

교육

제품 및 기술 얻기
  • 서브버전을 다운로드하여 소스 코드 버전을 관리하라.

  • 앤트를 다운로드하고 주기적이고 반복적으로 소프트웨어를 빌드하라.

  • SVN 앤트 태스크: 서브버전을 사용할 수 있도록 앤트 태스크를 다운로드하여 반복적인 프로세스로 소스 변경을 관리하라.

  • 허드슨: 지속적인 통합 서버를 다운로드하여 서브버전의 모든 변경 사항을 빌드로 실행하라.


토론


필자소개

Paul Duvall

Paul Duball은 Stelligent Incorporated의 CTO로, 개발팀이 배포 준비가 된 소프트웨어를 도출할 수 있도록 돕는 애자일 컨설팅을 하고 있다. Paul Duball은 Addison-Wesley 시그너처 시리즈 책인, Continuous Integration: Improving Software Quality and Reducing Risk(Addison-Wesley Professional, 2007년, 2008 졸트 상 수상작)의 공동 저자이기도 하다. 또 UML 2 Toolkit(Wiley, 2003년)과 No Fluff Just Stuff Anthology(Programatic Programmers, 2007년)에도 기여했다.




기사에 대한 평가


보다 나은 서비스를 제공하기 위함이오니 잠시 짬을 내어 이 양식을 제출하여 주십시오.



 


 


 


이 문서 북마킹 하기

mar.gar.in mar.gar.in naver naver eolin eolin del.icio.us del.icio.us





위로


Java and all Java-based trademarks are trademarks of Sun Microsystems, Inc. in the United States, other countries, or both. Microsoft, Windows, Windows NT, and the Windows logo are trademarks of Microsoft Corporation in the United States, other countries, or both. 기타 회사, 제품, 및 서비스명은 다른 상표나 서비스 마크일 수 있습니다.

developerWorks 콘텐트를 다른 사이트에 전재하기:
developerWorks 콘텐트에 대한 저작권은 IBM에 있습니다. IBM의 서면 허가나 원본 저자의 허락이 없이는 전재를 금합니다. 저희 콘텐트를 전재하시려면 IBM developerWorks 담당자 에게 문의하십시오.
    IBM 소개 개인정보 보호정책 문의