Programming

[Java] Classpath와 환경변수

steloflute 2017. 2. 10. 01:10

http://devyongsik.tistory.com/61


김세곤은 JSP Bible 책의 저자이며, 인프니스 사장이다.



출처 Good Luck !! | 크라임랩
원본 http://blog.naver.com/erfile/100008279019

원 출처가 어딘지 모르는..걍 떠돌아 다니는 글인데 클래스패스 땜에 컴퓨터를 뽀사버릴려다가 찾아서 퍼옴..

누군지 모르지만 김세곤 쌤.. 감사합니다..^^;;


---------------------------------------------------------------------------------------


클래스패스와 환경 변수, 그것이 알고 싶다.
김세곤 
2001년 4월 17일


서론
초보 자바 프로그래머를 괴롭히는 큰 문제 중에 그놈의 클래스패스는 빠지지 않는다. 클래스패스는 사실 이렇게 하나의 글로 설명하기조차 매우 부끄러운 사소한 것인데, 초보 자바 프로그래머에게는 절대 사소하지 않은 것이 현실이다. 더군다나, 가슴 아프게도 대부분의 자바 관련 서적은 클래스패스에 지면을 할애할 만한 형편도 안되고, 대부분의 저자들이 별것도 아닌 것에 공들여 설명하려 하지 않기 때문에, 당연히 클래스패스에 대해서는 많은 독자들이 제대로 이해하지 못한 채 끙끙댄다. 한편으로는, 이것은 기초를 제대로 다지지 않은 독자들의 책임이 크다. 클래스패스를 잘 설정해서 자바 프로그램을 컴파일하고 실행하는 기술은 자바 언어의 첫번째 단추이고, 이 내용은 대부분의 자바 책(자바 관련 서적이 아닌 자바 그 자체를 설명하는 책)에서는 설명이 되어 있기 때문이다.

필자는 JSP Bible이라는 책을 집필하고 독자로부터 많은 질문을 받았는데, 거짓말 보태지 않고 50% 이상의 독자가 클래스패스로 골머리를 썩고 있었다. JSP로 웹 개발을 시도하려 한다면 자바 언어의 첫번째 단추인 클래스패스와 컴파일 정도에는 문제가 없어야 하는데, 아쉽게도 이 첫번째 단추를 못 껴서 진도를 못 나가다니 가슴 아픈 현실이 아닐 수 없다.

가장 좋은 방법은 역시 자바 언어를 제대로 공부하고 나서 JSP, Servlet, EJB 등 그 다음 단계의 공부를 진행하는 것이다. 그런데, 이유야 어찌 됐건, 대부분의 독자들은 자바에 대한 기초없이 응용 단계인 JSP, Servlet, EJB 등으로 마구 앞질러 나간다. 그리고, 막히면 다시 자바 언어를 체계적으로 공부하지 않고, 끙끙거리며 당장 진도를 나가려고 발버둥친다. 이 대목에서 찔리는 독자 여러분들이 분명히 많을 것이다.

솔직히 말해서, 필자는 JSP Bible 집필 당시에 자바의 생기초라 할 수 있는 클래스패스 설정하기 및 컴파일하기 등에 대해서 이렇게 많은 독자들이 모르리라고 예상하지 못했다. 그리고, 끊임없이 클래스패스에 대한 질문을 받으면서, 같은 답변을 계속 하다보니 이제는 클래스패스에 대한 질문만 만나면 경기가 난다.

필자는 이 글로 더 이상 클래스패스나 환경 변수 혹은 컴파일하기 등에 관련된 질문이 없기를 간절히 기대한다. 더 나아가서 많은 자바를 공부하고자 하는 개발자들이 제발 자바 언어에 대해서는 탄탄하게 기초를 다졌으면 한다.


클래스 이름과 패키지
갑돌이가 A.java 라는 자바 프로그램을 만들었다고 하자. 그리고, 자신의 컴퓨터 C 드라이브의 myClass라는 폴더에 A.java를 컴파일한 A.class를 넣어 두었다고 치자. 그리고는 A.class를 잘 사용하다가 어느날 똑순이가 만든 클래스를 사용할 일이 있어서 똑순이의 클래스들을 건네 받았는데, 그 중에 A.class라는 같은 이름의 클래스가 있다는 사실을 알았다. 갑돌이는 갑자기 답답해졌다. 자, 어찌 하면 갑돌이가 만든 클래스 A와 똑순이가 만든 클래스 A를 구별할 수 있을까?

해답은 바로 패키지이다.

갑돌이의 회사는 gabdol이고, 똑순이의 회사는 ddogsun이므로, 이 이름을 클래스 이름 A에 붙여쓰면 구별이 될 수가 있는 것이다. 갑돌이의 클래스 A의 이름을 gabdol.A로 똑순이의 클래스 A의 이름을 ddogsun.A로 사용하면 고민이 해결된다는 말이다. 그런데, 덕팔이의 회사 이름이 갑돌이의 회사 이름과 같다면 또 이름이 충돌하게 된다. 그렇다면 전세계에서 유일한 이름을 클래스 이름 앞에 붙여주면 아주 간단해진다. 전세계에서 유일한 이름으로는 머리속에 팍 떠오르는 것이 회사의 도메인 명이다. 갑돌이네 회사가 www.gabdol.com이라는 도메인 이름을 소유하고 있다면, 갑돌이네 회사에서 만드는 모든 클래스의 이름을 com.gabdol.A와 같은 식으로 만들면 문제가 해결된다. 바로, 이 com.gabdol이 클래스 A의 패키지 이름이 되는 것이다. 만일, 갑돌이네 회사가 두 개의 자바 소프트웨어를 개발했는데, 둘 다 A.java라는 클래스가 있다면 이를 또 구별해야 한다. 이런 경우라면 com.gabdol 뒤에 임의로 갑돌이네 회사가 알아서 패키지 이름을 만들면 된다. 두 개의 소프트웨어 이름이 sw1, sw2라고 하면 com.gabdol.sw1.A, com.gabdol.sw2.A 식으로 클래스 이름을 사용하는 것이다.

이렇게 패키지를 사용하게 되면 이름 충돌의 문제도 해결할 수 있을 뿐만 아니라, 클래스를 분류하기도 쉽다. 예를 들어, 갑돌이네 회사에서 고객에 관련된 클래스들의 패키지 이름을 com.gabdol.client로 하고, 상품에 관련된 클래스들의 패키지 이름을 com.gabdol.product로 정하면 클래스들의 성격을 패키지 이름만 보고도 쉽게 파악할 수 있는 것이다.


클래스의 위치
이제, 갑돌이가 만드는 클래스의 이름이 com.gabdol.sw1.A로 정해졌다고 하자. 그렇다면 이 클래스는 어디에 위치해야 할까? com.gabdol.sw2.A와 같은 폴더에 존재하면 안 되므로, 패키지 이름에 따라 클래스의 위치가 유일하게 구별될 수 있어야 한다. 갑돌이는 자신의 클래스르 모두 C:\myClass라는 폴더에 두고 있으므로, com.gabdol.sw1.A 클래스는 C:\myClass\com\gabdol\sw1 폴더에 두고, com.gabdol.sw2.A 클래스는 C:\myClass\com\gabdol\sw2 폴더에 두면 두 클래스가 충돌하지 않을 것이다. 이렇게 하면, 소스 파일과 컴파일된 클래스 파일이 다음처럼 위치하게 된다.

C:\myClass\com\gabdol\sw1\A.java
C:\myClass\com\gabdol\sw1\A.class
C:\myClass\com\gabdol\sw2\A.java
C:\myClass\com\gabdol\sw2\A.class


이제, 패키지의 이름에 따른 클래스의 위치에 대해서 감이 오는가?


패키지 선언과 임포트
com.gabdol.sw1.A 클래스의 패키지는 com.gabdol.sw1이고 com.gabdol.sw2.A 클래스의 패키지는 com.gabdol.sw2이다. 이 정보는 당연히 두 소스 코드 내에 들어가야 한다. 방법은 간단하다. C:\myClass\com\gabdol\sw1\A.java 코드의 첫머리에 다음의 한 줄만 쓰면 된다.


package com.gabdol.sw1;


여기서, com.gabdol.sw1.A 클래스가 com.gabdol.sw2 패키지에 들어있는 클래스들을 사용하고 싶다고 하자. 어떻게 해야 할까? 방법은 두 가지이다.

첫번째는 클래스의 이름을 완전히 써 주는 것이다. 다음처럼 말이다.


package com.gabdol.sw1;
...
com.gabdol.sw2.B b = new com.gabdol.sw2.B();
...


이렇게 하면 클래스 B가 com.gabdol.sw2 패키지 내에 있는 것이 드러나므로 컴파일 시에 문제가 없다.

두번째는 패키지를 임포트(import)하는 것이다. 다음처럼 하면 된다.


package com.gabdol.sw1;
import com.gabdol.sw2.*;
...
B b = new B();
...


위의 코드의 import com.gabdol.sw2.*; 부분은 com.gabdol.sw1.A 클래스가 com.gabdol.sw2 패키지 내의 클래스들을 사용하겠다는 뜻이다. 이렇게 하면 자바 컴파일러가 B b = new B(); 부분을 컴파일하면서 B라는 클래스를 com.gabdol.sw1.A가 속해있는 com.gabdol.sw1 패키지와 임포트한 com.gabdol.sw2 패키지 내에서 찾게 된다. 만일, com.gabdol.sw1과 com.gabdol.sw2 패키지에 모두 B라는 이름의 클래스가 있다면 당연히 컴파일러는 어떤 것을 써야할지 모르므로 에러를 낸다. 이 경우에는 다음처럼 하는 수밖에 없다.


package com.gabdol.sw1;
import com.gabdol.sw2.*;
...
com.gabdol.sw1.B b1 = new com.gabdol.sw1.B();
com.gabdol.sw2.B b2 = new com.gabdol.sw2.B();
...


import com.gabdol.sw1.*과 같이 *를 사용하면 com.gabdol.sw1 패키지 내의 모든 클래스를 사용하겠다는 뜻이고, com.gabdol.sw1.B 클래스만 사용한다면 import com.gabdol.sw1.B라고 명시하면 된다.


클래스패스
이제, 갑돌이는 C:\myClass\com\gabdol\sw1\A.java 파일을 컴파일하려 한다. 갑돌이는 C:\myClass\com\gabdol\sw1\ 폴더로 이동해서 다음처럼 할 것이다.

javac A.java


결과는 당연히 클래스 B를 찾을 수 없다는 에러 메시지이다. com.gabdol.sw1.B 클래스가 컴파일된 바이트코드인 B.class가 도대체 어디에 있는지 자바 컴파일러로서는 알 길이 없기 때문이다. 이 때 필요한 것이 클래스패스이다. 갑돌이가 다음처럼 하면 컴파일이 성공적으로 이루어진다.

javac -classpath C:\myClass A.java


여기에 "-classpath C:\myClass" 부분은 자바 컴파일러에게 C:\myClass폴더를 기준으로 패키지를 찾으라는 뜻이다. 즉, com.gabdol.sw1.B 클래스는 C:\myClass 폴더를 시작으로 C:\myClass\com\gabdol\sw1 폴더에서 찾고, com.gabdol.sw2.B 클래스는 C:\myClass\com\gabdol\sw2 폴더에서 찾는 것이다.

그런데, 갑돌이는 돌쇠에게 건네 받은 클래스들을 C:\otherClass 라는 폴더에 저장하고 있었는데, 이 중에 일부 클래스를 A.java에서 사용할 일이 생겼다. 돌쇠가 건네 준 클래스는 com.dolsse.ddol 이라는 패키지에 포함되어 있고, 이 클래스들은 C:\otherClass\pr\com\dolsse\ddol 폴더에 있다면, 돌쇠가 준 클래스들은 C:\otherClass\pr 폴더를 시작으로 찾아야 한다. 따라서, 돌쇠의 클래스를 사용하는 A.java 파일을 컴파일하기 위해서는 다음처럼 해야 한다.

javac -classpath "C:\myClass;C:\otherClass\pr" A.java


이렇게 하면 C:\myClass 폴더에 저장되어 있는 com.gabdol.sw1.A, com.gabdol.sw2.B 클래스와 C:\otherClass\pr 폴더에 저장되어 있는 com.dolsse.ddol.* 클래스들을 자바 컴파일러가 제대로 찾을 수 있게 되는 것이다.

A.java를 컴파일해서 얻어진 클래스를 직접 실행하려면 자바 가상 머신을 구동해야 하는데, 이 때에서 당연히 A.class가 사용하는 다른 클래스를 자바 가상 머신이 찾을 수 있어야 한다. 따라서, 역시 컴파일할 때와 마찬가지로 클래스패스가 필요하다. A.class를 실행하려면 다음처럼 해야 한다.

java -classpath "C:\myClass;C:\otherClass\pr" com.gabdol.sw1.A


실행할 때에는 실행하고자 하는 클래스의 패키지 이름까지 명시해야 한다. 왜냐하면, 자바 가상 머신이 클래스를 실행할 때에는 지정된 이름이 패키지 이름을 포함한 것으로 생각하기 때문이다. 만일, 다음처럼 한다면,

java -classpath "C:\myClass;C:\otherClass\pr" A


자바 가상 머신은 C:\myClass\A.class 혹은 C:\otherClass\pr\A.class를 찾으려고 할 것이다.

클래스패스란 여러 폴더에 산재한 클래스들의 위치를 지정해서 패키지에 따라 클래스를 제대로 찾을 수 있게 해 주는 값이다.


환경 변수
매번, 컴파일할 때와 실행할 때에 classpath 옵션에 클래스패스를 명시하는 것을 불편하다. 한 번의 설정으로 이런 문제를 해결하기 위해서 환경 변수라는 것을 사용한다. 많은 독자들이 역시 환경 변수가 무엇인지, 어떻게 설정하는지 모르는 경우가 많으므로 여기서 잘 정리해 보겠다.

환경 변수는 말 그래로 변수에 어떤 값을 저장시키되 이를 환경에 저장하는 것이다. 환경에 저장한다는 말은 그 환경 내의 모든 프로그램이 그 변수의 값을 알 수 있게 된다는 뜻이다. 여러분이 윈도우 95/98/Me의 도스창 혹은 윈도우 NT/2000의 명령창을 실행시키면, 그 명령창 내에서 각종 프로그램을 구동할 수가 있는데, 이 때, 그 명령창의 환경에 변수 값을 설정하면 그 명령창 내에서 구동되는 프로그램들은 환경 변수 값을 얻을 수가 있게 된다.

명령창 내에서 환경 변수를 설정하는 방법은 간단하다.

C:\> SET 변수이름=값


혹은

C:\> SET 변수이름="값"


처럼 하면 된다. 여러분의 명령창 또는 도스창을 띄워서 다음처럼 해 보자.

C:\> SET myname="Hong Gil Dong"


이렇게 하면 환경 변수 myname에는 "Hong Gil Dong"이 설정된다. 제대로 설정이 되었는지 확인해 보기 위해서는 다음처럼 하면 된다.

C:\> echo %myname%
"Hong Gil Dong"


첫 번째 줄은 환경 변수 myname의 값을 출력하라는 명령이고, 두 번째 줄은 그 결과가 나온 것이다.

환경 변수는 메모리가 허락하는 양만큼 설정할 수 있다. 즉, 다음처럼 복수 개의 환경 변수를 설정할 수 있다는 말이다.

C:\> SET myname="Hong Gil Dong"

C:\> SET yourname="Kim Gap Soon"


자, 이번에는 이 명령창을 종료하고 다른 명령창을 띄워 보자. 그리고, 다음처럼 myname 환경 변수의 값을 출력해 보자.

C:\> echo %myname%
%myname%


두 번째 줄과 같은 결과가 나온다. 조금 전에 설정했던 값을 온데간데 없다. 왜 그럴까? 이유는 간단하다. 환경 변수는 그 명령창 내에서만 존재하기 때문이다. myname 변수를 설정한 명령창을 종료했기 때문에, 이와 동시에 myname 환경 변수가 사라진 것이다. 또, myname 변수를 설정한 명령창을 종료하지 않았다고 해도, 다른 명령창에서는 myname 변수 값을 공유하지 않는다. 오로지, 변수를 설정한 그 명령창 내에서만 그 변수는 의미가 있게 되는 것이다.

이렇게 환경 변수가 명령창 내에서만 의미가 있기 때문에, 온갖 프로그램에서 공유하기 위해서는 명령창에서 환경 변수를 설정하는 방법 외에 다른 방법을 사용해야 한다. 윈도우 95/98/Me라면 autoexec.bat 파일을 사용하고, 윈도우 NT/2000이라면 글로벌 환경 변수를 설정하는 별도의 방법이 존재한다.

우선, 윈도우 NT/2000이라면 다음처럼 한다.

1. 바탕화면의 "내 컴퓨터"를 오른쪽 클릭한다.

2. "고급" 메뉴를 택한다.

3. 가운데 "환경 변수" 메뉴를 택한다.

4. 두 창이 나오는데 위 쪽은 특정 로그인 사용자에 대한 환경 변수를 설정하는 것이고, 아래 쪽은 모든 사용자에게 적용되는 환경 변수를 설정하는 것이다. 관리자(Administrator) 권한을 갖고 있다면 아래 시스템 변수의 값을 설정할 수 있다. 상황에 맞게 선택하면 된다.

5. 버튼이 "새로 만들기", "편집", "삭제"가 있는데, 새로운 환경 변수를 만드는 경우에는 "새로 만들기"를 클릭하고, 기존 변수 값을 수정하고자 한다면 "편집"을 누른다. "삭제"는 물론 변수를 아예 없애는 경우에 클릭한다.

6. "새로 만들기"나 "편집"을 클릭하면 변수 이름과 값을 설정하게 되어 있는데 여기에 원하는 이름과 값을 입력하면 된다.


윈도우 95/98/Me라면, autoexec.bat 파일을 편집기로 열어서 "set 변수이름=값"을 아무데나 추가하면 된다.

자, 이제는 글로벌 환경 변수에 클래스패스 변수를 설정해 보자.

윈도우 NT/2000이라면 위의 단계를 차례로 실행한 후에, 이미 CLASSPATH 혹은 classpath 변수가 있다면 이를 편집하고, 없다면 새로 만들기를 하면 된다. 지금까지 예로 든 대로라면 다음의 값을 입력하면 된다.

C:\myClass;C:\otherClass\pr

<김상욱 주: 이렇게만 하면 한 디렉토리에서 방금 만들어진 클래스파일 즉 현재 디렉토리의 클래스파일을 찾지 못하기 때문에 현재 디렉토리를 나타내는 . 을 추가한다.>

<즉, .\C:\myClass;C:\otherClass\pr 이렇게 입력하여야 합니다.>


윈도우 95/98/Me라면 autoexec.bat 파일을 열어서, 다음의 한 줄을 추가한다.

set CLASSPATH="C:\myClass;C:\otherClass\pr"

<김상욱 주: 마찬가지 이유로 set CLASSPATH=".;C:\myClass;C:\otherClass\pr" 로 입력되어야 합니다.>


그렇다면, 자바 컴파일러와 자바 가상 머신은 이렇게 설정한 환경 변수와는 어떤 관계가 있을까? 자바 컴파일러와 자바 가상 머신은 -classpath 옵션을 지정하지 않으면 환경 변수 CLASSPATH 혹은 classpath의 값을 시스템으로부터 얻어서 이를 클래스패스로 사용한다. 클래스패스의 값을 환경 변수로부터 얻을 수 있도록 애초부터 만들어진 것이다.


jar 파일과 클래스패스
jar 파일은 클래스들을 묶어 놓은 것이다. 즉, 갑돌이는 자신이 만든 com.gabdol 이하의 모든 클래스를 하나의 파일인 gabdol.jar 파일로 묶을 수가 있다. 이렇게, gabdol.jar 파일을 C:\gg\cc 폴더 아래에 두었다면 이 압축 파일 내의 클래스를 역시 자바 컴파일러와 자바 가상 머신이 찾을 수 있도록 해야 한다. 이 때에는 파일 이름까지 포함해서 클래스패스에 추가해야 한다. 다음처럼 말이다.

set CLASSPATH=C:\gg\cc\gabdol.jar;C:\myClass;C:\otherClass\pr


마치며


지금까지 자바 프로그램의 가장 기초라고 할 수 있는 클래스패스에 대해서 알아 보았다. 필자는 이 글로 더 이상의 클래스패스에 대한 질문이 사라졌으면 하는 바램이다. 독자 여러분이 클래스패스를 고민하는 것도 시간 낭비이고, 저자가 이에 대해서 일일이 답변해 주는 것도 시간 낭비라고 생각된다. 만일, 독자 여러분이 클래스를 찾지 못한다는 에러를 만난다면 100% 클래스패스 설정 잘못이므로, 이 글을 잘 읽어서 근본적으로 클래스패스에 대해서 이해한 후에 문제를 해결하는 노력을 기울이도록 하자.