java.net과 이클립스를 이용한

오픈 소스 자바 어플리케이션 개발

 

Martin Pllu
martinpllu-AT-dev.java.net

소개


이 튜토리얼 시리즈는 java.net에 호스팅 되는 오픈 소스 자바 어플리케이션을 개발하기 위해 이클립스가 어떻게 사용될 수 있는지에 대해 설명하고 있다. 이 글은 자바에 대한 지식은 있지만 이클립스는 다뤄보지 않는 개발자들을 대상으로 한다.


문의 사항 또는 피드백이 있다면 튜토리얼의 포럼을 이용해보길 바란다.


튜토리얼의 1부에서는 이클립스에서 프로젝트를 생성하는 방법에 관해 살펴 보았다. 이번 글에서는 팀 개발을 위해서 CVS와 함께 이클립스를 사용하는 방법에 대하여 알아보도록 하자.




CVS의 소개


CVS (Concurrent Versioning System)는 80년대부터 개발되어 오고 있으며, 오픈 소스 개발을 위한 사실 상의 표준이 되어 버린 버전 제어 도구이다.

CVS는 클라이언트/서버 방식의 시스템이다. 온라인상의 대부분의 CVS 문서는, Unix/Linux 시스템에 설치된 cvs 명령어나 윈도우용 cvsnt 포트와 같은 명령 행 클라이언트에 대한 것들이다. 이클립스는 훌륭한 CVS GUI를 제공하는데다가 Java IDE와 밀접하게 통합되어 있어서 명령행 클라이언트를 이용하는 것보다 편리하다.

먼저 몇 가지 CVS 개념을 살펴보자. 이미 이러한 개념들을 잘 알고 있다면, 다음 절로 이동해도 좋다.

CVS 저장소


CVS 서버는 다수의 모듈(modules)을 포함하고 있는 저장소(repository)를 관리한다. java.net의 저장소의 경우 개별 프로젝트는 자신만의 모듈을 가지고 있다.

모듈은 디렉토리와 함께 버전이 부여된 파일을 포함하고 있다. 버전이 부여된 파일은 하나의 파일에 대한 이력을 나타내며, 다수의 개정 이력(revisions)을 포함한다. 파일에 변경이 가해질 때마다, 새로운 개정본(revision)이 생성된다. .java, .xml, .properties 등의 텍스트 기반 파일의 경우, 각각의 개정본은 이전 개정본에서 개정된 내역만을 포함하며, .exe, .jar, .pdf 등의 이진 파일의 경우, 각각의 개정본은 완전한 파일 내용을 포함하게 된다.

CVS는 또한 개정본 생성자와 개정본에 대한 주석, 그리고 개정본 번호도 함께 기록한다. 개정본 번호들은 자동으로 갱신되는데, 대부분의 경우, 파일의 첫번째 개정본 번호는 1.1이고 이후에는 1.2 등의 순서로 갱신된다.

repository
    moduleA/
          versionedFile1
             (revision number="1.1" user="dave" comment="Created")
             (revision number="1.2" user="mike" comment="New cache implementation")
             (revision number="1.3" user="lucy" comment="Fixed defect D113")
          versionedFile2
            
(revision number="1.1" user="dave" comment="Created")
             (revision number="1.2" user="dave" comment="Updated after feedback from Lucy")

          directory1/
                versionedFile3
                   (revision number="1.1" user="lucy" comment="Initial version")
                ...
    moduleB/
        ...

저장소 접속과 권한


CVS 클라이언트는 다양한 프로토콜을 사용하여 서버에 접속할 수 있다. 가장 일반적으로 사용되는 프로토콜은 다음과 같다:
  • pserver, CVS를 위한 간단한 프로토콜로 java.net을 이용하여 일반적인 작업을 하는 정도에 적합하다.
  • ssh, 추가적인 보안을 위해 SSL을 사용하지만, 보통은 인증서 생성과 같이, 대개 약간의 추가적인 설정 작업이 요구된다.
접근 권한은 모듈 단위로 설정된다. java.net 저장소의 경우, java.net에 로그인한 모든 사용자는 누구라도 모든 프로젝트 모듈들에 대해서 읽기 권한을 갖게 된다. 프로젝트 모듈에 대한 쓰기 권한은 모듈을 등록한 소유주나 프로젝트 개발자들에만 부여된다.

작업을 수행하기 위한 파일 사본 가져오기


저장소에 접속한 이후에는 하나 이상의 모듈을 가져올 수 있다. (역자 주:이를 check out 이라고 한다. 앞으로 보다 분명하게 행위 자체를 명시할 필요가 있는 경우 체크아웃이라고 표현하도록 하겠다.) 체크아웃을 하면 CVS에서 여러분의 컴퓨터로 모듈의 최신 내용이 복사되어, 여러분은 모듈 복사본을 가지고 원하는 작업을 수행할 수 있게 된다.


(저장소의 파일을)동시 수정 가능 잠금 방식(Optimistic locking)

버전 관리 도구에 따라 저장소에 위치한 파일을 한 사람이 가져가는 경우 해당 파일에 대해 잠금(locking)을 적용한다. 잠금 상태에서는 체크아웃 한 사람이 파일을 다시 저장소로 보내기 전까지는 아무도 파일에 대한 수정 작업을 할 수 없게 된다. (역자 주: 저장소로 파일을 복사하는 과정을 체크인이라고 한다.)

이렇게 엄격하게 잠금을 하는 방식의 버전 관리의 단점은, 종종 다른 개발자들의 수정 작업이 끝날 때까지 대기해야 한다는 점이다. 또한 체크아웃하고 나서 체크인하지 않을 경우, 자신도 모르게 수정도 하지 않는 파일을 잠궈 버리게 된다.

CVS는 잠금을 필요로 하지 않는 동시 수정 가능 방식을 사용한다. 개발자들은 CVS의 상충 사항을 발견하는 기능을 통해, CVS에서 가져온 파일을 동시에 수정할 수 있다. 상충되는 사항이 발견될 경우, CVS에서는 이를 해결해주는 도구를 제공한다. 이러한 방식은 위험해 보일 수 있지만, 실제 개발 환경에서는 매우 유용한 방식이다.

아예 상충이 나는 일 자체를 피하고 싶다면 CVS가 제공하는 파일에 대한 작업자 추적 정보를 이용할 수 있다.


동기화(synchronize), 갱신(update), 작업 공간의 수정 사항 반영(commit)


CVS 클라이언트를 사용하여 로컬 파일과 저장소 파일 사이의 동기화를 할 수 있다. 동기화 과정에서 로컬 파일과 CVS의 최신 모듈 사이의 차이점을 볼 수 있다. 이러한 차이들은 다음과 같이 분류 할 수 있다:
  • Incoming changes - 저장소에 변경이 생겼지만 로컬 파일에는 반영이 안된 경우.
  • Outgoing changes - 여러분의 로컬 파일에 변경이 생겼지만 저장소에는 반영이 안된 경우.
CVS 클라이언트에서 저장소의 변경 사항(incoming changes)의 일부 혹은 전부를 반영하여 로컬 파일을 갱신(update)할 수 있다. 모듈에 대한 쓰기 권한을 가지고 있는 경우, 로컬 파일의 변경 사항(outgoing changes) 중 필요한 만큼을 저장소에 반영(commit)하는 것도 가능하다.

상충


CVS는 자동적으로 저장소의 변경 사항과 작업 공간의 변경 사항 가운데서 상충하는 내용을 감지한다. 개발자 A와 B가 동시에 같은 파일을 수정하고, A가 먼저 이를 저장소에 반영하고 난 후에 곧바로 B가 자신이 변경한 내용을 저장소에 반영하려고 할 때 이러한 상충이 발생한다.

아래 다이어그램을 보자. 숫자로 된 화살표는 일의 발생 순서를 나타낸다. Alice가 Bob보다 먼저 변경사항을 저장소에 반영하고 Bob이 동기화를 하게 되면 Bob은 상충이 발생했다는 것을 알게 된다


상충되는 변경 사항 해결하기


파일에서 상충되는 변경 사항이 발견 될 경우, 파일을 저장소에 반영하기 전에 상충되는 변경 사항에 대해 병합(merge)을 수행해야 한다. CVS는 한 파일의 서로 다른 행의 변경 사항들에 대한 병합은 자동으로 수행한다. 다만 같은 행에서 변경이 발생할 경우, 개발자가 직접 병합 을 실행해야 한다. 나중에 보겠지만 이크립스는 개발자의 병합을 돕는 매우 훌륭한 GUI를 제공한다.

수정 작업자 추적


CVS에서는 동시 수정 가능 잠금 방식(optimistic locking)을 사용하여 누구라도 CVS 저장소에 접근에 동시에 파일을 수정 가능하지만, 누가 어떤 것을 수정하고 있는지 알아낼 수 있도 있다. 파일 수정 작업을 시작할 경우, 클라이언트가 이 사실을 서버로 알리도록 설정될 수 있다. 만약 누군가 파일을 수정하려고 하면 이미 그 파일이 수정 중이라는 경고를 받을 수 있다. 이것은 수정 사항의 상충을 피하는 유용한 방법이다.

태그


개발할 때 여러분은 종종 다음과 같은 이정표(milestones)들에 도달하게 된다:
  • "기본 시스템은 작동중이고 모든 테스트는 통과 했음"
  • "account 작업 전의 리팩토링"
  • "account 작업 후의 리팩토링"
  • "릴리즈 1.4"
  • "발견된 D113에 대한 수정 완료"
이러한 이정표를 표시해두는 것(marking)은, 변경 사항을 추적하고, 필요한 경우, 이전 상태로 파일들을 되돌릴 수 있도록 보장해주는데 있어 필수적이다.

CVS에서는 모듈에 태그를 붙임으로써 이정표를 표시하게 된다. 태그는 모듈 안에 있는 각각의 파일들에 대한 단일 개정본(single revision)을 취합한다.


태그의 이름은 이정표와 관련있다. ALL_TESTS_PASS와 같은 것은 직관적인 예다.

사용자는 모듈의 태그가 부여된 개정본을 저장소에서 가져올 수 있다. 설령 태그가 삭제되거나 변경되었다 하더라도 태그가 붙었던 버전의 모듈 내용물은 항상 같을 것이다.

분기(branches)와 HEAD


분기(branch)는 일련의 독립적인 개발 흐름을 유지하기 위해 쓰인다. 기본적으로 저장소의 내용과 작업 공간의 내용의 동기화를 위해 update나 commit 명령을 사용하면, HEAD라고 불리는 기본 분기(trunk 라고도 불리운다) 상에서 작업이 이루어진다.

사용자는 필요에 따라 새로운 분기를 생성할 수 있다. 분기는 항상 루트 태그(root tag)라 불리는 태그부터 반드시 시작되어야 하며, 루트 태그의 이름에는 특별한 규칙이 없다.

분기를 사용하는 목적인 대개 공식 배포(release)의 병렬적인 개발을 관리하기 위함이다. 예를 들어 어플리케이션의 1.4 버전을 공식 배포(Release) 했고 계속해서 HEAD 분기에서 2.0 버전을 개발하기 시작했다고 해보자. 만약 치명적인 버그가 1.4 에서 발견되었다는 사용자의 보고가 있다면 RELEASE1-4-0을 루트 태그로 사용하여 분기를 만들 수 있다.


분기 상에서 작업을 시작했을 때, 모듈 내용은 1.4 정식 배포 버전과 완전 동일할 것이다. 이후부터 저장소와의 동기화는 해당 분기에만 적용될 것이다.

태그는 HEAD에서 정의되는 것과 마찬가지로 다른 분기에서도 정의 될 수 있다. 가능하다면 한 수준 정도(one level)의 분기를 고수하는 것이 가장 좋지만, HEAD 이외의 분기에서도 또 다른 분기를 만들 수 있다.

 
태그가 부여된 버전은 변하지 않는다.

모듈의 태그가 부여된 버전에 대해서는 변경 사항을 저장소에 반영할 수 없고, 수정을 하려면 루트 태그를 사용하여 분기를 생성해야 한다.

CVS에서는 분기에 속한 변경 사항들만 저장소에 반영 할 수 있다. HEAD는 기본 분기로 특수한 형태의 분기이다.

 

분기의 병합


필요한 경우에 변경 사항을 특정 분기에서 다른 분기로 병합 할 수 있다. 예를 들어 위 그림에서, 버그를 수정한 1.4.1의 변경 사항을 2.0.0 정식 배포판에 포함시키기 위해서 HEAD 분기로 병합을 할 수 있다. 두 개의 분기 사이의 병합은 저장소와 작업공간의 변경 사항 동기화와 유사하며, 마찬가지로 상충이 발생하는 경우는 이클립스 GUI를 활용하면 좀 더 수월하게 해결할 수 있다.

이력 및 차이점 보기


CVS는 개별 파일이나 파일 조합의 변경 이력을 볼 수 있는 다양한 방법을 제공한다. 또한, CVS를 이용하면 각각의 수정본, 태그 그리고 분기들 사이의 차이를 볼 수 있다.

참고 자료





2부 : 이클립스와 CVS를 이용한 팀 작업



선행 사항


  • 이클립스가 설치 되어 있어야 한다. 1부의 선행 사항을 참조하라.
  • java.net의 CVS 저장소에 연결하기 위해서는 최소한 읽기 권한을 갖는 사용자 계정이 필요하다. 쓰기 권한은 프로젝트 소유주나 개발자들만이 갖는다.
  • 방화벽때문에 원격으로 CVS 저장소에 접근하지 못할 수도 있다 (CVS는 2401 포트를 사용한다).
  • 여러분이 프로젝트의 소유주이며 CVS를 이용하여 프로젝트의 홈페이지를 관리하기 원한다면 프로젝트 preference의 설정을 다음과 같이 바꾼다.

 
  • 이클립스의 preferences를 열고(Window->Preferences), 아래와 같이 CVS 통신 해제 시간을 600초로 설정한다:



여러분이 프로젝트의 소유주이고 아직 CVS에 프로젝트를 공유하지 않았다면 다음 절의 설명을 참조하라. 이미 특정 프로젝트의 개발자이거나 아직 참여하지 않은 프로젝트 내용을 조회할 수 있는 권한만을 갖고자 한다면 CVS 저장소의 프로젝트를 로컬 작업공간에 복사하기를 설명하는 절로 바로 이동해도 된다.

처음으로 프로젝트를 CVS에 등록하기

(프로젝트 소유주만 가능)


지난 튜토리얼의 마지막 부분에서 만든 프로젝트의 이름은 javatools-demo였다. 그리고 약간의 코드와 JUnit 테스트 코드를 작성하였다.



java.net의 CVS 저장소에 접속하여 이 프로젝트를 공유해보자. 먼저 프로젝트를 선택하고, 마우스 오른쪽 버튼을 클릭하고 Team->Share Project...를 선택한다.



Share Project 대화 창에서 저장소의 상세 정보를 입력한다 (아래 표를 보라).




설정 항목

Host
cvs.dev.java.net
Repository path
/cvs
User
java.net의 사용자이름(역자 주:java.net 가입이 필요함)
Password
java.net의 사용자 비밀 번호
Connection type
pserver (추가적인 보안을 위하여ssh 를 선택하는 것도 좋다.
추가적인 설명을 요하는 경우 이 글 을 참고하여라).
Port
그대로 사용한다.
Save Password
비밀번호를 이클립스가 기억하게 할 것인지 결정하라.

표 1 - CVS 연결 설정

"Next"를 클릭한다.



"Next"를 클릭한다.



"HEAD"를 선택하고 "Next"를 클릭한다. 마법사의 다음 페이지는 로컬 프로젝트 내용과 저장소에 있는 프로젝트 내용 사이의 차이점을 모두 보여준다.



프로젝트에서 마우스 오른쪽 버튼을 클릭하고 "Commit"을 선택한다. 이 명령은 이클립스의 모든 로컬 파일을 CVS에 저장한다.



다음 대화창은 CVS에 로컬 파일이 처음으로 추가된다는 것을 알려준다. "Yes"를 선택한다.

이제 첫 수정본들에 대한 주석을 작성할 수 있다. 주석에 대한 작성이 끝나면 "OK"를 클릭한다.



이클립스가 모든 파일을 CVS로 저장 할 것이다.

"Use your project's www/index.html file as the project's description" 박스를 체크했다고 가정하면(선행 사항을 확인할 것.) 저장소의 변경 사항이 나열된 www/index.html 파일을 볼 수 있다. 프로젝트에서 마우스 오른쪽 버튼을 클릭하고 "Update"를 선택하여 CVS로 부터 이 파일을 작업 공간에 복사한다.



모든 것이 끝났다. 이제 여러분의 프로젝트는 이제 CVS와 연결되었다.


프로젝트와 프로젝트 내부의 모든 내용들에 이제 오렌지색 실린더가 표시되어 있다. 이는 CVS와 "연결되었음"을 나타내고 있는 것이다.




CVS 저장소의 프로젝트를 로컬 작업 공간에 복사하기

(프로젝트 개발자나 읽기 권한만을 가진 경우)


여러분이 프로젝트의 소유주이고 앞 절의 설명을 따라했었다면 이 절은 지나쳐도 무방하다.

Java perspective에서, Package Explorer 상에서 마우스 오른쪽 버튼을 클릭하고 "Import..."를 선택한다.



"Checkout Projects from CVS"를 선택하고 "Next"를 누른다.


다음 페이지에서 표 1을 참고하여 CVS 저장소와 사용자 상세 설정을 채워 넣는다.



"Next"를 누른다. 다음 페이지에서, 저장소로부터 복사해 오려는 프로젝트의 이름을 입력한다.


프로젝트의 이름을 모른다면 아래와 같이 "Use an existing module"을 선택한다.

이 목록을 가져오는데 몇 분이 소요 될 수 있으며
, 이 과정 동안 이클립스가 응답 없음으로 변할 수도 있다. 그러므로 위에서와 같이 프로젝트 이름을 직접 입력할 것을 권장한다.



"Next"를 누른다. 다음 대화창에서 "Check out as a project in the workspace"를 선택하고 "Finish"를 누른다.



이제 이클립스가 여러분의 작업공간으로 프로젝트를 복사해올 것이다.



이 작업이 끝나면 작업 공간에서 프로젝트를 확인할 수 있다. 파일과 디렉토리 옆에 오렌지색 실린더가 붙어 있다면 이는 CVS와 연결되어 있다는 것을 나타낸다.


CVS 저장소 서버의 호스트 이름은 프로젝트 옆에 표시된다. 프로젝트의 각 파일은 그 파일이 이진 파일(binary)인지 텍스트 파일(ASCII)인지 표시한다. 대부분의 경우 기본 설정을 그대로 두어도 무방하지만, CVS preferences(Window->Preferences)를 통해 파일 형태에 따른 분류를 조정할 수 있다.

 


이클립스 프로젝트가 아닌 프로젝트를 작업 영역에 복사해오기

java.net의 모든 프로젝트가 이클립스 프로젝트인 것은 아닌다. 이클립스 프로젝트인지 알아보는 가장 빠른 방법은 Package Explorer에 있는 프로젝트 아이콘을 보는 것이다. 만약 파란색 "J" 심볼(javatools-demo처럼)이 있다면 이는 이클립스 프로젝트이다. 그러므로, 이클립스가 제공하는 자바 개발 기능을 바로 사용할 수 있다.

만약 "J" 심볼(아래의 javacc와 같이)이 없다면 불행하게도 이클립스의 자바 개발 기능을 사용하기 위해 몇가지 선행 작업을 해야 한다.



먼저 프로젝트를 선택한 상태에서 마우스 오른쪽 버튼을 클릭한 다음 "Delete"를 선택하여 프로젝트를 삭제한다. 대화 상자가 떴을 때, "Also delete contents under..."를 선택한다.

 


프로젝트를 로컬 작업 공간으로 가져오기 위해 CVS 저장소의 프로젝트를 로컬 작업 공간에 복사하기절의 내용을 다시 수행한다. 이 때 Check Out As 페이지에서 "Check out as a project configured using the New Project Wizard"를 선택한다.



이제 "Finish"를 클릭하면 새로운 프로젝트 마법사가 나타날 것이다 .

"Java Project"를 선택하고 "Next"를 누른다.

 

다음 페이지에서 프로젝트의 이름을 적고 "Finish"를 클릭한다.



이제 Package Explorer에서 프로젝트 내용을 볼 수 있지만 아마도 많은 수의 빨간색 X표시를 볼 게 될 것이다.

 

만약 이러한 상황이 발생한다면, Problems 뷰를 살펴보아라. 아마도 프로젝트의 자바 컴파일 에러를 볼 수 있을 것이다.




새로운 소스 폴더와 새로운 JAR 추가를 위해 프로젝트 클래스 패스를 설정해야 할 것이다. 이 경우, 1부에서 했던 클래스 패스 설정 부분을 수행하면 된다. 이 부분에 대하여는 자세히 다루진 않겠다.

자바 1.5(혹은 5.0) 프로젝트는 Eclipse 3.1을 필요로 한다.
이클립스 다운로드 페이지에서 이를 구할 수 있을 것이다.

복잡한 프로젝트에 있어서는 클래스패스 설정이 상당히 번거로운 일이 될 수 있다. 만약 이것들이 분명하지 않은 경우, 프로젝트 소유주와 클래스패스에 대해서 논의를 해볼 필요가 있다.

이클립스는 .project.classpath 라는 이름을 가진 파일에 프로젝트와 프로젝트의 클래스 패스에 관한 정보를 저장한다. 이 두 파일은 프로젝트의 최상위 폴더에 있다. 만약 여러분이 프로젝트 개발자인 경우, 여러분이 프로젝트의 클래스 패스를 설정하고, 모든 것들을 제대로 컴파일 한 후, 이 두 파일을 CVS에 반영 할 수 있다. 여러분이 개발자가 아니라 하더라도, 소유주에게 이 파일들을 보내어 이들을 CVS 저장소에 반영하거나 다른 방법으로 이들을 사용할 수 있도록 만들어 달라고 부탁할 수도 있다. 다양한 종류의 IDE가 활용되는 프로젝트라면 이들에 대한 고려도 수반되어야 한다.

필자가 보았던 몇몇 프로젝트들에서 CVS가 .project.classpath파일을 무시하도록 설정해두고 있다. 필자는 그 이유를 잘 모르겠다. 만약 누군가 그 이유를 잘알고 있다면 포럼에 의견을 올려 공유해주길 바란다.



저장소와 작업 공간의 동기화 - 변경 내역에 상충되는 것이 없는 경우


이클립스의 리팩토링 도구를 이용하여 org.jtdemo.disply.CurrencyFomatter 클래스의 이름을 org.jtdemo.display.CurrencyForma으로 변경한다고 가정해보자. 이클립스는 자동적으로 연관된 클래스 파일에 바뀐 이름을 적용한다.

Package Explorer에서, Account.javaTextDisplay.java 파일에 '크다' 기호(>)가 붙게 된다. 이것은 로컬 작업 영역에서 변경이 일어났다는 것을 가리킨다. CurrnecyFormat.java물음표(?) 표시가 붙는데, 이는 CVS가 전혀 알지 못하는 새로운 파일이라는 것을 나타낸다.


파일의 이름을 변경한 경우, CVS는 새로운 이름의 파일이 만들어지고, 기존 파일은 삭제된 것으로 간주한다.






프로젝트에서 마우스 오른쪽 버튼을 클릭하여 Team->Synchronise with Repository를 선택한다.



 대화창이 열린다.

 

필자는 보통 "Remember my decision"과 "No"를 선택한다.(역자 주:각자의 기호에 따라 무엇을 선택하더라도 큰 문제는 없다.)

Synchronise 뷰가 나타나게 될 것이다. 이 화면은 저장소에 있는 프로젝트와 작업 영역의 복사본 파일 사이의 차이점을 모두 보여준다.



CVS에 추가할 변경 사항은 다음과 같다.
  • Account.java가 변경되었다.
  • CurrencyFormat.java가 생성되었다.
  • CurrencyFormatter.java가 삭제되었다.
또한 로컬 작업 영역에 반영해야 할 변경 사항들은 다음과 같다.
  • TransactionManager.java가 변경되었다.
  • TransactionTest.java가 생성되었다.
로컬 작업 영역에 반영해야 하는 변경 사항은 누가 수정했는지 살펴보려면, 변경된 파일을 선택하고, 마우스 오른쪽 버튼을 클릭한 후, "Show in Resource History"를 선택한다.




다음의 CVS Resource History 뷰는 spiffy라는 프로젝트의 다른 개발자가 이를 변경했다는 사실을 보여주고 있다.



Synchronise 뷰에서 아무 파일이나 더블 클릭 해보면 로컬 파일과 저장소 파일 사이의 차이점을 볼 수 있다. TransactionManager.java 파일의 차이점을 확인해 보자.

"Java Source Compare"와 "Java Structure Compare" 모두 차이점을 보여준다. 멋지지 않은가!



로컬 작업 공간에 저장소의 변경 사항을 반영해보자. Synchronise 뷰에서, 프로젝트를 선택하고 마우스 오른쪽 버튼을 클릭하여 "Update"를 선택한다. 이렇게 하면 저장소의 모든 변경 사항이 작업 공간에 반영된다.



파일을 전부 받으면 동기화 뷰에서 변경 사항이 사라지게 된다.



변경된 파일들은 이제 작업 공간에 존재한다. TransactionManager.java 파일의 수정본 버전이 1.2가 된 것을 확인할 수 있다.




항상 저장소에 변경 사항을 반영하기 전에 로컬 작업 영역의 파일을 먼저 갱신하라.

항상 저장소에 변경 사항을 반영하기 전에 작업 공간의 파일을 저장소의 최근 변경 사항을 반영하도록 갱신하고, 다시 테스트를 수행 해야 한다. 이러한 절차가 저장소의 최근 변경 사항에 본인의 수정 사항을 정확하게 반영했음을 보장해준다.



이제 작업 영역의 변경 사항을 저장소에 반영한다. Synchronise 뷰에서 프로젝트를 선택하고, 마우스 오른쪽 버튼을 클릭한 후 "Commit"을 선택한다.



CurrencyFormat.java가 아직 CVS에 추가되지 않았다는 경고가 발생한다. "Yes"를 클릭한다.



변경 사항에 대한 주석을 적는다. java.net 저장소는 몇 가지 템플릿과 함께 자주 사용되는 주석들이 설정되어 있다. 이 템플릿은 프로젝트의 온라인 이슈 관리기 안에 있는 관련된 이슈 사항들에 변화가 있을때 사용된다. https://javatools-demo.dev.java.net/servlets/ProjectIssues가 그 예이다.



그러나 프로젝트들은 아직 이슈 추적 단계에 들어가지 않았기 때문에 전부 선택(Ctrl-A) 하고 자유로운 형태로 주석을 덮어쓴다.



Synchronise 뷰는 이제 우리의 작업 영역과 저장소 간에 어떠한 차이점도 존재하지 않는다는 것을 보여주고 있다.



저장소와 작업 공간의 동기화 - 변경 내역에 상충되는 것이 있는 경우


javatools-demo 예제 어플리케이션에는, martinplluspiffy라는 두 명의 개발자가 참여하고 있다.

martinpllu는 방금 막 Transaction 객체가 문자열 형태로 표현되도록 로직을 리팩토링했다. 즉, 이 로직을TextDisplay.update() 메소드로부터 Transaction.toString() 메소드로 옮겼다. 그런 후, 그는 변경된 파일을 저장소에 반영했다.



반면, spiffyCurrencyFormat 파일을 삭제하였고, CurrencyFormat.format() 라는 메소드 호출 코드를 java.text.NumberFormat.getCurrencyInstance().format() 메소드 호출 코드로 바꾸어 버렸다.

spiffy가 작업 영역과 저장소의 동기화를 시도하자, 그는 저장소와 본인의 작업 영역의 파일 사이에서 상충되는 변경 사항이 있음을 발견하였다.



Account.java 파일을 더블 클릭을 하자, toString() 메소드가 로컬의 작업 영역에서 변경되었고, getName() 메소드가 다른 곳에서 추가되어서 CVS 저장소에 반영되었다는 사실을 보여주고 있다.





상충되는 부분이 있는데, 그 변경 사항이 서로 다른 행에 위치할 경우, CVS는 자동으로 변경 사항에 대한 병합을 수행한다.


TextDisplay.java 파일을 더블 클릭하면 update() 메소드가 작업 영역에서도 변경되었고, 저장소에서도 변경되었다는 사실을 보여주고 있다.





상충되는 부분이 있는데, 그 변경 사항이 동일한 행에 위치할 경우, 개발자가 직접 수정 사항에 대한 병합을 수행해야 한다.


spiffy는 프로젝트를 선택하고 마우스 우측 버튼을 클릭하여 "Update"를 선택했다.  그러자, 다음과 같은 경고 상자가 나타났다.



spiffy는 "OK"를 선택했다. 그러자, Account.java에 자동으로 병합이 적용되는 변경 사항뿐만 아니라, CVS 저장소의 모든 변경 사항들이 나타났다.


Account.java 파일의 변경 사항 중에서 저장소에 반영되지 않은 것은 하나(toString() 메소드를 추가한 것)뿐이다. 저장소의 변경 사항은 자동으로 병합되었다.

이제 spiffyTextDisplay.java 파일에서 상충되는 변경 사항만 직접 해결해 주면 된다. Java Source Compare 뷰어에서 Show Ancestor Pane 버튼 을 클릭하면, 변경된 두 개의 파일의 공통적인 원본을 볼 수 있다.



spiffyTransaction.toString() 메소드에 NumberFormat.getCurrencyInstance().format() 을 추가할 필요가 있다는 것을 알게 되어 Transaction.java 파일에 이를 반영하고, "Copy current change from right to left" 버튼 을 클릭하여, TextDisplay.java 파일의 변경 내용을 갱신하였다.

이 작업을 마친 후, spiffy는 TextDisplay.java 파일을 선택하고 "Mark as merged" 메뉴를 선택하였다.

 

TextDisplay.java 파일은 이제 작업 공간에만 변경이 가해진 다른 파일과 동일하게 보인다.



spiffy는 상충을 해결하고 나서 문제가 없음을 확인하기 위해서 관련된 모든 테스트를 수행하였다. 그리고 나서 저장소에 변경사항을 반영하였다.

주기적인 갱신


주기적인 갱신을 통해 상충되는 부분을 최소화하고 작업영역의 코드를 최신으로 유지하는 것이 좋다.

이클립스는 주기적으로 저장소와 작업 공간을 동기화 하는 자동화 기능을 제공한다. 이 때, 저장소의 변경 사항이 자동으로 갱신되지는 않는다 (이는 다소 위험을 초래할 수 있기 때문이다). 이클립스는 단순히 동기화를 실행하고 그 결과를 Synchronise 뷰에 보여준다.

일정에 따른 동기화를 수행하려면, Synchronise 뷰의 메뉴에서 "Schedule..."을 선택한다.



원하는 주기를 선택하고 OK를 클릭한다.



태그


프로젝트에서 오른쪽 마우스 버튼을 클릭하고, Team->Tag as Version 메뉴를 선택하면 프로젝트에 태그를 붙일 수 있다.



태그 이름을 입력한다.



"Move tag if it already exists" 옵션을 선택하면 기존의 태그를 이동시킬 수 있다.

이제 태그를 다양한 방법으로 사용할 수 있다. 예를 들면, 프로젝트에서 오른쪽 마우스 버튼을 클릭하고 Compare With->Another Branch or Version을 선택하면 "Versions" 아래에 태그가 나타난다.



또한 Replace With->Another Branch or Version을 선택하는 방법도 있다. 이렇게 하면 태그가 부여된 버전의 프로젝트 산출물을 작업 공간으로 가져온다:



비어있는 작업 공간으로 태그가 부여된 버전의 프로젝트를 불러오려면, 보통 체크아웃 마법사( checkout wizard)를 이용한다 (Checking out an existing project를 참조하여라). 그러나, 이 경우, 아래에 보여지는 것처럼 태그가 부여된 버전을 선택한다.




태그가 부여된 버전은 변하지 않는다.

만약 태그가 부여된 프로젝트를 작업 공간으로 가져올 경우, 이 프로젝트에 변경을 가할 수 없다는 사실을 명심하여라. 만약 변경 사항을 저장소로 반영하려 하게 되면, 다음과 같은 에러가 발생하는 것을 보게 될 것이다:

 

만약 태그 버전에 따라 변경을 가하고 싶다면, 태그를 루트로 사용하여 분기를 생성할 필요가 있다.




항상 프로젝트 수준에서 태그를 부여하는 것이 좋다.

비록 개별적인 파일, 패키지, 소스 폴더 등에 태그를 붙이는 것도 가능하지만, 항상 프로젝트 수준에서 태그를 부여하여라. 동일한 한 프로젝트 안에서 서로 다른 태그가 부여된 파일들과 폴더들을 섞어서 가져올 경우 혼란을 가져다 줄 수 있다. CVS는 이를 제대로 처리해주지 않는다.


CVS Repository Exploring perspective


"Window->Open Perspective->Other" 메뉴에서 CVS Repository Exploring perspective를 선택한다:



CVS Repositories 뷰는 사용 가능한 CVS 연결들을 보여준다.



저장소 내용들을 살펴보는 방법으로는 네 가지가 있다:



  • HEAD는 각 모듈의 가장 최신 내용을 보여준다.
  • Branches는 각 모듈에 위해 생성되었던 분기들을 보여준다.
  • Versions는 각 모듈의 태그 버전을 보여준다.
  • Dates는 사용자가 설정한 날짜에 지정한 모듈들의 내용을 보여준다.

이 뒤의 내용을 진행해 나가기 전에, CVS 통신 해제 시간을 600초로 설정했는지 확인한다 (선행 사항 부분을 참고한다).


"HEAD"를 펼친다 (프로젝트 목록을 가져오는데 다소 시간이 걸릴 수 있다).



아래로 스크롤 해가면서 자신의 프로젝트를 찾는다. 여기에서 가장 최근의 내용들을 볼 수 있을 것이다.





Branches, Versions 및 Dates 목록에 관한 부분은 여러분의 몫으로 남겨놓도록 하겠다.

분기


javatools-demo 개발자들은 공식 배포판(release) 0.1 빌드를 하나 작성하였고, 이 프로젝트에 V0-1-1이라는 태그를 부여하였다. 이것은 베타 테스트를 위해 배포되었으며, HEAD 상에서는 공식 배포판 0.2를 위한 개발이 진행되고 있다.
베타 테스터들은 급하게 변경을 요청하였다. 이 변경 사항에 대해서는 spiffy에게 맡겨졌다. spiffy는 우선 작업 공간으로 V0-1-1을 불러왔다:



그런 후, spiffy는 Team->Branch를 선택하였다:




새로운 분기에 대한 이름은 branchR0-1이 될 것이다.



spiffy가 OK를 클릭하게 되면, 이 분기는 작업 공간으로 불려지며, spiffy는 이에 대해 작업을 시작할 수 있게 된다.

spiffy가 변경 사항을 저장소에 반영하면, 이 변경 사항은 해당 분기에만 반영된다. 이 분기 상에서의 개발은 HEAD 상에서 진행되는 개발돠는 완전히 분리되어 있다.

변경 사항에 대한 작업 수행, 테스트 및 분기로의 반영이 이루어진 후, 이 어플리케이션은 빌드되고 테스터에게로 넘겨진다. 이 분기에는 V0-1-2이라는 태그를 부여하였는데, 이는 "공식 배포판 1.0의 빌드 2"라는 의미이다.

변경 사항들을 공식 배포판 0.2에 포함시키기 위해서는, 이 분기를 HEAD에 병합해야 할 것이다. spiffy는 먼저 Replace with->Another branch or version에서 HEAD를 선택하고 이를 자신의 작업 공간으로 불러왔다. 그런 후, Team->Merge를 선택하였다:



분기 루트 태그는 V0-1-1이었다.



"from" 분기는 branchR0-1이다:



이 분기는 이제 Synchronise 뷰를 사용하여 HEAD에 병합될 수 있다. 보면 알 수 있겠지만, 분기로부터의 저장소 변경 사항은 두 가지이다:



update를 선택하여 변경 사항들을 가져온다.



이제 변경 사항들은 작업 공간에 존재한다...



... 그런 후, 평소와 같이 변경 사항들이 저장소에 반영될 수 있다.

Watch/Edit를 통해 이력 정보(누가 무엇을 수정하고 있는지에 관한 정보) 추적하기


Introduction to CVS에서 언급했듯이, 누가 무엇을 수정하고 있는지 추적하도록 CVS를 설정할 수 있다.

Window->Preferences를 통한 preferences에서, "Configure projects to use Watch/Edit on checkout"을 선택한다.



프로젝트에 있는 어떠한 파일이라도 변경하게 되면, 변경한 사람은 해당 파일의 수정자(editor)로 등록된다. 이 파일 타인에 의해 변경되어 반영되거나, 해당 파일을 "unedit" (파일을 마우스 우측 버튼을 클릭한 후, Team->Unedit를 선택한다) 하지 않는 한, 해당 파일의 수정자 등록 정보는 그대로 남아있게 된다.

마우스 우측 버튼을 클릭해 Team->Show Editors를 선택하면, 누가 어떠한 파일을 수정하고 있는지 알아볼 수 있다:



만약 다른 누군가가 해당 파일을 수정하려 한다면, 이들은 다음과 같은 메시지를 보게 될 것이다:




이러한 방식 모든 프로젝트의 개발자가 "Configure projects to use Watch/Edit on checkout"을 사용할 경우에만 제대로 적용되어 동작한다.

CVS 웹 인터페이스


java.net에서는 CVS 저장소에 대해 읽기 전용 권한으로 접근할 수 있도록 해주는 웹 인터페이스를 제공하고 있다. 프로젝트 홈페이지에서 Version control - CVS 링크를 통해 이에 접근할 수 있다.




디렉토리들의 안에 들어 있는 내용들을 쭉 살펴볼 수 있다:



한 파일을 클릭하게 되면, 이 파일의 개정본 이력을 볼 수 있다:





HEAD로의 링크 구성

CVS에서 파일의 최신 버전을 항상 가리키도록 링크를 작성할 수 있다. 파이어폭스 브라우저의 경우, "Direct link to HEAD" 링크 상에서 마우스 우측 버튼을 클릭하고 "Copy Link Location"을 선택한다. 인터넷 익스플로러 브라우저의 경우, "Copy Shortcut"을 선택한다. 이는 해당 URL을 클립 보드록 복사한다.

예를 들면, 다음은 Account.java의 가장 최신 버전에 대한 링크이다: https://javatools-demo.dev.java.net/source/browse/javatools-demo/src/org/jtdemo/Account.java?view=markup

프로젝트의 웹 페이지 수정하기


여러분이나 프로젝트 소유주가 "Use your project's www/index.html file as the project's description" 박스(see 선행 사항을 살펴보아라)를 선택했다면, 프로젝트는 www/index.html 파일을 포함하게 된다.



다음은 예제 프로젝트인 https://javatools-demo.dev.java.net의 메인 프로젝트 페이지이다. 만약 이를 수정하고 CVS 저장소로 반영하게 되면, 해당 변경 사항은 즉시 온라인 상에 반영된다.

만약 더 많은 페이지가 필요하다면, www 폴더 아래에 필요한 페이지들을 두고 이들에 대한 링크를 포함시키면 된다:



만약 홈페이지가 복잡하다면, HTML 편집기를 사용하고 싶을 수도 있을 것이다. 이클립스를 위한 몇몇 HTML 편집기 플러그 인 들을 사용하거나 외부 편집기를 사용할 수도 있다 (Mozilla Composer는 매우 훌륭한 WYSIWYG 편집기이다).


외부 편집기 사용하기

만약 외부 편집기를 사용해 프로젝트 파일들을 편집하고 있다면, 이클립스에서 변경된 내용을 감지하기 위해 File->Refresh를 통해 프로젝트를 새로 고침(refresh)해야 한다.

CVS 키워드


키워드들을 사용해 CVS가 파일들에 동적으로 유용한 정보를 추가하도록 만들 수 있다. 이러한 정보의 예로는 마지막 가져온 날짜/시간에 관한 것을 들 수 있다. 자세한 내용은 도구 조언 부분을 참조하여라.

요약


여러분이 이클립스와 CVS 사용법에 대해서 충분히 이해했기를 바란다. 튜토리얼에 대한 질문이나 의견이 있다면 포럼을 이용하길 바란다.

다음 글에서는, 자동 빌드와 테스트 수행을 위해 이클립스에서 Ant와 JUnit을 사용하는 방법에 대해 살펴볼 것이다.

저자와 역자


Martin Pllu: 스코틀랜드 에든버러에 있는 Standard Life Assurance사의 Messaging & Java Infrastructure 팀 멤버이다. java.net에 호스트 되어있는 leafcuttertracetest같은 오픈 소스 어플리케이션의 개발자이기도 하다.


역자: 홍근석, 안영회, 최한수

 

출처 : 자바넷. https://eclipse-tutorial.dev.java.net/eclipse-tutorial/korean/part2.html


Cron & Crontab

 


cron 디먼

목적

명령을 자동으로 수행합니다.

구문

cron

 

설명

cron 디먼은 지정된 날짜와 시간에 쉘 명령을 수행합니다. 다음 이벤트 유형들이 cron 디먼으로 계획됩니다.

  • crontab 명령 이벤트
  • at 명령 이벤트
  • batch 명령 이벤트
  • sync 서브루틴 이벤트
  • ksh 명령 이벤트
  • csh 명령 이벤트

이들 이벤트가 처리되는 방법은 /var/adm/cron/queuedefs 파일에 의해 지정됩니다.

정기적으로 계획된 명령은 crontab 파일에 있는 지침에 따라 지정될 수 있습니다. crontab 명령을 사용하여 crontab 파일을 제출할 수 있습니다. at 명령을 사용하여 한번만 수행될 명령을 제출하십시오. cron 디먼은 절대로 종료하지 않으므로, 한번만 수행되어야 합니다.

cron 디먼은 cron 디먼이 초기설정될 때만 crontab 파일과 at 명령 파일을 시험합니다. crontab 명령을 사용하여 crontab 파일에 변경을 수행할 때, 변경을 나타내는 메세지가 cron 디먼으로 전송됩니다. 이것으로 새로운 또는 변경된 파일에 대해 정기적으로 계획된 간격으로 오버헤드를 점검하지 않아도 됩니다.

chtz 명령, 웹 기반 시스템 관리 응용프로그램으로 또는 SMIT를 통해 TZ 환경 변수를 변경할 때, cron 디먼을 재시작해야 합니다. 이로써 cron 디먼이 새 TZ 환경 변수에 대한 올바른 시간대 및 일광절약시간 변경 정보를 사용할 수 있게 됩니다.

cron 디먼은 /var/adm/cron/log 파일에 활동 로그를 작성합니다.

보안

감사(audit) 이벤트: 감사(auditing) 서브시스템이 적절하게 구성설정되었고 사용 가능한 경우, cron 명령은 실행될 때마다 다음 감사 레코드(이벤트)를 생성합니다.

이벤트 정보
CRON_Start 각 작업의 이름, 작업이 at 또는 cron 명령에 의해 개시되었는지 여부 및 작업을 시작한 시간을 나열합니다.
CRON_Finish 사용자의 이름, 작업의 프로세스 ID 및 처리가 완료된 시간을 나열합니다.

감사 이벤트 선택 및 그룹화하는 방법과 감사 이벤트 데이타 수집 구성설정 방법에 대한 자세한 내용은 AIX Version 4.3 System Management Guide: Operating System and Devices에 있는 "감사 기능 설정"을 참조하십시오.

파일

/var/adm/cron/FIFO crontab 또는 at 명령으로 새 작업이 제출될 때 cron 디먼으로 메세지를 전송하는 명명된 파이프.
/var/adm/cron cron 디먼 디렉토리를 지정합니다.
/var/adm/cron/log 사용통계 정보를 지정합니다.
/var/adm/cron/queuedefs
                          cron 디먼 이벤트 파일을 지정합니다.
/var/spool/cron 스풀 영역을 지정합니다.
/usr cron 디먼에 의해 계속 열려 있는 디렉토리를 나타냅니다.
/usr/bin cron 디먼에 의해 계속 열려 있는 디렉토리를 나타냅니다.
/usr/lib cron 디먼에 의해 계속 열려 있는 디렉토리를 나타냅니다.
/etc cron 디먼에 의해 계속 열려 있는 디렉토리를 나타냅니다.
/tmp cron 디먼에 의해 계속 열려 있는 디렉토리를 나타냅니다.

 


crontab 명령

목적

cron 작업을 제출, 편집, 나열 또는 제거합니다.

구문

crontab -e | -l | -r | -v | File ]

 

설명

crontab 명령은 cron 작업을 제출, 편집, 나열 또는 제거합니다. cron 작업은 cron 디먼이 정기적으로 계획된 간격으로 수행하는 명령입니다. cron 작업을 제출하려면, crontab 명령을 -e 플래그와 함께 지정하십시오. crontab 명령은 편집 세션을 호출하여 crontab 파일을 작성할 수 있게 합니다. 이 파일에 각 cron 작업에 대한 항목을 작성합니다. 각 항목은 cron 디먼에 맞는 양식이어야 합니다. 항목 작성에 대한 내용은 crontab 파일 항목 형식을 .

사용자가 항목 작성을 마치고 파일을 종료할 때, crontab 명령이 그것을 /var/spool/cron/crontabs 디렉토리에 복사한 후, 사용자의 현재 사용자 이름에 대해 명명된 파일에 배치합니다. 사용자 이름을 갖는 파일이 crontabs 디렉토리에 이미 존재하는 경우, crontab 명령은 기존 파일위에 겹쳐씁니다.

다른 방법으로, File 매개변수를 지정하여 crontab 파일을 작성할 수 있습니다. 파일이 존재할 경우, 그 파일은 cron 디먼이 예상하는 형식이어야 합니다. 파일이 없을 경우, crontab 명령이 편집기를 호출합니다. EDITOR 환경 변수가 있을 경우, 명령은 지정하는 편집기를 호출합니다. 그 외에는 crontab 명령이 vi 편집기를 사용합니다.

crontab 파일의 내용을 나열하려면, crontab 명령에 -l 명령을 지정하십시오. 기존 파일을 제거하려면, -r 플래그를 사용하십시오.

cron 디먼

cron 디먼은 crontab 파일 항목에 따라 명령을 수행합니다. 사용자가 cron 작업의 출력을 표준 출력 또는 오류로 재지정하지 않는 한, cron 디먼은 사용자에게 모든 명령 출력 또는 오류를 메일로 전송합니다. crontab 파일에서 cron 작업을 잘못 지정하면, cron 디먼은 작업을 수행하지 않습니다.

cron 디먼은 cron 디먼이 초기설정될 때만 crontab 파일을 시험합니다. crontab 명령을 사용하여 crontab 파일에 변경을 수행할 때, 변경을 나타내는 메세지가 cron 디먼으로 전송됩니다. 이것으로 새로운 또는 변경된 파일에 대해 정기적으로 계획된 간격으로 오버헤드를 점검하지 않아도 됩니다.

crontab 명령 사용에 대한 제어

/var/adm/cron/cron.allow/var/adm/cron/cron.deny 파일은 crontab 명령을 사용할 수 있는 사용자를 제어합니다. 루트 사용자는 이 파일을 작성, 편집 또는 삭제할 수 있습니다. 이 파일의 항목은 한 행에 이름이 한 개 있는 사용자 로그인 이름입니다. 사용자의 로그인 ID가 둘 이상의 로그인 이름과 연관되는 경우, crontab 명령은 사용자가 실제로 사용 중인 로그인 이름과 상관없이 /etc/passwd 파일에 있는 첫번째 로그인 이름을 사용합니다.

다음은 cron.allow 파일의 한 예입니다.

root
nick
dee
sarah

cron.allow 파일이 있을 경우, 로그인 이름이 여기에 나타나는 사용자들만이 crontab 명령을 사용할 수 있습니다. 루트 사용자의 로그 이름이 cron.allow 파일에 반드시 나타나야 합니다. 시스템 관리자는 사용자의 로그인 이름을 cron.deny 파일에 나열하여 그 사용자가 crontab 명령을 사용할 수 없게 할 수 있습니다. cron.deny 파일만이 존재하는 경우, 파일에 나타나지 않는 모든 사용자가 crontab 명령을 사용할 수 있습니다.

다음 중 하나에 해당되는 경우, crontab 명령을 사용할 수 없습니다.

  • cron.allow 파일과 cron.deny 파일이 존재하지 않습니다(루트 사용자만 허용).
  • cron.allow 파일이 존재하지만 사용자의 로그인 이름이 나열되어 있지 않습니다.
  • cron.deny 파일이 존재하고 사용자의 로그인 이름이 나열되어 있습니다.

cron.allowcron.deny 파일이 둘다 존재하지 않는 경우, 루트 사용자 권한을 갖는 사람만이 crontab 명령으로 작업을 제출할 수 있습니다.

crontab 파일 항목 형식

crontab 파일은 각 cron에 대한 항목을 포함합니다. 항목들은 개행 문자로 분리됩니다. 각 crontab 파일 항목은 다음 형태로 공백이나 탭으로 분리된 6개 필드를 포함하고 있습니다.

 
minute(분)  hour(시)  day_of_month(일)  month(월)  weekday(요일)  command(명령)

이들 필드는 다음 값을 승인합니다.

minute(분) 0 - 59
hour(시) 0 - 23
day_of_month(일) 1 - 31
month(월) 1 - 12
weekday(요일) 일요일부터 금요일까지를 나타내는 0 - 6
command(명령) 쉘 명령

각 필드에 대해 반드시 값을 지정해야 합니다. command 필드를 제외하고, 필드 다음에 들어갈 수 있습니다.

  • 지정된 범위에 있는 숫자. 5월에 명령을 수행하려면, 필드에 5를 지정하십시오.
  • 두 수를 대시로 분리하여 두 수 자체도 포함하는 범위를 나타내는 범위 지정. 화요일부터 금요일까지 cron 작업을 수행하려면, weekday 필드에 2-5를 입력하십시오.
  • 숫자들을 쉼표로 분리하여 나열한 숫자 리스트. 1월의 첫날과 마지막 날에 명령을 수행하려면, 필드에 1,31을 입력합니다.
  • *(별표)는 모든 허용되는 값을 의미합니다. 매 시간마다 작업을 수행하려면, hour(시) 필드에 별표를 지정하십시오.
주: 앞에 역슬래시(% 포함)가 오는 모든 문자는 문자 그대로 처리됩니다. 날짜의 지정은 두 필드(날짜와 요일)에 의해 이루어질 수 있습니다. 둘다를 요소로 지정할 경우, 둘다 고수하십시오. 예를 들어 다음과 같이 입력합니다.
0 0 1,15 * 1 command

이 명령은 모든 월요일뿐만 아니라 각 달의 1일과 15일에 command를 수행합니다. 단 하나의 필드로 날짜를 지정하려면, 다른 필드는 *를 포함해야 합니다.

명령 지정

cron 디먼은 선택된 날짜와 시간의 여섯 번째 필드에 지정된 명령을 수행합니다. 여섯 번째 필드에 %(퍼센트 기호)를 포함하면, cron 디먼은 그 앞에 오는 모든 것을 명령 호출로서 취급하고, 퍼센트 기호(\%)를 탈출하지 않는 한 그 뒤에 오는 모든 것을 표준 입력에 사용 가능하게 만듭니다. 공백 행과 첫번째 비공백 문자가 번호 기호(#)인 행을 무시됩니다.

주: 쉘은 명령 필드의 첫번째 행만을 수행합니다. 모든 다른 행은 명령에 표준 입력으로 사용 가능하게 됩니다.

cron 디먼은 사용자의 HOME 디렉토리에서 서브쉘을 시작합니다. 사용자가 로그인되지 않았을 때 수행하도록 명령을 계획하고 사용자의 .profile 파일에 있는 명령을 수행하려면, 명령은 반드시 명시적으로 사용자의 .profile 파일을 읽어야 합니다.

cron 디먼은 모든 쉘에 대해 HOME, LOGNAME, SHELL(=/usr/bin/sh) 및 PATH(=/usr/bin)를 정의하는 디폴트 환경을 제공합니다.

플래그

-e 사용자의 crontab 파일의 사본을 편집하거나 또는 아직 crontab 파일이 없으면 편집 세션을 시작합니다. 편집이 완료될 때, 항목이 사용자의 crontab 파일로서 설치됩니다. 편집 세션은 EDITOR 환경 변수에 의해 지정되는 편집기를 사용하여 시작됩니다. 디폴트 편집기는 vi입니다.
-l crontab 파일을 나열합니다.
-r crontab 디렉토리에서 사용자 crontab 파일을 제거합니다.
-v cron 작업의 상태를 나열합니다.

보안

감사(audit) 이벤트: 감사(auditing) 서브시스템이 적절하게 구성설정되고 사용 가능한 경우, crontab 명령은 명령이 실행될 때마다 다음 감사 레코드(이벤트)를 생성합니다.

이벤트 정보
CRON_JobRemove cron 작업을 제거한 사용자와 시기를 나열합니다.
CRON_JobAdd cron 작업을 추가한 사용자와 시기를 나열합니다.

감사 이벤트 선택 및 그룹화하는 방법과 감사 이벤트 데이타 수집 구성설정 방법에 대한 자세한 내용은 AIX Version 4.3 System Management Guide: Operating System and Devices에 있는 "감사 기능 설정"을 참조하십시오.

종료 상태

이 명령은 다음과 같은 종료값으로 복귀합니다.

0 성공적으로 완료되었습니다.
>0 오류가 발생하였습니다.

예제

  1. mycronjobs이라는 파일을 /var/admn/cron/crontabs 디렉토리에 복사하려면, 다음과 같이 입력하십시오.
    crontab mycronjobs
  2. 매 시간마다 콘솔에 시간을 기록하려면, 다음과 같이 입력하십시오.
    0 * * * * echo The hour is `date` .
    >/dev/console
  3. 모든 월요일, 수요일 및 금요일 오전 6시 30분에 calendar 명령을 수행하려면, 다음과 같이 입력하십시오.
    30 6 * * 1,3,5 /usr/bin/calendar
  4. 일년 내내 매일 6시 30분에 calendar 명령을 수행하려면, 다음과 같이 입력하십시오.
    30 6 * * * /usr/bin/calendar
  5. 8월 동안 매일 자정에 maintenance라는 스크립트를 수행하려면, 다음과 같이 입력하십시오.
    0 0 * 8 * /u/harry/bin/maintenance
  6. 명령에 대한 표준 입력에 대해 텍스트를 정의하려면, 다음과 같이 입력하십시오.
    0 16 * 12 5 /usr/sbin/wall%HAPPY HOLIDAY!%Remember to 
    turn in your time card.
    %(퍼센트 기호) 뒤에 오는 텍스트가 다음과 같이 wall 명령에 대한 표준 입력을 정의합니다.
    HAPPY HOLIDAY!
     
    Remember to turn in your time card.

파일

/var/adm/cron/FIFO crontab 또는 at 명령으로 새 작업이 제출될 때 cron 디먼으로 메세지를 전송하는 명명된 파이프.
/var/spool/cron/crontabs crontab 스풀 영역을 지정합니다.
/var/adm/cron/cron.allow crontab 명령에 대한 액세스가 허용되는 사용자 리스트를 지정합니다.
/var/adm/cron/cron.deny crontab 명령에 대한 액세스가 거부되는 사용자 리스트를 지정합니다.

 


리눅스에서는 매일 혹은 매주, 아니면 한달에 한번 등등 주기적으로 해야 하는 작업을 편리하게 지원해주는 cron이라는 도구가 있습니다.
cron을 이용하면 이러한 단순 반복 작업을 쉽게 서버에서 자동으로 실행되도록 할 수 있습니다.

그러면 간단한 사용방법과 응용법에 대해 살펴보도록 하죠.

cron을 이용한 작업 자동화 스케줄링은 크게 두가지 단계로 생각해 볼 수 있습니다.
첫번째는 작업 내용을 위한 간단한 쉘 실행 스크립트를 만드는 것이고,
두번째는 그 실행 스크립트를 crontab을 이용해 cron에서 자동실행 할 수 있도록 등록하는 것이죠.

cron 을 사용하기 위해서는 crontab 이란 명령을 이용합니다.

[root@linux /root]# crontab
usage:  crontab [-u user] file
        crontab [-u user] { -e | -l | -r }
                (default operation is replace, per 1003.2)
        -e      (edit user's crontab)
        -l      (list user's crontab)
        -r      (delete user's crontab)
[root@linux /root]#

주로 사용하는 옵션은 -l 옵션과 -e 옵션입니다.
-l은 cron에 스케쥴링 되어 있는 작업 리스트를 보여주며, -e는 작업 내용을 편집할 수 있도록 해줍니다.(기본적으로 편집기로 VI 에디터를 사용하므로 VI 에디터의 기본적인 사용이 가능해야 합니다.)

crontab을 이용해 스케줄링을 편집할 경우의 형식은 다음과 같습니다.

0 3 * * * /home/www/cron/daily_backup > /dev/null 2>&1

0, 3, 그리고 *로 이루어진 맨 앞의 5개의 필드는 각각 분, 시, 일, 월, 요일을 나타냅니다.

  • 분 : 0부터 59
  • 시 : 0부터 23
  • 일 : 1부터 31
  • 월 : 1부터 12, 또는 jan, feb 등의로 정의
  • 요일 : 0부터 6(0이 일요일), 또는 sun, mon 등으로 정의

그리고 다음의 /home/www/cron/daily_backup은 실행 명령 또는 실행 파일을 나타내며, cron의 출력 결과와 에러내용은 메일을 통해 root에게 보내지는데 메일을 통해 통보받기를 원하지 않고 어떠한 기록을 남길 필요가 없을때는 > /dev/null 2>&1 으로 각각 표준 출력과 표준에러 처리에 대해 정의하면 됩니다.
만약 로그파일을 이용해 결과를 남기고 싶다면
> /dev/null 대신 >> /home/www/cron/cron_log 등으로 정의하면 차곡차곡 출력 결과가 쌓이겠죠.

오늘 설명드릴 내용은 홈페이지의 내용을 하루에 한번씩 백업하는 내용이므로, 백업파일이 정상적으로 생성되었는지 확인하는 것이 cron이 제대로 작동했는지 확인하는 것이 됩니다. 그래서 위와 같이 메일이나 로그등이 남지 않도록 하였습니다.

그럼 지정된 명령이나 실행 스크립트를 어떻게 구성할 것인가 살펴봐야겠죠?

[root@linux /root]# cat /home/www/cron/daily_backup
#!/bin/sh
## WWW Daily Backup
cd /home/www/backup
dir='date +%y%m%d'
mkdir $dir
cd $dir
cp -r /home/www/public_html/* .
[root@linux /root]#

간단하죠? 첫째 줄에서는 쉘을 정의하고…그다음 명령들은 날짜에 해당하는 백업 디렉토리를 만들어 이동한다음, 홈페이지 내용을 복사해오는 명령들의 나열입니다.

그러면 다시 cron에 설정되어있는 내용을 살펴보면…

0 3 * * * /home/www/cron/daily_backup > /dev/null 2>&1

앞의 필드를 살펴본다면 매일 새벽 3시 정각에 daily_backup 쉘 스크립트를 실행하고, 표준 출력 결과는 모두 버리라는 뜻으로 해석하면 되겠군요.

이러한 방법으로 DB나 기타 백업 처리의 자동화가 가능합니다.
물론 기타 작업또한 자동화 시킬 수 있겠죠.

 

출처 : Cron & Crontab 2006.07.26 | 카페명 : Super UNIX(IBM HP SUN SCO)


'개발 세발 창고' 카테고리의 다른 글

JFrame 에서 윈도우 닫기 버튼 비활성화  (0) 2009.03.10
이클립스와 CVS를 이용한 팀 작업  (0) 2009.03.10
리눅스/유닉스 명령어 모음  (0) 2009.03.10
Introducing JMS  (0) 2009.03.10
프로그램은..  (0) 2009.03.10

목차
  1. 리눅스/유닉스 명령어 모음
  2. BASH 일반
  3. 파일과 디렉토리
  4. 압축과 아카이브
  5. 디스크와 파일 시스템
  6. 프로세스 관리
  7. 사용자와 그룹
  8. 시스템 관리
  9. 네트워크/인터넷
  10. 텍스트 처리
  11. 기타
  12. MS-윈도우에서 유닉스/리눅스 명령어의 사용
  13. 잡담

리눅스/유닉스 명령어 모음

리눅스와 유닉스에서 쓰이는 일반적인 명령어들을 나열하고 실제 활용 방법을 기록합니다.

BASH 일반

파일과 디렉토리

  • ls: 파일 목록 보기
  • cp: 파일의 복사
  • mv: 파일의 이동과 이름 바꾸기
  • rm: 파일의 삭제
  • ln: 파일과 디렉토리에 대한 링크 생성
  • mkdir: 디렉토리의 생성
  • rmdir: 디렉토리의 삭제
  • touch: 빈파일을 생성하거나 파일의 수정된 날짜를 현재 시각으로 변경
  • chown: 파일의 소유자 바꾸기
  • chgrp: 파일의 그룹 바꾸기
  • chmod: 파일의 허가권 설정
  • du: 현재 디렉토리가 차지하고 있는 용량보기
  • cat:
  • find: 파일 검색
  • locate :
  • which: 실행파일의 경로 표시
  • whereis:

압축과 아카이브

디스크와 파일 시스템

  • fstab: 파일 시스템 마운트 정보를 저장한 /etc/fstab 파일의 작성법
  • mount: 디스크와 파일 시스템의 마운트
  • umount: 마운트된 파일 시스템 언마운트(마운트 해제)
  • df: 마운트된 각 파티션의 용량과 사용량, 남은 용량 보기

프로세스 관리

  • top: 현재 메모리 및 프로세스별 자원 사용률 표시
  • ps:
  • kill:
  • killall:

사용자와 그룹

시스템 관리

  • shutdown: 시스템의 종료와 재부팅
  • free: 메모리와 스왑 이용량 출력
  • uname: 시스템 정보 표시(커널 종류/버전 등)

네트워크/인터넷

텍스트 처리

  • sed: 텍스트 프로세싱 툴(스트림 에디터)
  • awk(gawk): 패턴 스캔 & 프로세싱을 위한 랭귀지 툴
  • grep: 텍스트 파일 내용 검색
  • more:
  • less:

기타

  • man: Manual 페이지 보기

MS-윈도우에서 유닉스/리눅스 명령어의 사용

MS-윈도우에서도 저 훌륭한 유닉스/리눅스 명령어/툴들을 사용할 수 있다.
  • [WWW]GnuWin32: 윈도우로 포팅된 GNU 명령어들
  • [WWW]Cygwin: 윈도우로 Bash 부터 시작하여 GCC, VI 등 X 윈도우까지 포팅하는 프로젝트

잡담

  • 리눅스 명령어 활용 사전제작에 들어갑니다. 많은 분들의 참여로 진정한 "리눅서들"의 위키가 되었으면 좋겠습니다. -- 권남
  • 링크가 생성 안된 것들을 이제 함께 채워나가요..
  • Basic Command - 이 페이지를 잠시 유지함. Command 페이지가 완성되면 삭제 

'개발 세발 창고' 카테고리의 다른 글

JFrame 에서 윈도우 닫기 버튼 비활성화  (0) 2009.03.10
이클립스와 CVS를 이용한 팀 작업  (0) 2009.03.10
Cron & Crontab  (0) 2009.03.10
Introducing JMS  (0) 2009.03.10
프로그램은..  (0) 2009.03.10
Introducing JMS


자바스터디 네트워크 [www.javastudy.co.kr]

조대협 [bcho_N_O_SPAM@j2eestudy.co.kr]




JMS는 Java Messaging System의 약자로 Java에서 Messaging System을 사용하기 위한 API들의 정의 이다.

Messaging System


먼저 Messaging System에 대해서 알아보도록 하자. Messagign System이란, Application과 Application이 서로 통신을 하도록 지원해주는 시스템을 이야기 한다.

예를 들어서 설명해보자. 각 지점에 설치된 매출 관리 시스템 A라는 AP(※ Application의 약자, 이하 AP)와 본점에 설치된 B라는 AP가 있다고 하자, A라는 AP는 매출이 발생할때마다, 그 내용을 매장에 있는 PC에도 저장하지만, 그 내용을 본사의 Unix Machine에 전송해서 추후에 총합하도록 하게 한다고 하자.

일반적으로 Messaging System이 만들어지기 전에는 이런 업무를 A와 B 시스템간에 Socket을 직접 연결해서 Packet을 정의하고 그 Packet에 따라서 통신을 했다. 이 통신을 위해서, Packet에 대한 flow control이나, 네트워크에 문제가 발생했을때의 예외처리등을 다 직접 프로그래밍을 해야 했다. 그러나 Messaging System은 이런 모든 AP간의 통신에 대한 여러 기능들을 제공하여 통신에 대한 부분을 간결화 시켜준다

Messaging System에는 이외에도, 하나의 Publisher(방송者)가 여러 Subscriber(구독者) 에게 메세지를 전송하는 모델이라던지, P2P모델 그리고, 메세지가 중간에 유실되지 않게 하는 Reliable Messaging, 비동기방식으로 메세지를 전달하는 방법, 분산 트렌젝션, 클러스터링등을 지원한다. 이 내용에 대해서는 뒤에서 좀 더 자세하게 알아보도록 하자.


What is JMS?


그렇다면 JMS는 무엇인가? JMS는 이런 메세징 시스템을 Java에서 사용하기 위한 표준 API이다. 우리가 DBMS를 사용하면서 JDBC를 사용하는것처럼, JMS는 Messaging 시스템을 사용하기 위한 API의 집합이다.


<그림 1. jms 시스템 개념도>


그림을 살펴보자, Java Application은 표준화된 JMS API를 이용해서 Messaging System을 사용하게 된다. 이 JMS API는 각각 Messaing System Vendor에서 제공되는 Provider Code를 이용해서 Implementation되어 있기 때문에, Java Application 개발자는 JMS라는 표준 API만 사용하면 대부분의 Messanging System을 사용할 수 있다.

※ Notice !! - JMS라는 표준이 있기는 하지만 각각의 Messaging System의 특성에 따라 달라질 수 있다. 작동구조나 성능 역시 각각의 Messaging System 마다 차이가 있기 때문에. 이를 충분히 고려해서 사용해야한다.


<그림 2. jms 시스템 개념도>


Messaging System의 경우 JMS API뿐 아니라 C와 같은 Non-Java AP를 위한 Lib Code를 제공하는 경우가 있는 데 이런 경우에는 Java Application이 아닌 다른 언어로 개발된 Application과도 호환이 되기 때문에, JMS기반의 Messaging 시스템은 Mainframe이나 다른 Application들과 Java Application을 연동하는데 많이 사용이 되며, 근래에 많이 출시되는 EAI솔루션도 JMS를 이용하는 경우가 많다.

우리가 흔히 이야기하는 JMS Product는 이 Messaging System과 JMS API 가 함께 제공되는 Product를 이야기 한다. (Sonic MQ,WebLogic JMS,Oracle Advanced Queing.. etc.)


Feature of JMS


지금까지 JMS에 대한 간단한 개념을 알아봤다. 그럼 JMS API에는 어떤 특징이 있는지 하나씩 살펴 보기로 하자.
AP A와 AP B가 통신을 한다고 했을때, 통신하는 방법은 방식에 따라 data-centric이냐, interface-centric이냐로 분리될 수 있다.

RMI,IIOP,SOAP 또는 직접 TCP Packet을 정의하여 socket으로 통신하는 방식은 interface-centric이라고 한다. 데이타를 보내는 Sender에서 데이타를 보내게 되고, receiver는 어떤 데이타 형이 올지를 알고 있다. (데이타 타입이 서로 약속되어 있다.) 그리고 sender는 recevier로 부터 어떤 데이타를 받을것인지를 알고 있고, 그 데이타가 올때까지 기다리는게 일반적인 흐름이다. 즉 서로 통신을 하는 AP들이 데이타형이나 동작 방법에 대해서 알고 있는 경우가 된다.

그러나 JMS의 경우 data-centric 모델로, sender는 데이타를 보내기만 한다. Receiver가 data를 받았건 말건, 내지는 receiver의 수가 얼마가 되었는지는 상관하지 않는다. JMS에서는 sender는 JMS의 Messaging System에 데이타를 보내기만 한다. Receiver는 이렇게 Messaing System에 보내진 데이타를 중계해서 받는다. 이때 데이타 형은 미리 정해져 있지 않다. (물론 데이타를 받은 다음에는 알맞은 형으로 casting해야 된다. 그러나 데이타형을 몰라도 데이타를 받는것 자체에는 문제가 없다.) 즉 메세지를 주고 받는데에 sender와 receiver간의 약속이 필요하지 않다.


Asynchronous messaging


기본적으로 JMS Messaging 시스템은 Async 방식의 Messaging을 지원한다. Interface-centric의경우, data 를 send하면 ack를 받거나 return 값을 받을때까지 sender는 waiting을 하게 된다. Receiver역시, 계속 sender로 부터 데이타가 오기를 기다린다. 이런 통신 방식은 sync 방식이라 한다.

그러나 Async방식은 sender는 일단 message 시스템에 데이타를 보내논다. Receiver가 받았는지 여부를 확인할 필요 없이, sender는 계속해서 메세지를 보내고, 메세지를 다 보냈으면 작업을 중단한다. Receiver는 sender로 부터 데이타가 오기만을 기다리는게 아니라, 필요할때, message 시스템에 저장되어 있는 (sender로 부터 보내진) 메세지만 꺼내서 바로 사용하면 된다. 즉 sender와 receiver의 message 전송작업이 동시에 일어나지 않게 된다.


Reliable and unreliable messaging


앞에서 설명했듯이 JMS는 sender가 receiver의 상태에 상관없이 message를 무조건 보낸다고 설명했다. 여기서 집고 넘어가야할것이 그럼 어떻게 sender가 보낸 메시지가 receiver에 도착했는지를 보장할 수 있느냐는 것이다. (네트워크 문제나 기타 문제로 메세지가 유실 될 수 있다. ) 이건 JMS Messaging 시스템이 보장해준다. Reliable Messaging의 경우, sender가 데이타를 보냈으면 시스템이 중간에 다운되더라도, sender가 보낸 메세지는 receiver에게 도착할 수 있도록 보장한다. 이런 Reliable Messaging은 회사간 거래에서 중요한 데이타를 전송하는 모델 등에 유용하게 사용될 수 있다.

그 밖에 실시간 데이타 처럼 신뢰성이 중요하지 않고 메세지가 전송되는 것이 중요하다면 unreliable Messaging을 이용하여, 별도의 보장 없이 메세지를 빨리 전송하는데만 초점을 맞출 수 있다.


Publish/Subscribe model


JMS Messaging System은 sender와 receiver를 지원하는 모델에서도 다소 차이가 있다. Interface centric model의 경우(IIOP,RMI. Etc)의 경우 하나의 sender는 하나의 connect된 recevier에만 message를 보내는 1:1 (peer-to-peer) model이다. JMS는 이런 peer-to-peer 모델이외에 1:N (publish/subscriber model)과 , 특정 메세지그룹의 메세지만을 특정 그룹이 받을 수 있게 할 수 도 있다.


Queue/Bus


JMS Messaging 시스템에서 sender가 메세지를 보내는 destination이 되고, reciever가 메세지를 읽어오게 되는 부분을 우리는 Queue 또는 Bus라고 이야기한다.


<그림 3. JMS Queue의 개념>


그림 3을 보면 좀더 쉽게 이해를 할 수 있다. 포탈 쇼핑몰에 입점한 여러개의 쇼핑몰이 있다고 하자. 포탈쇼핑몰에서는 주문 내용을 메세지로 만들어서 보내고, 이 메세지를 컴퓨터에 대한 주문은 QueueA로, 전자제품에 대한 주문은 Queue B로 보낸다고 한다. 컴퓨터 판매 쇼핑몰은 QueueA에서만 주문 정보를 받고, 전자제품 쇼핑몰을 QueueB에서 주문을 받는다. Queue는 이처럼 Sender와 Receiver간에 Message를 주고 받는 채널의 역할을 한다.

여기서 만약 포탈 쇼핑몰이 하나 더 늘어 났다고 하자. 그러면 주문 연동은 어떻게 할것인가? 답은 간단하다. 새로운 포탈 쇼핑몰 B가 컴퓨터 판매 쇼핑몰에 주문을 넘기기 위해서는 Queue A에 주문 Message를 보내기만하면 된다 < 그림 3-1. 포탈 쇼핑몰이 늘어난 경우 >


<그림 3-1. 포탈 쇼핑몰이 늘어난 경우>


마찬 가지 방법으로, 포탈 쇼핑몰 A가 컴퓨터 판매에 대한 주문을 컴퓨터 판매 쇼핑몰이 아니라, 전자 제품 판매 쇼핑몰에 하고자 할때는 컴퓨터 판매에 대한 주문을 Queue B에만 넣어주면 된다.

이처럼 JMS Messaging 시스템에는 Queue의 개념을 이용하면, Message의 경로 배정( Routing)을 매우 유연적으로할 수 있으며, 이런 유연성은 업무 흐름 (Work flow)를 구현하는데 큰 강점으로 작용한다.

그럼 이렇게 기능이 좋은 JMS를 통신에는 다 사용하면 될것인가? 당연히 대답은 NO다. 이메일을 위해서는 이미 SNMP라는 좋은 프로토콜이 개발되어 있고, 이를 이용하기 위한 JavaMail API가 있다. Audio나 Video Streaming 같은 경우에는 Real Time이 중요하다, JMS는 오히려 Async 메세징에 더 유리하다고 볼 수 있다. JMS Messaging 시스템의 특성에 대해서 제대로 파악하고, 적재 적소에 사용한다면 좀더 뛰어나고 안정된 상호 운영성을 확보할 수 있을 것이다.

지금까지 JMS 시스템의 대략적인 구조에 대해서 살펴보았다.. 처음 접하는 사람은 쉬운 개념이 아니었을지도 모르지만, JMS 시스템이 대략 어떤 개념인지만 이해한다면 충분히 성공한것이다. 좀 더 구체적인 내용과 구현방법은 JMS 시스템에 대한 서적을 참고하기 바란다.

※ 참고 자료 Enterprise JMS Programmin / Shaun Terry - M Books

'개발 세발 창고' 카테고리의 다른 글

JFrame 에서 윈도우 닫기 버튼 비활성화  (0) 2009.03.10
이클립스와 CVS를 이용한 팀 작업  (0) 2009.03.10
Cron & Crontab  (0) 2009.03.10
리눅스/유닉스 명령어 모음  (0) 2009.03.10
프로그램은..  (0) 2009.03.10

소스 코드 짤 때 맘 다르고

 

소스 코드 볼 때 맘 다르니

 

이래서 주석이 필요한가 보오..

 

쉣~!!

 

ㅡㅡ;

'개발 세발 창고' 카테고리의 다른 글

JFrame 에서 윈도우 닫기 버튼 비활성화  (0) 2009.03.10
이클립스와 CVS를 이용한 팀 작업  (0) 2009.03.10
Cron & Crontab  (0) 2009.03.10
리눅스/유닉스 명령어 모음  (0) 2009.03.10
Introducing JMS  (0) 2009.03.10

+ Recent posts