UNIX와 Mac OS X에서 classpath를 관리하는 방법 ![]() | ![]() |
![]() |
난이도 : 초급 Elliotte Harold, Adjunct Professor, Polytechnic University 2007 년 1 월 09 일 classpath 는 자바™ 플랫폼에서 가장 복잡한 부분 중 하나이지만, 전문 자바 프로그래머가 되려면 반드시 이것을 통달해야 합니다. Elliotte Rusty Harold는 classpath와 sourcepath를 설명하고, UNIX와 Mac OS X에서 사용하는 방법을 설명합니다. Windows를 사용하고 있다면, 관련 기술자료를 참조하십시오. classpath는 자바 런타임과 파일시스템간 연결이다. 인터프리터가 로딩할 .class 파일들을 검색할 장소를 정의한다. 기본 개념은, 파일시스템 계층이 자바 패키지 계층을 미러링 하고, classpath가 파일시스템에서 어떤 디렉토리가 자바 패키지 계층에 대한 루트의 역할을 할 것인지를 지정하는 것이다. 안타깝게도, 파일시스템은 복잡하고, 플랫폼에 상당히 의존하며, 자바 패키지와 완벽하게 들어맞지 않는다. 따라서, classpath는 신참 사용자와 숙련된 자바 프로그래머 모두에게서 가시와 같은 존재가 되었다. 자바 플랫폼에서도 환영을 받는 존재도 아니다. 지독하게도 솔루션이 나오지 않는 소소한 문제들을 해결하느라 많은 노력을 기울여야 한다. Eclipse 같은 훌륭한 IDE는 classpath 관리의 어려움을 덜어주기는 하지만, 어디까지나 모든 것이 잘 작동하고 있을 경우에만 해당된다. (어떤 것은 늘 잘못되기 마련이다.) 결국, 모든 자바 프로그래머는 classpath를 완전히 이해해야 한다. 완벽한 통달만이 classpath에서 발생하는 가시 같은 문제들을 해결할 수 있다. 이 글에서, UNIX, Linux, Mac OS X 플랫폼에서 자바 classpath(관련 sourcepath 포함)에 대해 알아야 할 모든 것을 망라했다. 참고 기술자료에서는 Windows 플랫폼을 대상으로 설명했다. 이 글이 충실한 가이드가 될 것이며, 대부분의 classpath 문제들을 해결할 수 있을 것이다. classpath 마스터는 소스 코드부터 시작한다. 모든 클래스는 패키지에 속해 있고, 이 패키지는 표준 네이밍 규약을 따라야 한다. 간단히 정리하면 다음과 같다. 패키지 이름은
도 치된 도메인 네임 다음에는, 한 단어로 된 서브패키지(subpackage) 이름만 사용한다. 단어를 생략하지 말고, 모든 단어를 정확하게 작성한다. 필요하다면, 스펠 체커를 사용한다. classpath 관련 문제들 대부분이 소스 코드에 한 단어를 사용하는 데서 기인하고, 파일시스템의 단어와 약간 다른 스펠링 또는 축약 때문에 발생한다. 가장 현명한 방법은 줄이지 않은 정확한 스펠링을 사용하는 것이다. 전체 패키지 이름은 소문자로 하며, 일반적으로 대문자를 사용하는 고유 명사와 두문자일 경우에도 소문자를 사용한다. 패키지 이름은 ASCII 문자들로 구성된다. 컴파일러는 Hebrew, Cyrillic, Greek, 기타 스크립트로 작성된 패키지 이름들을 허용하지만, 많은 파일 시스템들은 그렇지 않다. 이러한 패키지 이름들은 디렉토리 이름으로서의 이중 기능을 할 필요가 없다. 결국, 패키지(그리고 클래스) 이름들은 ASCII로 제한되어야 한다. (자바 패키지와 클래스 이름은 Unicode이지만, 많은 파일 시스템들은 아직 Unicode가 아니다. 다른 디폴트 인코딩으로 파일을 시스템으로 복사하면 컴파일러와 인터프리터가 올바른 클래스를 찾을 수 없도록 한다.)
패 키지 이름에 인색하게 굴지 말라. 장기적으로 볼 때 큰 재앙이 될 수 있다. 도메인 이름이 필요하면, 구매해도 좋다. 이름이 너무 길다면, 짧은 것을 구매하라. (나는 xom.nu를 샀기 때문에, 나의 패키지 접두사는 단 여섯 문자뿐이다.) 클래스를 디폴트 패키지(클래스에 패키지 문을 추가하지 않을 경우 얻게 되는 패키지)에 두지 말라. 패키지 액세스가 객체 통신을 금하면, 더 많은 퍼블릭 메소드를 클래스에 추가하라. 한 번 이상 사용하는 모든 클래스는 패키지에 있어야 한다. 다음 단계는 소스 파일들을 구성하여 패키지 구조와 맞추는 것이다. 빈 디렉토리를 만들어라. 이 글에서는 그 이름을 project로 하겠다. 이 디렉토리 안에, 두 개의 디렉토리를 더 만든다. bin과 src이다. (어떤 사람들은 이것을 각각 build와 source로 하기도 한다.) src 디렉토리 안에, 패키지 계층과 똑 같은 계층을 만든다. 예를 들어, 그림 1. 디렉토리 구조는 패키지 구조를 따른다. ![]() 중 요: src 디렉토리에 소스 코드 외 다른 것을 절대 넣지 말라. 그곳에 들어갈 유일한 파일은 .java 파일들이다. 가끔 이 디렉토리에 .html(Javadoc) 또는 다른 유형의 소스 코드를 넣는 경우도 있다. 하지만, .class 파일이나 다른 생성물들을 이 계층에 넣어서는 안된다. 큰 문제로 돌아올 것이다. javac 컴파일러의 경우 조심하지 않는다면 반드시 문제를 일으킨다. 다음 섹션에서 이러한 문제를 해결하는 방법을 설명하겠다.
자바 코드 컴파일은 다르지만 관련된 여러 가지 것들을 트래킹 해야 하기 때문에 손이 많이 간다.
기본적으로, javac 컴파일러는 이 모든 것이 현재 실행 디렉토리라고 간주하는데, 대게는 우리가 원하는 것이 아니다. 결국, 컴파일 할 때 엘리먼트들을 명확하게 지정해야 한다. 우선, 컴파일 하고자 하는 .java 파일을 지정해야 한다. 이것은 현재 실행 디렉토리에서 그 파일로 가는 경로로 주어진다. 예를 들어, 그림 1처 럼, project 디렉토리를 생각해 보자. 이 디렉토리에는 src 디렉토리가 있다. src 디렉토리에는 com 디렉토리가 있고, com 디렉토리에는 예제 디렉토리가 포함되어 있다. 여기에는 Fraction.java 파일이 있다. 다음 명령행이 이것을 컴파일 한다.
경로가 정확하지 않다면, 다음과 같은 에러 메시지를 받게 된다.
에러 메시지를 보면, 경로를 보면서 철자가 올바르게 쓰였는지를 확인한다.
이 문제는 잘못 입력된 경로를 나타내지만, 여러분이 생각한 디렉토리에 있지 않다는 것도 의미한다. 이 예제에서, 현재 실행 디렉토리가 project 디렉토리인지를 확인했다.
컴파일 하기 전에 신
택스 에러가 없을 경우, javac는 .java 파일이 있는 디렉토리에 컴파일 된 .class 파일을 배치한다. 이것은 우리가
원한 것이 아니다. .class와 .java 파일을 섞어 놓으면, 컴파일 된 파일들을 지우기가 매우 어렵다. 실수로 .java
파일들을 지울 수도 있다. 이는 깨끗한 구현도 문제거리로 만들며, 버저닝 문제도 일으킨다. 또한 바이너리를 배포할 때, 컴파일
된 .class 파일들만 압축하기도 힘들다. 따라서, 완전히 다른 디렉토리에 컴파일 된 결과를 두도록 컴파일러에 명령해야 한다.
그림 2 같은 아웃풋이 생겼다. javac는 완성된 com/elharo/math 디렉토리 계층을 만들었다. 우리는 이것을 직접 수행할 필요가 없다. 그림 2. 병렬 소스와 컴파일 된 계층 ![]() 자바가 소스 파일들을 검색할 디렉토리는 sourcepath이다. 여기에서는, src 디렉토리이다. 이 디렉토리에는, 자신들의 디렉토리로 구성된, 소스 파일의 계층이 포함되어 있다. com 디렉토리도 src/com/elharo/math 디렉토리도 아니다. 대부분의 프로젝트는 한 개 이상의 클래스와 한 개 이상의 패키지를 사용한다. 이것은 import 문과 전체 패키지 클래스 이름들로 연결된다. 예를 들어, Listing 1. 한 패키지의 클래스가 또 다른 패키지의 클래스를 반입할 수 있다.
이 클래스는 그림 3. 여러 패키지의 소스 구조 ![]() 이제, 전에 했던 것처럼 MainFrame.java를 컴파일 할 때 어떤 일이 발생하는지 보자. Listing 2. MainFrame.java 컴파일
javac가 MainFrame.java를 어디에서
찾아야 할 지를 알지만, Fraction.java를 어디에서 찾아야 할지 모르기 때문에 Listing 2에 에러가 생겼다.
(매칭 패키지 계층들을 인식하는 것으로도 충분히 똑똑하다고 생각하겠지만, 그렇지가 않다.) sourcepath를 지정해야 한다. 이것은 컴파일러가 소스 파일의 계층을 검색할 디렉토리를 지정한다. Listing 2에서, 이것은 src이다. 따라서, 나도 다음과 같이,
이제 프로그램은 에러 없이 컴파일 하고, 그림 4와 같은 아웃풋을 만들어 낸다. javac 역시 내가 컴파일 했던 파일에 의해 참조된 Fraction.java 파일을 컴파일 했다. 그림 4. Multiclass 아웃풋 ![]() sourcepath 에 한 개 이상의 디렉토리가 생겼다. 필요한 것은 아니지만, 콜론으로 구분되어 있다. 예를 들어, 또 다른 프로젝트용 소스 코드를 관리할 로컬 src 디렉토리와 /Users/elharo/Projects/XOM/src 디렉토리를 추가하고 싶다면, 다음과 같이 컴파일 할 수 있다.
이 명령어는 그 계층에서 발견된 모든 파일들을 컴파일 하지 않는다. 컴파일 되도록 분명히 요청했던 하나의 .java 파일에 의해 직/간접적으로 참조된 파일만 컴파일 한다. .java 파일용으로 하나의 소스 디렉토리가 있지만, 사전 컴파일 된 서드 파티 라이브러리가 배치된 클래스 또는 JAR 아카이브용 여러 디렉토리가 있다. 이것은 classpath의 역할이다. 중대형 프로젝트에서, 매번 모든 파일들을 재 컴파일 한다는 것은 시간 낭비다. 다른 클래스나 bin 디렉토리에 같은 프로젝트의 독립된 부분들을 개별적으로 컴파일 및 저장함으로써 이러한 문제를 완화시킬 수 있다. 클래스를 classpath에 추가하는 여러 가지 방법이 있다. 하지만, 여러분은
두 개의 디렉토리, /Users/elharo/project1/classes와/Users/elharo/project2/classes를 추가한다고 해보자. 콜론으로 구분하여 다음과 같이 추가한다.
물론, 원한다면, 상대 경로라는 다양한 형태를 사용할 수 있다. project1과 project2가 현재 실행 디렉토리와 인접해 있다면(같은 부모 디렉토리를 갖고 있다면) 다음과 같이 한다.
지금까지, 프로그램이 완전하고, 개별적으로 컴파일 된 서드 파티 라이브러리를 사용하지 않는 경우를 가정했다. 만약 그렇다면, 이들을 classpath에도 추가해야 한다. 라이브러리는 junit.jar 또는 icu4j.jar 같은 JAR 파일로서 배포된다. 이 경우, classpath에 추가하는 것은, JAR 파일 그 자체이다. 이것을 포함하고 있는 디렉토리가 아니다. (본질적으로, JAR 파일은 컴파일 된 .class 파일들을 포함하고 있는 디렉토리로서 작동한다.) 예를 들어, 다음 명령어는 classpath에 세 가지를 추가한다. /Users/elharo/classes 디렉토리, 현재 실행 디렉토리에 있는 icu4j.jar 파일, /Users/elharo/lib에 있는 junit.jar 파일.
JAR 파일들은 .java 파일과 sourcepath가 아닌, .class 파일과 classpath에만 사용된다.
프로그램을 성공적으로 컴파일 했고, 이제 실행할 준비가 되었다. 컴파일과 비슷하지만 더 간단하다. 프로그램을 실행할 때, 다음 두 가지를 지정하면 된다.
sourcepath를 지정할 필요가 없다. 일반적으로, classpath는 프로그램을 컴파일 하기 위해 사용했던 것과 같은 classpath이다. 컴파일 된 아웃풋이 배치되었던 디렉토리가 추가된다. 예를 들어, 컴파일 명령어가 다음과 같고,
명령행의 마지막 아이템은 클래스 이름이다. 파일 이름이 아니다. .java나 .class로 끝나지 않는다. 이 클래스는 classpath 어디에선가는 발견되어야 한다.
컴 파일 할 때나, 실행할 때, 언제나 분명하게 classpath를 지정해야 한다. 파일을 둘 수 있는 다른 장소들이 있기 때문에, classpath에 추가되고, javac 컴파일러와 자바 인터프리터에 의해서 발견될 수 있다. 이 옵션들은 적은 양의 타이핑만 저장하고, classpath에 오래된 버전의 클래스를 둘 경우 디버깅에 많은 시간이 걸릴 것이다. 예기치 않게 classpath로 와서 문제를 일으키는 클래스 하이딩을 찾을 장소를 설명하겠다. 서버처럼, 우리들이 제어할 수 없는 머신이 될 것이다. 요청을 하든, 하지 않든 간에, 컴파일러는 현재 실행 디렉토리(.)를 classpath에 추가한다. 같은 디렉토리 안에 무엇이 있고, 없는지를 쉽게 잊는다. 따라서, 클래스나 계층들을 프로젝트나 홈 디렉토리에 두지 말라. 대신, .java 파일용 src 디렉토리와 bin 디렉토리에 명확하게 분리해서 저장해야 한다. bin 디렉토리와 JAR 아카이브를 classpath에 직접 추가하는 것이 지겨워 질것이다. 이때 이 와 같은 유혹과 싸워라. 잘못된 클래스나 잘못된 클래스 버전을 로딩할 때 문제를 일으킬 것이다. 저장할 때 마다, 잘못된 클래스를 로딩했기 때문에 생긴 문제들을 디버깅 할 때 수백 번 취소당할 것이다. classpath를 자동화하고 타이핑을 피할 수 있는 더 나은 방법이 있다. jre/lib/ext 디렉토리에 있는 JAR 아카이브는 가상 머신에서 실행되는 모든 애플리케이션의 classpath에 추가된다. 이것이 안전해 보이지만, 디렉토리들을 이 문제는, 서버 측 애플리케이션을 전개할 때 특별히 위험하다. 전개할 서버에 jre/lib/ext 디렉토리에 추가 JAR 파일들이 없어야 한다. classpath에서 잘못된 버전의 JAR 아카이브로 생긴 문제들은, 증상을 인지하지 못하거나, 무엇을 찾아야 할지 모를 경우, 디버깅이 매우 어렵다. 이러한 문제들을 피하기 위해, 일부 프레임웍은 자바 코드의 일반 클래스 로딩 메커니즘을 우회하는 클래스 로더를 작성하기도 한다. jre/lib/endorsed에 있는 JAR 파일들은 가상 머신으로 실행되는 모든 애플리케이션의 classpath에도 추가된다. 차이점은, 이 파일들이 실제로 일반 classpath가 아닌 bootclasspath에 추가되고, 표준 클래스를 JDK로 대체할 수 있다. 이러한 방식은 XML 파서를 업그레이드 하고, VM에서 버그를 픽스할 때 유용하다. 이 기술은 간편해 보이지만, 이 역시 장기적으로 볼 때는 위험하다. JDK 클래스로 대체해야 한다면, 런타임 시
못 총(nail gun)을 집어 들기 전에 망치를 사용하는 방법부터 배워야 한다. 마찬가지로, 보다 강력한 툴을 찾기 전에 클래스를 직접 관리하는 것에 익숙해져야 한다. 하지만, sourcepath와 classpath를 다루어야 하는 고통을 줄여주는 툴들이 있다. 대부분 이 툴들은 파일들을 라인 별로 정리한다. Eclipse와 NetBeans 같은 통합 개발 환경은 classpath 관리의 일부를 자동화 한다. 예를 들어, 패키지 이름을 변경할 때, Eclipse는 상응하는 .java 파일을 매치하는 것으로 옮긴다. (그림 5) 그림 5. Eclipse에서 classpath ![]() 하 지만 IDE는 다른 툴과 다른 IDE와 통합해야 한다면, 올바르게 설정되어야 하는 파일시스템 상에 여전히 있다. 이 툴의 주된 특징은 GUI 다이얼로그, 트리 뷰, 탭들이 명령행 스위치를 대체한다는 점이다. 하지만, 기본 파일 구조는 같다. Ant는 빌드 프로세스를 자동화 하는 표준 툴이다. 디렉토리를 jre/lib/ext 또는 Maven은 빌드 프로세스와 관련 classpath 문제들을 구성 및 자동화 한다는 점에서 Ant 보다 더 향상되었다. Maven은, 단 몇 줄의 코드로도 간단한 프로젝트를 구현할 수 있도록, 합리적인 디폴트 설정을 제공한다. Maven이 찾을 수 있는 곳에 소스 파일을 두면 된다. 여전히, 파일 시스템 계층과 패키지 계층을 조정해야 한다. Maven은 삼자 라이브러리에 대한 의존성 관리에 특별히 뛰어나다. Ant 만큼 커스터마이징은 쉽지 않다.
classpath는 문제가 많지만, 몇 가지 간단한 규칙을 사용하면 능히 다스릴 수 있다. 특히,
마 지막 팁: classpath와 관련한 많은 문제들은 디렉토리 이름을 잘못 입력했다거나, 잘못된 디렉토리에서 컴파일 하는 등 간단한 에러에서 진화한다. 무엇이 잘못되었는지 알 수 없다면, 동료나 친구에게 물어보고 문제를 찾아야 한다. 오히려 문제에 너무 집착하여, 다른 사람들 눈에는 명확하게 보이는 버그를 못 볼 수도 있다. 두 눈은 매우 효과적인 디버깅 방법이다. classpath는 분명 쉬운 것은 아니다. 하지만, 관리도 가능하다. 네이밍 규약, 명령행 인자, 디렉토리 구조에 신경을 쓴다면, 실수를 최소한으로 줄이고 프로그램을 컴파일 및 실행할 수 있을 것이다. 교육
제품 및 기술 얻기 토론
출처 : http://www.ibm.com/developerworks/kr/library/j-classpath-unix/index.html |
SSISO Community