DeepintoPharo:Chapter 07: Difference between revisions
Onionmixer (talk | contribs) (DIP 제 7 장 자신의 코드를 Monticello로 버전관리하기 페이지 추가) |
Onionmixer (talk | contribs) (이미지 확장자 변경 및 이미지 추가) |
||
(One intermediate revision by the same user not shown) | |||
Line 46: | Line 46: | ||
Monticello는 표준 Pharo 배포판에 포함된다. Monticello Browser는 World 메뉴에서 선택 가능하다. 그림 7.1을 참조하면, Monticello Browser가 두 개의 리스트 패인과 하나의 버튼 패인으로 구성됨을 볼 수 있다. 좌측 패인은 설치된 패키지를 열거하고 우측 패인은 알려진 저장소를 표시한다. 버튼 패인과 두 개의 리스트 패인의 메뉴를 통해 여러 연산을 실행할 수 있다. | Monticello는 표준 Pharo 배포판에 포함된다. Monticello Browser는 World 메뉴에서 선택 가능하다. 그림 7.1을 참조하면, Monticello Browser가 두 개의 리스트 패인과 하나의 버튼 패인으로 구성됨을 볼 수 있다. 좌측 패인은 설치된 패키지를 열거하고 우측 패인은 알려진 저장소를 표시한다. 버튼 패인과 두 개의 리스트 패인의 메뉴를 통해 여러 연산을 실행할 수 있다. | ||
[[image:DeepintoPharo_Image_7-1. | [[image:DeepintoPharo_Image_7-1.jpg|그림 7.1: Monticello Browser.]] | ||
Line 70: | Line 70: | ||
* '''Perfect''' 의 범주, 또는 이름이 '''Perfect-''' 로 시작되는 범주 내 어떤 클래스든 그에 속하는 모든 메서드로, '''*''' 로 이름이 시작되는 프로토콜 내 클래스에 속하는 메서드는 제외된다 (''예:'' 다른 패키지에 속하는). 이는 프로토콜 '''running''' 에 속하기 때문에 우리의 '''testPerfect''' 메서드를 포함한다. | * '''Perfect''' 의 범주, 또는 이름이 '''Perfect-''' 로 시작되는 범주 내 어떤 클래스든 그에 속하는 모든 메서드로, '''*''' 로 이름이 시작되는 프로토콜 내 클래스에 속하는 메서드는 제외된다 (''예:'' 다른 패키지에 속하는). 이는 프로토콜 '''running''' 에 속하기 때문에 우리의 '''testPerfect''' 메서드를 포함한다. | ||
[[image:DeepintoPharo_Image_7-2. | [[image:DeepintoPharo_Image_7-2.jpg|그림 7.2: Perfect 패키지 생성하기.]] | ||
Line 91: | Line 91: | ||
[[image:DeepintoPharo_Image_7-3. | [[image:DeepintoPharo_Image_7-3.jpg|그림 7.3: 패키지의 버전을 저장 시에는 새로운 버전 이름과 커밋(commit) 메시지를 설정할 수 있다.]] | ||
Line 132: | Line 132: | ||
어떤 개발 툴이든 그것이 포함된 패키지에서 코드를 수정하면 패키지를 더럽게 만든다. 즉, 이미지 내 패키지 버전은 저장 또는 로딩된 버전과 다르다는 의미다. | 어떤 개발 툴이든 그것이 포함된 패키지에서 코드를 수정하면 패키지를 더럽게 만든다. 즉, 이미지 내 패키지 버전은 저장 또는 로딩된 버전과 다르다는 의미다. | ||
[[image:DeepintoPharo_Image_7-4. | [[image:DeepintoPharo_Image_7-4.jpg|그림 7.4: Perfect 패키지를 수정하면 "더러워질" 것이다.]] | ||
Line 149: | Line 149: | ||
{{CommentPharo|package-cache 저장소를 선택하고 열면 그림 7.5과 같은 모습이 표시될 것이다.}} | {{CommentPharo|package-cache 저장소를 선택하고 열면 그림 7.5과 같은 모습이 표시될 것이다.}} | ||
[[image:DeepintoPharo_Image_7-5. | [[image:DeepintoPharo_Image_7-5.jpg|그림 7.5: 저장소 인스펙터]] | ||
Line 177: | Line 177: | ||
{{CommentPharo|Monticello 브라우저에서 Perfect 패키지와 그 저장소를 선택하라. 패키지명을 액션 클릭하고 {{HighlightGray|unload package}}를 선택하라.}} | {{CommentPharo|Monticello 브라우저에서 Perfect 패키지와 그 저장소를 선택하라. 패키지명을 액션 클릭하고 {{HighlightGray|unload package}}를 선택하라.}} | ||
[[image:DeepintoPharo_Image_7-6. | [[image:DeepintoPharo_Image_7-6.jpg|그림 7.6: 패키지 언로딩하기.]] | ||
Line 185: | Line 185: | ||
{{CommentPharo|Monticello 브라우저에서, 패키지 패인에서 어떤 내용도 선택하지 않고 저장소 패인에 '''package-cache''' 를 선택한 후 저장소 인스펙터를 {{HighlightBox|Open}} 하라. 아래로 스크롤하여 '''Perfect''' 패키지를 선택하면 일반 인터페이스에 그것이 설치되지 않았음이 표시될 것이다. 이제 패키지의 첫번째 버전을 선택하고 {{HighlightBox|Load}} 하라.}} | {{CommentPharo|Monticello 브라우저에서, 패키지 패인에서 어떤 내용도 선택하지 않고 저장소 패인에 '''package-cache''' 를 선택한 후 저장소 인스펙터를 {{HighlightBox|Open}} 하라. 아래로 스크롤하여 '''Perfect''' 패키지를 선택하면 일반 인터페이스에 그것이 설치되지 않았음이 표시될 것이다. 이제 패키지의 첫번째 버전을 선택하고 {{HighlightBox|Load}} 하라.}} | ||
[[image:DeepintoPharo_Image_7-7. | [[image:DeepintoPharo_Image_7-7.jpg|그림 7.7: 이전(earlier) 버전 로딩하기]] | ||
Line 232: | Line 232: | ||
변경 내용이 저장되지 않았다는 경고를 받을 것이다. | 변경 내용이 저장되지 않았다는 경고를 받을 것이다. | ||
[[image:DeepintoPharo_Image_7-8. | [[image:DeepintoPharo_Image_7-8.jpg|그림 7.8: 저장되지 않은 변경 내용을 알리는 경고.]] | ||
Line 243: | Line 243: | ||
{{CommentPharo|저장소 인스펙터가 아직 열려 있다면 새 버전으로 Refresh하라 (그림 7.9).}} | {{CommentPharo|저장소 인스펙터가 아직 열려 있다면 새 버전으로 Refresh하라 (그림 7.9).}} | ||
[[image:DeepintoPharo_Image_7-9. | [[image:DeepintoPharo_Image_7-9.jpg|그림 7.9: 두번째 버전과 세번째 버전은 각각 첫번째 버전의 구분된 branch다.]] | ||
Line 254: | Line 254: | ||
그림 7.10의 왼쪽 그림에서 보는 바와 같이 Perfect 패키지의 현재 상황을 고려해보자. 우리는 첫번째 버전을 기반으로 새로운 세번째 버전을 공개했다. 두번째 버전 또한 첫번째 버전을 기반으로 하므로 두번째 버전과 세번째 버전은 독립된 branch가 된다. | 그림 7.10의 왼쪽 그림에서 보는 바와 같이 Perfect 패키지의 현재 상황을 고려해보자. 우리는 첫번째 버전을 기반으로 새로운 세번째 버전을 공개했다. 두번째 버전 또한 첫번째 버전을 기반으로 하므로 두번째 버전과 세번째 버전은 독립된 branch가 된다. | ||
[[image:DeepintoPharo_Image_7-10. | [[image:DeepintoPharo_Image_7-10.jpg|640px|그림 7.10: 브랜칭(왼쪽)과 병합(오른쪽)]] | ||
이 시점에서 우리는 두번째 버전에 대한 변경 내용을 세번째 버전의 변경 내용과 병합하면 좋겠다고 생각한다. 세번째 버전이 현재 로딩되어 있기 때문에 두번째 버전의 변경 내용으로 병합하여 그림 7.10에서 오른쪽 그림과 같이 병합된 네번째 버전을 공개하고자 한다. | 이 시점에서 우리는 두번째 버전에 대한 변경 내용을 세번째 버전의 변경 내용과 병합하면 좋겠다고 생각한다. 세번째 버전이 현재 로딩되어 있기 때문에 두번째 버전의 변경 내용으로 병합하여 그림 7.10에서 오른쪽 그림과 같이 병합된 네번째 버전을 공개하고자 한다. | ||
[[image:DeepintoPharo_Image_7-11. | [[image:DeepintoPharo_Image_7-11.jpg|그림 7.11: 병합되어야 할 구분된 branch(굵은 글씨) 선택하기.]] | ||
Line 267: | Line 267: | ||
병합 툴은 정밀한 버전 병합을 허용한다. 병합될 패키지에 포함된 요소들은 상단 텍스트 패인에 열거된다. 하단 텍스트 패인은 선택된 요소의 정의를 표시한다. | 병합 툴은 정밀한 버전 병합을 허용한다. 병합될 패키지에 포함된 요소들은 상단 텍스트 패인에 열거된다. 하단 텍스트 패인은 선택된 요소의 정의를 표시한다. | ||
[[image:DeepintoPharo_Image_7-12. | [[image:DeepintoPharo_Image_7-12.jpg|640px|그림 7.12: 현재 세번째 버전과 병합 중인 Perfect 패키지의 두번째 버전.]] | ||
Line 282: | Line 282: | ||
'''오른쪽 화살표 = 저장소가 현재 버전을 대체한다.''' 오른쪽 화살표가 표시된 요소가 사용되고 이미지 내 현재 버전을 대체할 것이다. 그림 7.12에서 두번째 버전의 Integer>>isPerfect 가 사용되었음을 볼 수 있다. | '''오른쪽 화살표 = 저장소가 현재 버전을 대체한다.''' 오른쪽 화살표가 표시된 요소가 사용되고 이미지 내 현재 버전을 대체할 것이다. 그림 7.12에서 두번째 버전의 Integer>>isPerfect 가 사용되었음을 볼 수 있다. | ||
'''왼쪽 화살표 = 저장소 버전이 거부되었다.''' 왼쪽 화살표가 표시된 요소는 거부되고, 로컬 정의는 대체되지 않을 것이다. 그림 7.12는 | '''왼쪽 화살표 = 저장소 버전이 거부되었다.''' 왼쪽 화살표가 표시된 요소는 거부되고, 로컬 정의는 대체되지 않을 것이다. 그림 7.12는 두번째 버전의 Integer>>divisors 가 거부되었기 때문에 세번째 버전의 정의가 그대로 유지될 것을 보여준다. | ||
Line 288: | Line 288: | ||
지금 저장소 인스펙터를 새로고침하면, 굵은 글씨체로 표시된 버전이 더 이상 없음을 눈치챌 것인데, 이는 모든 버전이 현재 로딩된 | 지금 저장소 인스펙터를 새로고침하면, 굵은 글씨체로 표시된 버전이 더 이상 없음을 눈치챌 것인데, 이는 모든 버전이 현재 로딩된 네번째 버전의 조상이라는 의미다 (그림 7.13). | ||
Line 296: | Line 296: | ||
Monticello에는 그 외에 유용한 기능들이 많다. 그림 7.1에서 볼 수 있듯이 Monticello 브라우저 창에는 8개의 버튼이 위치한다. 그 중 지금까지 4개-{{HighlightBox|+Package}}, {{HighlightBox|Save}}, {{HighlightBox|+Repository}}, {{HighlightBox|Open}} 버튼을 사용해보았다. 이제 저장소의 상태와 내역을 살펴보기 위해 {{HighlightBox|Browse}} 와 {{HighlightBox|Changes}} 버튼을 살펴보겠다. | Monticello에는 그 외에 유용한 기능들이 많다. 그림 7.1에서 볼 수 있듯이 Monticello 브라우저 창에는 8개의 버튼이 위치한다. 그 중 지금까지 4개-{{HighlightBox|+Package}}, {{HighlightBox|Save}}, {{HighlightBox|+Repository}}, {{HighlightBox|Open}} 버튼을 사용해보았다. 이제 저장소의 상태와 내역을 살펴보기 위해 {{HighlightBox|Browse}} 와 {{HighlightBox|Changes}} 버튼을 살펴보겠다. | ||
[[image:DeepintoPharo_Image_7-13. | [[image:DeepintoPharo_Image_7-13.jpg|그림 7.13: 모든 오래된 버전들은 현재 병합되어있는 네번째 버전의 조상들이다.]] | ||
Line 307: | Line 307: | ||
{{CommentPharo|Perfect 패키지를 선택하고 {{HighlightBox|Browse}} 버튼을 클릭하라.}} | {{CommentPharo|Perfect 패키지를 선택하고 {{HighlightBox|Browse}} 버튼을 클릭하라.}} | ||
[[image:DeepintoPharo_Image_7-14. | [[image:DeepintoPharo_Image_7-14.jpg|그림 7.14: 스냅샷 브라우저는 Perfect 패키지가 2개의 메서드가 있는 Integer 클래스를 확장함을 보여준다.]] | ||
Line 336: | Line 336: | ||
[[image:DeepintoPharo_Image_7-15. | [[image:DeepintoPharo_Image_7-15.jpg|640px|그림 7.15: 패치 브라우저는 이미지 내 코드와 가장 최근에 적용된 버전의 차이를 표시한다.]] | ||
Line 354: | Line 354: | ||
{{CommentPharo|Perfect 패키지를 선택하고 오른쪽 마우스를 클릭한 후 {{HighlightGray|History}} 항목을 선택하라.}} | {{CommentPharo|Perfect 패키지를 선택하고 오른쪽 마우스를 클릭한 후 {{HighlightGray|History}} 항목을 선택하라.}} | ||
[[image:DeepintoPharo_Image_7-16. | [[image:DeepintoPharo_Image_7-16.jpg|그림 7.16: 버전 히스토리 뷰어는 패키지의 다양한 버전에 관한 정보를 제공한다.]] | ||
Line 374: | Line 374: | ||
그림 7.17은 Pier에서 이것이 어떻게 작용하는지를 보여준다. Pier-All 패키지는 일종의 우산 역할을 하는 빈 페키지다. 이는 Pier-Blog, Pier-Caching과 다른 모든 Pier 패키지들을 필요로 한다. | 그림 7.17은 Pier에서 이것이 어떻게 작용하는지를 보여준다. Pier-All 패키지는 일종의 우산 역할을 하는 빈 페키지다. 이는 Pier-Blog, Pier-Caching과 다른 모든 Pier 패키지들을 필요로 한다. | ||
[[image:DeepintoPharo_Image_7-17. | [[image:DeepintoPharo_Image_7-17.jpg|640px|그림 7.17: Pier에서 의존성.]] | ||
Line 385: | Line 385: | ||
{{CommentPharo|아래 단계를 따라하라.}} | {{CommentPharo|아래 단계를 따라하라.}} | ||
* ''패키지 캐시에서 '''Perfect''' 패키지의 | * ''패키지 캐시에서 '''Perfect''' 패키지의 네번째 버전을 로딩하라.'' | ||
* ''브라우저에 '''NewPerfect-Tests''' 라는 새 패키지를 생성하고 해당 패키지로 '''PerfectTest''' 를 드래그하라.'' | * ''브라우저에 '''NewPerfect-Tests''' 라는 새 패키지를 생성하고 해당 패키지로 '''PerfectTest''' 를 드래그하라.'' | ||
* '''''Integer''' 클래스의 '''*perfect''' 프로토콜을 '''*newperfect-extensions'''(재명명은 Action 클릭을 통해)로 재명명하라.'' | * '''''Integer''' 클래스의 '''*perfect''' 프로토콜을 '''*newperfect-extensions'''(재명명은 Action 클릭을 통해)로 재명명하라.'' | ||
Line 398: | Line 398: | ||
Monticello 브라우저에서 '''NewPerfect-All''' 을 선택하면 의존하는 패키지들이 '''굵은 글씨체''' 로 표시된다 (그림 7.18 참고). | Monticello 브라우저에서 '''NewPerfect-All''' 을 선택하면 의존하는 패키지들이 '''굵은 글씨체''' 로 표시된다 (그림 7.18 참고). | ||
[[image:DeepintoPharo_Image_7-18. | [[image:DeepintoPharo_Image_7-18.jpg|그림 7.18: NewPerfect-All 은 NewPerfect-Extensions와 NewPerfect-Tests 를 필요로 한다.]] | ||
Line 523: | Line 523: | ||
SmalltalkHub는 온라인 저장소로서, 자신의 Monticello 패키지를 보관하는 데에 사용할 수 있다. 인스턴스는 http://smalltalkhub.com/에서 실행 및 접근 가능하다. | SmalltalkHub는 온라인 저장소로서, 자신의 Monticello 패키지를 보관하는 데에 사용할 수 있다. 인스턴스는 http://smalltalkhub.com/에서 실행 및 접근 가능하다. | ||
[[image:DeepintoPharo_Image_7-19. | [[image:DeepintoPharo_Image_7-19.jpg|640px|그림 7.19: SmalltalkHub, 온라인 Monticello 코드 저장소]] | ||
Line 541: | Line 541: | ||
SmalltalkHub 계정이 없다면 SmalltalkHub 홈 페이지에서 <u>Join</u> 링크를 누르는 일이 첫 단계가 될 것이다. 회원이 되면 <u>+New Project</u> 를 이용 시 새 프로젝트를 생성할 수 있다. | SmalltalkHub 계정이 없다면 SmalltalkHub 홈 페이지에서 <u>Join</u> 링크를 누르는 일이 첫 단계가 될 것이다. 회원이 되면 <u>+New Project</u> 를 이용 시 새 프로젝트를 생성할 수 있다. | ||
[[image:DeepintoPharo_Image_7-20. | [[image:DeepintoPharo_Image_7-20.jpg|640px|그림 7.20: SmalltalkHub에서 저장소를 설정 가능하다.]] | ||
Latest revision as of 09:55, 30 March 2014
- 제 7 장 자신의 코드를 Monticello로 버전관리하기
자신의 코드를 Monticello로 버전관리하기
- Oscar Nierstrasz 와 공동 작성 (oscar.nierstrasz@acm.org)
버전관리 시스템은 코드의 여러 버전을 보관하고 로그하도록 도와준다. 게다가 공통 소스 코드 저장소로 동시 접근을 관리하도록 도와주기도 한다. 또 문서 집합에 일어나는 모든 변경사항을 추적하고, 여러 개발자들의 협력을 가능하게 한다. 소프트웨어 크기가 몇 개의 클래스 이상으로 증가하는 즉시 버전관리 시스템이 필요할 것이다.
여러 다른 버전관리 시스템이 이용 가능하다. CVS[1], Subversion[2], Git[3]가 아마도 가장 유명할 것이다. 원칙상 이들은 Pharo 소프트웨어 프로젝트의 개발을 관리하는 데에 사용할 수 있지만 그러한 실습은 버전관리 시스템을 Pharo 환경에서 분리시킬 것이다. 뿐만 아니라 CVS와 같은 툴은 일반 텍스트 파일만 버저닝하고 개별적인 패키지, 클래스 또는 메서드는 버저닝하지 않는다. 따라서 적절한 세분성(granularity) 수준에서 변경 내용을 추적하는 능력이 우리에게 부족할 것이다. 만일 당신이 일반 텍스트 대신 클래스나 메서드를 보관한다는 사실을 버전관리 툴이 알고 있다면 개발 과정을 더 잘 지원할 수 있을 것이다.
프로젝트를 보관하는 저장소에는 여러 가지가 있다. SmalltalkHub[4]와 Squeaksource 3[5]은 두 가지 무료로 이용 가능한 대표적인 저장소다. 이들은 텍스트의 행 대신 클래스와 메서드가 변경의 단위인 Pharo를 위한 버전관리 시스템들이다. 이번 장에서는 SmalltalkHub 를 이용하겠지만 Squeaksource 3도 이와 동일하게 이용 가능하다. SmalltalkHub 는 SourceForge와 같고, Monticello는 CVS와 같다.
본 장을 통해 당신은 Monticello와 SmalltalkHub 를 이용해 자신의 소프트웨어를 관리하는 방법을 학습할 것이다. 앞의 여러 장에 걸쳐 Monticello는 간략하게 살펴보았으나[6], 이번 장에서는 Monticello를 세부적으로 살펴보고, 큰 애플리케이션을 버저닝하는 데에 유용한 추가 기능들을 몇 가지 설명하겠다.
기본 사용
패키지를 생성하고 변경 내용을 적용하는 기본 내용을 검토하는 것으로 시작해 업데이트와 변경 내용을 병합하는 방법을 살펴볼 것이다.
예제 실행하기 - 완전수(perfect numbers)
이번 장에서는 Monticello의 기능을 설명하기 위해 완전수[7]를 실행하는 작은 예제를 사용하겠다. 몇 가지 간단한 테스트를 정의함으로써 프로젝트를 시작하겠다.
Perfect 패키지에 PerfectTest 라 불리는 TestCase 의 서브클래스를 정의하고, running 프로토콜 내 아래 테스트 메서드를 정의하라.
PerfectTest>>testPerfect
self assert: 6 isPerfect.
self assert: 7 isPerfect not.
self assert: 28 isPerfect.
물론 정수에 대한 isPerfect 메서드를 구현하지 않았기 때문에 위의 테스트들은 실패할 것이다. 해당 코드를 수정하고 확장하는 동안 Monticello의 제어 하에 두고자 한다.
Monticello 시작하기
Monticello는 표준 Pharo 배포판에 포함된다. Monticello Browser는 World 메뉴에서 선택 가능하다. 그림 7.1을 참조하면, Monticello Browser가 두 개의 리스트 패인과 하나의 버튼 패인으로 구성됨을 볼 수 있다. 좌측 패인은 설치된 패키지를 열거하고 우측 패인은 알려진 저장소를 표시한다. 버튼 패인과 두 개의 리스트 패인의 메뉴를 통해 여러 연산을 실행할 수 있다.
패키지 생성하기
Monticello는 패키지의 버전들을 관리한다. 패키지는 근본적으로 클래스와 메서드의 명명된 집합이다. 사실 패키지는 하나의 객체, PackageInfo의 인스턴스로서, 자신에게 속한 메서드와 클래스를 식별하는 방법을 알고 있다.
우리의 PerfectText 클래스를 버저닝하고자 한다. 올바른 방법은 Perfect라고 불리는 패키지를 정의하는 것으로, 해당 패키지는 PerfectTest와 그 외의 관련된 클래스 및 메서드를 모두 포함하는데, 이와 관련해서는 추후에 소개하겠다. 현재로선 그러한 패키지가 전혀 존재하지 않는다. 단지 Perfect(이름이 우연의 일치는 아니다)라고 불리는 범주를 갖고 있다. Monticello는 우리를 위해 범주를 패키지로 매핑할 것이므로 이는 완벽하다.
Monticello 브라우저에서 +Package 를 누르고 Perfect 를 입력하라.
짜잔! 이제 Perfect Monticello 패키지가 생성되었다.
Monticello 패키지는 클래스와 메서드 범주를 대상으로 한 중요한 명명 규칙을 많이 따른다. Perfect 라고 명명된 우리의 새 패키지는 아래를 포함한다.
- Perfect 범주 내 또는 이름이 Perfect- 로 시작되는 범주 내 모든 클래스. 현재로선 우리의 PerfectTest 클래스만 포함된다.
- perfect 또는 *Perfect 로 명명된 프로토콜이나, 이름이 *perfect- 또는 *Perfect- 로 시작되는 프로토콜에서 정의된 어떤 클래스든 (어떤 범주든) 그에 속하는 모든 메서드. 그러한 메서드는 확장(extensions)이라고 한다. 아직 우리에겐 이러한 메서드가 없지만 조만간 정의하게 될 것이다.
- Perfect 의 범주, 또는 이름이 Perfect- 로 시작되는 범주 내 어떤 클래스든 그에 속하는 모든 메서드로, * 로 이름이 시작되는 프로토콜 내 클래스에 속하는 메서드는 제외된다 (예: 다른 패키지에 속하는). 이는 프로토콜 running 에 속하기 때문에 우리의 testPerfect 메서드를 포함한다.
변경 적용하기
그림 7.2 에서 Save 버튼은 비활성화(회색 처리)되었음을 주목하라.
우리의 Perfect 패키지를 저장하기 전에 먼저 어디에 이것을 저장하길 원하는지 명시할 필요가 있다. 저장소는 패키지 컨테이너로, 자신의 머신에 국부적이거나 원격적(네트워크에 걸쳐 접근)이다. 다양한 프로토콜을 이용해 Pharo 이미지와 저장소 간 연결을 구축할 수 있다. 후에 7.5절에서 살펴보겠지만 Monticello는 다양한 저장소 선택을 지원하지만 그 중에서 가장 공통적으로 사용되는 것은 HTTP인데, 그 이유는 SmalltalkHub에서 사용되기 때문이다.
적어도 기본적으로 package-cache라고 불리는 저장소가 마련되어 있는데, Monticello 브라우저(그림 7.1 참고)의 우측에 열거된 저장소 리스트에서 가장 첫 엔트리에 표시될 것이다. package-cache는 Pharo 이미지가 위치한 로컬 디렉터리에 자동으로 생성된다. 이는 당신이 원격 저장소에서 다운로드한 모든 패키지의 복사본을 포함할 것이다. 패키지의 복사본을 원격 서버로 저장하면 이 또한 기본적으로 package-cache에 저장된다.
각 패키지는 그것을 저장할 수 있는 저장소를 안다. 선택된 패키지로 새 저장소를 추가하려면 +Repository 버튼을 누른다. 그러면 HTTP를 포함해 다양한 저장소 유형이 제공될 것이다. 이번 장의 나머지 부분에서는 Monticello의 기능을 탐구하는 데에 필요한 package-cache 저장소를 작업할 것이다.
package cache 로 명명된 디렉터리 저장소를 선택하고 Save를 누른 후, 적절한 로그 메시지를 입력하고 변경 내용을 저장하기 위해 Accept를 눌러라.
Perfect 패키지는 이제 package-cache에 저장되는데, 이는 Pharo 이미지와 동일한 디렉터리에 포함된 하나의 디렉터리에 지나지 않는다. 하지만 다른 유형의 저장소(예: HTTP, FTP, 다른 로컬 디렉터리)를 사용할 경우 당신의 패키지 복사본 또한 package-cache에 보관될 것임을 주목하라.
자신이 가장 좋아하는 파일 브라우저(예: Windows Explorer, Finder 또는 XTerm)를 이용해 파일 Perfect-XX.1.mcz가 패키지 캐시에서 생성되었음을 확인하라. XX는 당신의 이름이나 이니셜에 일치한다.[8]
version은 저장소로 작성된 패키지의 변경 불가한 스냅샷이다. 각 버전은 저장소에서 그것을 식별하는 유일한 버전 번호를 갖는다. 하지만 이 숫자는 전역적으로 유일하진 않음을 주목해야 하는데, 다른 저장소에서 이와 같은 파일 식별자는 다른 스냅샷을 의미하는 수도 있다. 예를 들어, 다른 저장소에서 Perfect-onierstrasz.1.mcz 는 프로젝트의 deployed 버전의 final이 될 수도 있는 것이다! 버전을 저장소로 저장할 때는 그 다음으로 이용 가능한 번호가 자동으로 버전에 할당되지만 원한다면 번호를 수정할 수도 있다. 버전 branch들은 번호 매김 방식을 간섭하지 않음을 (CVS나 Subversion처럼) 명심하라. 나중에 살펴보겠지만 버전은 기본적으로 저장소를 살펴볼 때 버전 번호에 따라 정렬된다.
클래스 확장
우리의 테스트를 초록색으로 만들어줄 메서드를 구현해보자.
아래 두 메서드를 Integer 클래스에 정의하고, 각 메서드를 *perfect라 불리는 프로토콜에 집어 넣자. 새로운 경계 테스트도 추가하라. 테스트가 초록색(green)인지 확인하라.
Integer>>isPerfect
^ self > 1 and: [self divisors sum = self]
Integer>>divisors
^ (1 to: self - 1 ) select: [ :each | (self rem: each) = 0 ]
PerfectTest>>testPerfectBoundary
self assert: 0 isPerfect not.
self assert: 1 isPerfect not.
Integer 에서 메서드는 Perfect 범주에 속하진 않지만, 이름이 * 로 시작되고 패키지명을 매치하기 때문에 Perfect 패키지에 속한다. 그러한 메서드들은 기존 클래스를 확장하기 때문에 클래스 확장으로 알려진다. 이러한 메서드들은 Perfect 패키지를 로딩하는 사람만 이용 가능하다.
"깨끗한" 패키지와 "더러운" 패키지
어떤 개발 툴이든 그것이 포함된 패키지에서 코드를 수정하면 패키지를 더럽게 만든다. 즉, 이미지 내 패키지 버전은 저장 또는 로딩된 버전과 다르다는 의미다.
Monticello 브라우저에서 더러운 패키지는 이름 앞에 붙은 별표(*)로 확인된다. 이는 변경 내용을 적용하지 않은, 따라서 변경 내용이 손실 되지 않기 위해서 저장소로 저장되어야 하는 패키지를 나타낸다. 더러운 패키지를 저장하면 깨끗해진다.
Browser 와 Changes 버튼이 하는 일을 확인하고 싶다면 눌러보기 바란다. 변경 내용을 Perfect 패키지로 저장하려면 Save 를 이용하라. 패키지가 다시 "깨끗해"졌는지 확인하라.
Repository 인스펙터
저장소 내용은 저장소 인스펙터(repository inspector)를 이용해 살펴볼 수 있는데, 인스펙터는 Monticello 의 Open 버튼을 이용해 시작된다 (그림 7.5).
package-cache 저장소를 선택하고 열면 그림 7.5과 같은 모습이 표시될 것이다.
저장소 내 모든 패키지는 인스펙터의 좌측에 열거된다:
- 밑줄이 그어진 패키지명은 해당 패키지가 이미지 내에 설치되었음을 의미한다.
- 굵은 글씨체로 밑줄이 그어진 패키지명은 패키지가 설치되어 있으나 저장소 내에 더 최신 버전이 존재함을 의미한다.
- 일반 글씨체로 된 패키지명 은 패키지가 이미지 내에 설치되어 있지 않음을 의미한다.
패키지를 선택하면 선택된 패키지의 버전이 우측 패인에 열거된다:
- 밑줄이 그어진 버전 이름은 이미지 내에 해당 버전이 설치되었음을 의미한다.
- 굵은 글씨체의 버전 이름은 해당 버전이 설치된 버전의 조상이 아님을 의미한다. 즉, 이것이 더 최신 버전이거나, 설치된 버전의 다른 branch에 속한다는 말이다.
- 일반 글씨체로 된 버전 이름 은 설치된 최신 버전보다 조금 더 오래된 버전임을 나타낸다.
인스펙터 우측을 액션 클릭하면 여러 가지 정렬 옵션이 열거된 메뉴가 열린다. 메뉴의 unchanged 엔트리는 어떤 특정 정렬이든 취소하고, 저장소에 의해 주어진 정렬을 사용한다.
패키지 로딩, 언로딩, 업데이트하기
현재로선 package-cache 저장소에 두 개의 Perfect 패키지 버전이 안전하게 보관되어 있다. 이제 해당 패키지를 언로딩하고 기존 버전을 로딩하여 업데이트하는 방법을 살펴보겠다.
Monticello 브라우저에서 Perfect 패키지와 그 저장소를 선택하라. 패키지명을 액션 클릭하고 unload package를 선택하라.
이제 Perfect 패키지가 이미지에서 사라졌음을 확인할 수 있을 것이다!
Monticello 브라우저에서, 패키지 패인에서 어떤 내용도 선택하지 않고 저장소 패인에 package-cache 를 선택한 후 저장소 인스펙터를 Open 하라. 아래로 스크롤하여 Perfect 패키지를 선택하면 일반 인터페이스에 그것이 설치되지 않았음이 표시될 것이다. 이제 패키지의 첫번째 버전을 선택하고 Load 하라.
원본(빨간색) 테스트만 로딩되었음을 확인할 수 있을 것이다.
저장소 인스펙터에서 Perfect 패키지의 두 번째 버전을 선택하고 Load 하라. 이제 패키지가 최신 버전으로 업데이트되었다.
이제 다시 테스트는 초록색이 되었을 것이다.
branching
branch는 다른 계열과 독립적으로 존재하지만 충분히 멀리 되돌아보면 공통된 조상 버전을 공유하는 개발 버전의 계열이다.
당신은 패키지를 저장할 때 새 버전 branch를 생성할 수 있다. 브랜칭은 새로운 병렬(parallel) 개발을 목표로 할 때 유용하다. 예를 들어, 기업 내에서 소프트웨어를 관리하는 것이 당신의 일이라고 가정해보자. 어느 날 다른 부서에서 동일한 소프트웨어가 일을 약간만 달리 수행하도록 몇 가지만 수정하여 줄 것을 요청한다고 치자. 이러한 상황을 처리하기 위해서는 첫 번째 branch는 손대지 않고 두면서 수정(tweaks)을 병합하는 프로그램의 두 번째 branch를 생성해야 한다.
저장소 인스펙터에서 Perfect 패키지의 첫번째 버전을 선택한 후 Load 하라. 두번째 버전이 굵은 글씨체로 다시 표시가 되는데, 더 이상 로딩되지 않음을 의미한다 (첫번째 버전의 조상이 아니기 때문이다). 이제 두 개의 Integer 메서드를 구현하여 *perfect 프로토콜에 위치시킨 후, 기존의 PerfectTest 테스트 메서드를 아래와 같이 수정하라.
Integer>>isPerfect
self < 2 ifTrue: [ ^ false ].
^ self divisors sum = self
Integer>>divisors
^ (1 to: self - 1 ) select: [ :each | (self \\ each) = 0]
PerfectTest>>testPerfect
self assert: 2 isPerfect not.
self assert: 6 isPerfect.
self assert: 7 isPerfect not.
self assert: 28 isPerfect.
완전수에 대한 우리 구현이 약간 다르겠지만 다시 테스트는 초록색이 될 것이다.
Perfect 패키지의 두번째 버전의 로딩을 시도해보라.
변경 내용이 저장되지 않았다는 경고를 받을 것이다.
Cancel 을 선택해 새 메서드의 오버라이드를 피하라. 이제 변경 내용을 Save 하라. 로그 메시지를 입력하고 새 버전을 Accept 하라.
축하한다! Perfect 패키지의 새 branch 가 성공적으로 생성되었다.
저장소 인스펙터가 아직 열려 있다면 새 버전으로 Refresh하라 (그림 7.9).
병합하기(merging)
Monticello 브라우저에서 Merge 버튼을 이용해 패키지의 한 버전을 다른 버전과 병합하는 것도 가능하다. 보통은 (i) 오래된 버전으로 작업하고 있음을 발견할 때나, (ii) 이전에 독립적이었던 branch들이 재병합되어야 하는 경우에 이 작업이 필요할 것이다. 두 상황 모두 여러 개발자들이 동일한 패키지를 작업할 때 흔히 겪는 시나리오에 해당한다.
그림 7.10의 왼쪽 그림에서 보는 바와 같이 Perfect 패키지의 현재 상황을 고려해보자. 우리는 첫번째 버전을 기반으로 새로운 세번째 버전을 공개했다. 두번째 버전 또한 첫번째 버전을 기반으로 하므로 두번째 버전과 세번째 버전은 독립된 branch가 된다.
이 시점에서 우리는 두번째 버전에 대한 변경 내용을 세번째 버전의 변경 내용과 병합하면 좋겠다고 생각한다. 세번째 버전이 현재 로딩되어 있기 때문에 두번째 버전의 변경 내용으로 병합하여 그림 7.10에서 오른쪽 그림과 같이 병합된 네번째 버전을 공개하고자 한다.
저장소 브라우저에서 그림 7.11과 같이 두번째 버전을 선택하고 Merge 버튼을 클릭하라.
병합 툴은 정밀한 버전 병합을 허용한다. 병합될 패키지에 포함된 요소들은 상단 텍스트 패인에 열거된다. 하단 텍스트 패인은 선택된 요소의 정의를 표시한다.
그림 7.12을 보면 Perfect 패키지에 대한 두번째 버전과 세번때 버전간의 차이점이 세 가지가 있다. PerfectTest>>testPerfectBoundary 메서드가 새로 나타나고, 표시된 Integer의 메서드 두 개가 변경되었다. 하단 패인에는 Integer>>isPerfect의 소스 코드에 대한 기존 버전과 새 버전이 표시된다. 새 코드는 빨간색으로, 제거된 코드는 파란색 취소선으로 표시되고, 변경되지 않은 코드는 검정색으로 나타난다.
메서드나 클래스는 그 정의가 수정된 경우 서로 충돌한다. 그림 7.12는 Integer 클래스에 두 개의 충돌하는 메서드, isPerfect와 divisors를 보여준다. 충돌하는 패키지 요소는 밑줄, 취소선, 또는 굵은 글씨체로 표시된다. 글씨체 규칙은 아래와 같다.
일반 글씨체 = 충돌 없음. 일반 글씨체는 정의가 충돌하지 않음을 의미한다. 예를 들어 PerfectTest>>testPerfectBoundary 메서드는 기존 메서드와 충돌하지 않으므로 설치할 수 있다.
빨간색 = 메서드가 충돌한다. 제안한 변경 내용을 고수하거나 거부하도록 결정을 내릴 필요가 있다. 제안한 메서드 Integer>>>isPerfect 는 이미지에 있는 기존의 정의와 충돌한다. 충돌은 메서드를 오른쪽 마우스로 클릭하여 Keep current version(현재 버전 유지) 또는 Use incoming version(새로운 버전 사용)을 통해 해결이 가능하다.
오른쪽 화살표 = 저장소가 현재 버전을 대체한다. 오른쪽 화살표가 표시된 요소가 사용되고 이미지 내 현재 버전을 대체할 것이다. 그림 7.12에서 두번째 버전의 Integer>>isPerfect 가 사용되었음을 볼 수 있다.
왼쪽 화살표 = 저장소 버전이 거부되었다. 왼쪽 화살표가 표시된 요소는 거부되고, 로컬 정의는 대체되지 않을 것이다. 그림 7.12는 두번째 버전의 Integer>>divisors 가 거부되었기 때문에 세번째 버전의 정의가 그대로 유지될 것을 보여준다.
Integer>>isPerfect 의 새로운 버전을 사용해 Integer>>divisors 의 현재 버전을 유지하고, Merge 버튼을 클릭하라. 테스트가 모두 초록색인지 확인하라. Perfect 패키지의 새로 병합된 버전을 네번째 버전으로 적용하라.
지금 저장소 인스펙터를 새로고침하면, 굵은 글씨체로 표시된 버전이 더 이상 없음을 눈치챌 것인데, 이는 모든 버전이 현재 로딩된 네번째 버전의 조상이라는 의미다 (그림 7.13).
Monticello 저장소 살펴보기
Monticello에는 그 외에 유용한 기능들이 많다. 그림 7.1에서 볼 수 있듯이 Monticello 브라우저 창에는 8개의 버튼이 위치한다. 그 중 지금까지 4개-+Package, Save, +Repository, Open 버튼을 사용해보았다. 이제 저장소의 상태와 내역을 살펴보기 위해 Browse 와 Changes 버튼을 살펴보겠다.
Browse(탐색하기)
Browse 버튼을 이용하면 패키지의 내용을 표시하기 위해 "스냅샷 브라우저(snapshot browser)"가 열린다. 브라우저보다 스냅샷 브라우저를 사용할 때 이점은 클래스 확장을 표시하는 능력에 있다.
Perfect 패키지를 선택하고 Browse 버튼을 클릭하라.
예를 들자면, 그림 7.14는 Perfect 패키지에 정의된 클래스 확장을 표시한다. (환경이 그에 따라 준비되는 경우) 클래스명이나 메서드명을 액션 클릭할 경우 일반 브라우저를 열 수는 있지만 여기서 코드를 수정할 수는 없음을 주목하라.
패키지의 코드를 공개하기 전에 항상 브라우징하는 것은 훌륭한 실습이 되는데, 자신이 생각하는 내용을 실제로 포함하는지 확신할 수 있기 때문이다.
Changes(변경사항)
Changes 버튼은 이미지 내 코드와 저장소 내 가장 최신 패키지의 차이를 계산한다.
Monticello 브라우저에서 PerfectTest 에 아래와 같은 변경 내용을 적용하여 Changes 버튼을 클릭하라.
PerfectTest>>testPerfect
self assert: 2 isPerfect not.
self assert: 6 isPerfect.
self assert: 7 isPerfect not.
self assert: 496 isPerfect.
PerfectTest>>testPerfectTo1000
self assert: ((1 to: 1000) select: [:each | each isPerfect]) = #(6 28 496)
그림 7.15는 Perfect 패키지가 부분적으로 수정되어 하나의 메서드가 변경되고 하나의 새 메서드가 생성되었음을 보여준다. 늘 그렇듯 변경 내용을 액션 클릭하면 컨텍스트의 연산에 대한 선택권을 제공한다.
고급 주제
이제부터 살펴볼 내용은 몇 가지 고급 주제들로서, 히스토리, 의존성 관리, 구성하기, 클래스 초기화가 포함된다.
히스토리
패키지를 액션 클릭하여 History 항목을 선택하면 선택된 패키지의 각 버전과 함께 적용된 주석을 표시하는 버전 히스토리 뷰어가 열린다 (그림 7.16 참고). Perfect 의 경우 패키지 버전은 좌측에 열거되고, 선택된 버전에 관한 정보는 우측에 표시된다.
Perfect 패키지를 선택하고 오른쪽 마우스를 클릭한 후 History 항목을 선택하라.
특정 버전을 액션 클릭하면 이미지에 로딩된 패키지의 현재 작업 중인 복사본과 관련된 변경 내용을 살펴보거나, 선택된 버전에 관련해 새 히스토리 브라우저를 야기할 수 있다.
의존성
대부분의 애플리케이션은 독립적으로 살아갈 수 없으며, 적절하게 작동하기 위해선 다른 패키지들이 필요하다. 가령 meta-described 내용 관리 시스템인 Pier[9]를 살펴보도록 하자. Pier는 다양한 측면(툴, 문서, 블로그, catch 전략, 보안 등)을 가진 커다란 소프트웨어 조각이다. 각 측면은 구분된 패키지에 의해 구현된다. 대부분의 Pier 패키지들은 독립적으로 사용될 수가 없는데, 다른 패키지에서 정의된 메서드와 클래스를 참조하기 때문이다. Monticello는 주어진 패키지에서 요구되는 패키지를 선언하는 의존성 메커니즘을 제공하여 그것이 올바르게 로딩되도록 보장한다.
근본적으로 의존성 메커니즘은 패키지 자체가 로딩되기 전에 그것이 필요로 하는 모든 패키지가 로딩되도록 보장한다. 요구되는 패키지들 또한 다른 패키지들을 필요로 하기 때문에 이 과정은 의존성 트리에 재귀적으로 적용되어 트리의 leaf들은 그들이 의존하는 branch보다 먼저 로딩되도록 한다. 요구되는 패키지의 새 버전이 확인(check in)될 때마다 그에 의존하는 패키지의 새 버전들은 자동으로 새 버전에 의존할 것이다.
그림 7.17은 Pier에서 이것이 어떻게 작용하는지를 보여준다. Pier-All 패키지는 일종의 우산 역할을 하는 빈 페키지다. 이는 Pier-Blog, Pier-Caching과 다른 모든 Pier 패키지들을 필요로 한다.
이러한 의존성 때문에 Pier-All을 설치 시 다른 모든 Pier 패키지들이 설치되는 결과가 야기된다. 뿐만 아니라 개발 시 저장해야 하는 유일한 패키지는 Pier-All로, 모든 의존적인 더러운 패키지들은 자동으로 저장된다.
실제로 어떻게 작동하는지 살펴보도록 하자. 우리의 Perfect 패키지는 현재 implementation과 함께 테스트를 번들한다. 대신 이것들을 구분된 패키지로 구분하여 implementation을 테스트 없이 로딩할 수 있기를 원한다고 가정하자. 하지만 기본적으로는 모든 것을 로딩하길 원한다.
- 패키지 캐시에서 Perfect 패키지의 네번째 버전을 로딩하라.
- 브라우저에 NewPerfect-Tests 라는 새 패키지를 생성하고 해당 패키지로 PerfectTest 를 드래그하라.
- Integer 클래스의 *perfect 프로토콜을 *newperfect-extensions(재명명은 Action 클릭을 통해)로 재명명하라.
- Monticello 브라우저에서 NewPerfect-All 과 NewPerfect-Extensions 패키지를 추가하라.
- NewPerfect-Extensions 와 NewPerfect-Tests 를 요구되는 패키지로서 NewPerfect-All 에 추가하라(NewPerfect-All에서 액션 클릭).
- package-cache 저장소에 NewPerfect-All 패키지를 저장하라. Monticello는 요구되는 패키지를 저장하기 위해 주석의 입력을 요구할 것이다.
- 세 개의 패키지가 모두 package cache 에 저장되었는지 확인하라.
- Monticello는 Perfect 가 여전히 로딩되었다고 생각한다. 이를 언로딩한 후 저장소 인스펙터로부터 NewPerfect-All 를 로딩하라. 그러면 NewPerfect-Extensions 와 NewPerfect-Tests 가 요구하는 패키지들과 함께 로딩될 것이다.
- 모든 테스트가 실행되는지 확인하라.
Monticello 브라우저에서 NewPerfect-All 을 선택하면 의존하는 패키지들이 굵은 글씨체 로 표시된다 (그림 7.18 참고).
그 이유는 다음과 같다.
- 저장소에서 (package-cache 또는 다른 곳에서) NewPerfect-All 을 로딩 시 같은 저장소에서 NewPerfect-Extensions와 NewPerfect-Tests 가 로딩될 것이다.
- PerfectTests 클래스를 수정할 경우 NewPerfect-Tests와 NewPerfect-All 패키지 둘 다 더러워질 것이다 (NewPerfect-Extensions는 해당하지 않음).
- 변경 내용을 적용하려면 NewPerfect-All 을 저장해야 한다. 이는 NewPerfect-All 의 새 버전을 시작하여 후에 NewPerfect-Tests의 새 버전을 필요로 한다. (이 또한 기존에 수정되지 않은 NewPerfect-Extensions 버전에 의존할 것이다.) NewPerfect-All의 최신 버전을 로딩 시 요구되는 패키지의 최신 버전도 로딩할 것이다.
- 대신 NewPerfect-Tests를 저장하면 NewPerfect-All은 저장되지 않을 것이다. 이는 의존성을 효과적으로 파괴하기 때문에 바람직하지 못하다. 이후 NewPerfect-All의 최신 버전을 로딩할 경우 필요로 하는 패키지들의 최신 버전을 얻을 수 없다. 삼가하라!
패키지들 간 좀 더 유연한 의존성을 빌드하기 위해서는 Metacello 설정(configuration)의 사용을 권한다 (제 9장 참조). +Config 버튼을 이용하면 일종의 설정 구조를 생성한다. 이 때는 의존성만 추가하면 될 일이다.
클래스 초기화
Monticello가 패키지를 이미지로 로딩하면 클래스 측에 initialize 메서드를 정의하는 클래스에게 initialize 메시지가 전송될 것이다. 메시지는 클래스 측에서 해당 메서드를 정의하는 클래스에게만 전송된다. 해당 메서드를 정의하지 않는 클래스는 초기화되지 않을 것이며, 그 슈퍼클래스들 중 하나가 initialize를 정의하더라도 마찬가지다. 주의: initialize 메서드는 패키지를 재로딩한다고 해서 호출되지 않는다!
클래스 초기화를 이용해 원하는 횟수만큼 검사 또는 특수 액션을 실행할 수 있다. 클래스에 새 인스턴스 변수를 추가 시 특히 유용하겠다.
클래스 확장은 클래스로 새 메서드를 추가하는 것으로 엄격히 제한된다. 하지만 때때로 확장 메서드는 새 인스턴스 변수의 존재를 필요로 할 수도 있다.
예를 들어 SUnit의 TestCase 클래스를 확장하여 테스트가 마지막으로 빨간색이었던 히스토리를 기록하는 메서드가 포함되길 원한다고 가정해보자. 어딘가에 정보를 보관해야 하지만 불행히도 인스턴스 변수를 확장의 일부로 정의할 수는 없다.
여기서 해결책은 클래스들 중 하나의 클래스 측에 initialize 메서드를 정의하는 방법이 된다.
TestCaseExtension class>>initialize
(TestCase instVarNames includes: 'lastRedRun')
ifFalse: [TestCase addInstVarName: 'lastRedRun']
우리 패키지가 로딩되면 위의 코드가 평가되고 인스턴스 변수가 존재하지 않을 경우 추가될 것이다. 자신의 패키지에 없는 클래스를 변경하더라도 다른 패키지가 더러워질 것이란 사실을 명심하라. 앞의 예제에서 SUnit 패키지는 TestCase를 포함한다. TestCaseExtension를 설치한 후 SUnit 패키지는 더러워질 것이다.
두 버전으로부터 변경 집합 얻기
Monticello 버전은 하나 또는 그 이상의 패키지에 대한 스냅샷이다. 각 버전은 기본 패키지를 구성하는 클래스 및 메서드 정의의 완전한 집합을 포함한다. 때로는 두 개의 버전으로부터 "패치(patch)"를 얻는 편이 유용하다. 패치는 어떤 버전 A로부터 다른 버전 B로 이동하기 위해 시스템에 필요한 모든 부가적 효과의 집합을 의미한다.
변경 집합(change set)은 시스템 패치를 정의하기 위해 Pharo에 내장된 메커니즘이다. 변경 집합은 시스템에 미치는 전역적인 부가적 효과로 구성되어 있다. 새로운 변경 집합은 Change Sorter 에서 생성 및 편집할 수 있다. 해당 툴은 World▷Tools 엔트리에서 이용 가능하다.
두 개의 Monticello 버전 간 차이는 패키지의 두 번째 버전을 로딩하기 전에 새 변경 집합을 생성하면 쉽게 포착이 가능하다. 예로, Perfect 패키지의 첫번째 버전과 두번째 버전의 차이를 포착해보겠다.
- Monticello 브라우저에서 Perfect 의 첫번째 버전을 로딩하라.
- Change sorter(변경 정렬기)를 열고 새 변경 집합을 생성하라. DiffPerfect 로 명명하자.
- 두번째 버전을 로딩하라.
- Change sorter를 보면 첫번째 버전과 두번째 버전의 차이점이 표시될 것이다. 변경 집합을 액션 클릭하고 file out을 선택하면 변경 집합이 파일시스템에 저장될 것이다. DiffPerfect.X.cs 파일은 이제 당신의 Pharo 이미지 옆에 위치한다.
저장소 종류
Monticello가 지원하는 저장소 종류에는 여러 가지가 있으며, 각각이 다른 특성과 용도를 가진다. 저장소는 읽기만 가능하거나, 쓰기만 가능하거나, 읽기-쓰기가 가능하다. 접근 권한은 전역적으로 정의되거나 특정 사용자에게 (예: SmalltalkHub에서와 같이) 지정할 수 있다.
HTTP. HTTP 저장소는 SmalltalkHumb가 지원하는 저장소 종류라 아마도 가장 많이 사용되는 저장소 종류일 것이다.
HTTP 저장소의 좋은 점은 웹 사이트로부터 특정 버전으로 직접 연계하기가 쉽다는 점이다. HTTP 서버에 약간의 구성만 작업하면 HTTP 저장소는 일반 웹 브라우저, WebDAV 클라이언트 등을 통해 브라우징이 가능하다.
HTTP 저장소는 SmalltalkHub 외에 HTTP 서버와 함께 사용할 수 있다. 예를 들어, 간단한 구성만으로[10] Apache를 제한된 접근 권한을 가진 Monticello 저장소로 전환한다.
"My apache2 install worked as a Monticello repository right out of the box on my
RedHat 7.2 server. For posterity's sake, here's all I had to add to my apache2 config:"
Alias /monticello/ /var/monticello/
<Directory /var/monticello>
DAV on
Options indexes
Order allow,deny
Allow from all
AllowOverride None
# Limit write permission to list of valid users.
<LimitExcept GET PROPFIND OPTIONS REPORT>
AuthName "Authorization Realm"
AuthUserFile /etc/monticello-auth
AuthType Basic
Require valid-user
</LimitExcept>
</Directory>
"This gives a world-readable, authorized-user-writable Monticello repository in
/var/monticello. I created /etc/monticello-auth with htpasswd and off I went.
I love Monticello and look forward to future improvements."
FTP. 이는 HTTP 저장소와 비슷한데, 대신 FTP 서버를 사용한다는 점이 다르다. FTP 서버 또한 제한된 접근 권한을 제공하여 다른 FTP 클라이언트들을 이용해 Monticello 저장소와 같은 대상을 살펴볼 수 있다.
GOODS. 해당 저장소는 버전을 GOODS 객체 데이터베이스에 보관한다. GOODS는 완전 분산형 객체지향 데이터베이스 관리 시스템으로, 능동적인 클라이언트 모델을 사용한[11]. 이는 읽기-쓰기가 가능한 저장소기 때문에 버전을 저장하거나 검색하기에 훌륭한 "작업" 저장소가 된다. GOODS가 제공하는 거래(transaction) 지원, 저널, 복사(replication) 기능 때문에 다수의 클라이언트가 사용하는 대규모 저장소에 적합하다.
Directory(디렉터리). 디렉터리 저장소는 로컬 파일 시스템 내 디렉터리로 버전을 보관한다. 준비하는 데에 약간의 수고만 요하므로 개인(private)적인 프로젝트에 유용하고, 네트워크 연결을 요구하지 않으므로 연결해제된 개발에 유일한 옵션이 된다. 이번 장의 실습에 사용된 package-cache가 이러한 저장소 유형에 해당한다. 디렉터리 저장소 내 버전은 추후 public 또는 공유 저장소로 복사할 수 있다. SmalltalkHub는 주어진 프로젝트로 패키지 버전을 (.mcz 파일) 가져오도록 허용함으로써 이러한 기능을 지원한다. SmalltalkHub 로 로그인하고, 프로젝트를 검색한 후 Import Versions 링크를 클릭하면 된다.
Directory with Subdirectories(하위디렉터리가 있는 디렉터리). "하위디렉터리가 있는 디렉터리"는 "디렉터리"와 매우 유사하지만 이용 가능한 패키지 리스트를 검색 시 하위디렉터리를 살펴본다는 점에서 다르다. 단층(flat) 디렉터리가 모든 패키지 버전을 포함하는 대신 저장소가 하위디렉터리로 계층적으로 구조화된다.
SMTP. SMTP 저장소는 버전을 메일로 전송할 때 유용하다. SMTP 저장소를 생성할 때는 대상 이메일 주소를 명시한다. 이는 가령 패키지의 관리자처럼 다른 개발자의 주소가 되기도 하고 pharo-project와 같은 우편 목록이 되기도 한다. 해당 저장소에 저장된 버전은 모두 이 주소로 메일을 통해 전송된다. SMTP 저장소는 쓰기만 가능하다.
Programmatically adding repositories(계획에 따라 추가하는 저장소). 특별한 목적을 위해 새 저장소를 계획에 따라 추가해야 하는 경우가 있다. 이는 구성 또는 분산된 Monticello 패키지의 거대한 집합을 관리하거나, Monticello 브라우저에서 이용 가능한 엔트리를 단순히 맞춤설정할 때에 발생한다. 예를 들어, 아래 코드 조각은 새 디렉터리 저장소를 계획에 따라 추가한다.
{'/path/to/repositories/project-1/'.
'/path/to/repositories/project-2/'.
'/path/to/repositories/project-3/'. } do:
[ :path |
repo := MCDirectoryRepository new directory:
(path asFileReference).
MCRepositoryGroup default addRepository: repo ].
SmalltalkHub 사용하기
SmalltalkHub는 온라인 저장소로서, 자신의 Monticello 패키지를 보관하는 데에 사용할 수 있다. 인스턴스는 http://smalltalkhub.com/에서 실행 및 접근 가능하다.
웹 브라우저를 이용해 메인 페이지, http://smalltalkhub.com/ 를 방문하라. 프로젝트를 선택하면 아래와 같은 저장소 표현식 이 확인될 것이다.
MCHttpRepository
location: 'http://smalltalkhub.com/mc/PharoExtras/Phexample/main'
user: ''
password: ''
해당 저장소에서 +Repository를 클릭한 후 HTTP를 선택하여 저장소를 Monticello에 추가한다. 프로젝트에 해당하는 URL로 템플릿을 작성하라. 위의 저장소 표현식을 웹 페이지에서 복사해 템플릿에 붙여넣을 수도 있다. 해당 패키지의 새 버전을 적용하는 것이 아니기 때문에 사용자 이름과 비밀번호는 입력할 필요가 없겠다. 저장소를 Open 하고, Phexample 의 최신 버전을 선택한 후 Load 를 클릭하라.
SmalltalkHub 계정이 없다면 SmalltalkHub 홈 페이지에서 Join 링크를 누르는 일이 첫 단계가 될 것이다. 회원이 되면 +New Project 를 이용 시 새 프로젝트를 생성할 수 있다.
SmalltalkHub는 프로젝트 저장소를 설정하기 위한 옵션을 제공한다 (그림 7.20 참고). 태그를 할당하고, 프로젝트에 참여하지 않는 사람들의 접근 권한을 제한하는 (private, public) 라이센스를 선택할 수 있고, 사용자를 프로젝트의 구성원으로 저장할 수도 있다. 프로젝트를 공유하는 팀을 생성할 수도 있다.
.mcz 파일 포맷
버전은 저장소에 이진 파일(binary file)로서 보관된다. 이러한 파일은 .mcz라는 확장자를 가진 파일이라 보통 "mcz 파일"이라고 부른다. mcs 파일은 소스 코드와 다른 메타 데이터를 포함하는 압축(zipped) 파일이기 때문에 "Monticello zip"이란 의미를 지닌다.
소스 코드를 직접 살펴보기 위해 파일의 압축 해제를 시도해 볼 수 있겠지만 최종 사용자는 스스로 이러한 파일을 압축 해제할 필요가 없는 것이 보통이다. 압축 해제 시 아래와 같은 mcs 파일의 멤버(member)를 발견할 것이다.
파일 내용. Mcz 파일은 사실 특정 규칙을 따르는 ZIP 아카이브에 해당한다. 개념적으로 한 버전 당 네 가지를 포함한다.
- Package. 버전은 특정 패키지와 관련이 있다. 각 mcz 파일은 패키지명에 관한 정보를 포함하는 "package"라는 파일을 갖고 있다.
- VersionInfo. 스냅샷에 관한 메타 데이터다. 작성자 이니셜, 스냅샷을 얻은 날짜와 시간, 스냅샷의 조상을 포함한다. 각 mcz 파일은 이러한 정보를 포함하는 "version"이란 멤버를 포함한다.
버전은 소스 코드의 전체 히스토리를 포함하지는 않는다. 이는 특정 시점에 소스 코드의 스냅샷으로서, 스냅샷을 식별하는 UUID와, 그것이 파생된 이전의 모든 스냅샷의 UUID 기록을 포함한다. - Snapshot. Snapshot은 특정 시간에 패키지의 상태에 대한 기록이다. 각 mcz 파일은 "snapshot/"이란 이름의 디렉터리를 포함한다. 해당 디렉터리 내 모든 멤버들은 프로그램 요소의 정의를 포함하는데, 이들을 결합하면 Snapshot이 형성된다. Monticello 최신 버전들은 이 디렉터리에 하나의 멤버, "source.st"만 생성한다.
- Dependencies. 버전은 다른 패키지의 특정 버전에 의존할 수 있다. Mcz 파일에는 각 의존성마다 하나의 멤버가 존재하는 "dependencies/" 디렉터리가 포함되어 있다. 이러한 멤버들은 Monticello 패키지가 의존하는 패키지의 이름을 따서 명명될 것이다. 예를 들어, Pier-All mcz 파일은 그 dependencies 디렉터리에 Pier-Blog와 Pier-Caching으로 명명된 파일들을 포함할 것이다.
소스 코드 인코딩. "snapshot/source.st"로 명명된 멤버는 패키지에 속하는 코드의 표준 fileout을 포함한다.
메타데이터 인코딩. 그 외 zip 아카이브의 멤버들은 S-표현식을 이용해 인코딩된다. 개념상 표현식은 nestable dictionary를 표현한다. 목록에서 각 요소 쌍은 키와 값을 나타낸다. 예를 들어, 아래는 AA로 명명된 패키지에서 "version" 파일을 발췌한 것이다:
(name 'AA-ab.3' message 'empty log message' date '10 January 2008' time '10:31:06 am' author 'ab' ancestors ((name 'AA-ab.2' message...)))
이는 AA-ab.3 버전에 빈 로그 메시지가 있고, ab에 의해 2008년 1월 10일에 생성되었으며, AA-ab.2, ...라는 조상이 있다는 의미다.
요약
본 장에서는 Monticello의 기능을 상세히 설명하였으며, 아래의 요점들을 다루었다.
- Monticello는 Smalltalk 범주와 메서드 프로토콜에 매핑된다. Foo라는 패키지를 Monticello에 추가하면 이는 Foo라는 범주 또는 Foo-로 시작되는 범주 내 모든 클래스를 포함할 것이다. 그러한 범주에 위치한 모든 메서드도 포함할 것이지만 *로 시작하는 프로토콜 내 메서드들은 해당하지 않는다. 마지막으로, 시스템 어디든 *foo 라는 프로토콜 또는 *foo-로 시작되는 프로토콜 내 모든 클래스 확장 메서드들도 포함할 것이다.
- 패키지 내 어떤 메서드나 클래스든 이를 수정할 경우 Monticello에 "dirty"로 표시될 것이며, 저장소로 저장이 가능하다.
- 저장소에는 많은 종류가 있으며, 가장 자주 사용되는 저장소는 SmalltalkHub가 사용하는 것과 같은 HTTP 저장소다.
- 저장된 패키지는 package-cache라는 디렉터리에 국부적으로 보관되는 캐시다.
- Monticello 저장소 인스펙터를 이용해 저장소의 브라우징이 가능하다. 어떤 패키지 버전을 로딩 또는 언로딩할 것인지 선택할 수 있다.
- 새 버전을 최신 버전보다 앞의 것에 해당하는 버전에 기반을 두어 패키지의 새 branch를 생성할 수 있다. 저장소 인스펙터는 패키지들의 계통(ancestry)을 추적하고, 특정 branch에 속하는 버전이 어떤 것인지 알려준다.
- branch는 병합이 가능하다. Monticello는 병합 버전들 간 충돌을 해결하는 데에 양호한 제어 수준을 제공한다. 병합된 버전은 계승된 두 버전을 조상으로 갖는다.
- Monticello는 패키지들 간 의존성을 추적할 수 있다. 필요로 하는 패키지에 대한 의존성을 가진 패키지가 저장되면 해당 패키지의 새 버전이 생성되어 요구되는 모든 패키지들의 최신 버전에 의존한다.
- 자신의 패키지에 포함된 클래스들이 클래스 측 initialize 메서드를 갖는 경우, 패키지를 로딩 시 initialize가 그러한 클래스들로 전송된다. 이러한 메커니즘을 이용해 다양한 검사 또는 스타트 업 액션을 실행할 수 있다. 특히 자신이 정의하는 확장 메서드에 해당하는 클래스로 새 인스턴스 변수를 추가할 때 유용하게 사용된다.
- Monticello는 파일 확장자가 .mcz인 특수 압축 파일로 패키지 버전을 보관한다. Mcz 파일은 당신의 패키지 버전의 전체 소스 코드에 대한 스냅샷 뿐만 아니라 패키지 의존성과 같은 다른 중요한 메타데이터가 들어 있는 파일들도 포함한다.
- 이미지에 mcz 파일을 드래그 앤 드롭하여 빠르게 로딩할 수 있다.
Notes
- ↑ http://www.nongnu.org/cvs
- ↑ http://subversion.tigris.org
- ↑ http://git-scm.com/
- ↑ http://smalltalkhub.com/
- ↑ http://ss3.gemstone.com/
- ↑ "A first application(첫 번째 애플리케이션)"과 "The Pharo programming environment(Pharo 프로그래밍 환경)"
- ↑ 완전수는 Euclid(유클리드 기하학에 나오는 그분 맞다)에 의해 발견되었다. 완전수는 자신의 진약수들의 합이 자신이 되는 양의 정수를 나타낸다. 6=1+2+3이 첫 번째 완전수가 된다.
- ↑ 과거에는 개발자들이 이니셜만 이용해 변경 내용을 로그하는 것이 규칙이었다. 현재는 같은 이니셜을 공유하는 많은 개발자들이 "apblack"이나 "AndrewBlack"과 같이 풀 네임을 기반으로 한 식별자를 이용하는 것을 규칙으로 한다.
- ↑ http://source.lukas-renggli.ch/pier
- ↑ http://www.visoracle.com/squeak/faq/monticello-1.html
- ↑ http://www.garret.ru/goods.html