프로그램을 설계하다보면 같은 목적을 위해 만든 클래스들을 구분하여 따로 묶어 관리하는 것이 좋을 때가 있습니다.

스타크래프트의 경우라면 Zealot 클래스와 Dragoon 클래스, Reaver 클래스는 같이 묶어서

관리하는 것이 좋겠습니다. 왜냐하면 모두 같은 프로토스 유닛을 의미하는 클래스이기 때문

입니다. 하지만 Tank클래스, Hydra클래스등과는 같이 묶어서 관리하는 것은 어울리지

않겠지요.  롯데리아 전산실에서 전국 롯데리아 지점에서 판매하는 모든 상품에 대한 통계

를 내는 프로그램을 만든다고 했을 때 판매되는 햄버거들 클래스와 판매량등 통계에 관련된

클래스를 나누어 관리한다면 좋겠습니다.

 

 왜 이렇게 관리하는가 하면 현실적으로는 나누어진 클래스들은 적어도 단일된 조직에서

담당하고 있는 클래스일 확률이 높기 때문입니다.

, 모아진 클래스를 설계/유지하는 담당 프로그래머가 1명이거나, 적어도 같은 팀

조직 내에 존재하는 경우입니다. 추후에 모아진 클래스가 변경이 되거나 수정이 되더라도

같은 조직 안에서의 의사소통만으로 해결할 수 있기 때문에 상당히 실전적이 됩니다.

물론 1명의 프로그래머가 이 모든 것을 담당한다 하더라도 논리적으로 연관관계가 높은 것

들만 따로 묶어서 관리한다면 효율성이 높아진다 하겠습니다.

또다른 이유는 Naming Conflict (이름 충돌) 문제때문입니다.

프로그램을 하다보면 같은 이름의 클래스가 등장할 수 있습니다.

예를 들어 A팀 프로그래머와 B팀 프로그래머가 각각 클래스를 만든후 공교롭게 이름을

같은 이름으로 짓게 되면 두 클래스는 어떻게 될까요? 한쪽 이름을 변경해야 하나요?

자바의 이런 묶음 성질 때문에 그렇게 하지 않아도 됩니다.

 자바에는 이렇게 클래스들을 집합으로 묶어서 관리할 수 있는데 이런 단위를 패키지

(package)라고 합니다.

물론 패키지가 갖는 기능은 단순히 묶어서 관리할 수 있는 것 이상의 의미를 가지지만

필자의 경험으로는 일단 이렇게 생각하여도 무난하다고 여기고 있습니다.

그리고 클래스가 속해있는 패키지가 다르면 같은 이름의 클래스라도 전혀 다른 클래스

입니다. 이를 이용해서 클래스를 Naming Conflict를 해결하고 클래스를 유일하게 지정할 수

있고 관리할 수 있습니다.

 

패키지 선언

해당 클래스를 원하는 패키지로 묶는 방법은 아주 단순합니다.

클래스 정의를 하기에 앞서 가장 먼저 패키지 선언을 하면 되는 것입니다.

 

package protoss;                      // Zealot클래스는 protoss패키지에 포함됩니다.

public class Zealot {

                           . . .

}

예제 2 - 16 Zealot.java

            

Zealot 클래스는 package protoss; 선언을 통해서 protoss패키지에 속하게 되었습니다. 하지만 이로 인해서 변하게 되는 의미는 상당합니다.

 

패키지 클래스의 컴파일 I

 

가장 현실 적인 문제를 한번 살펴보도록 하겠습니다. 예제 2 - 17 Player클래스는

질럿을 하나 만들고 질럿으로 하여금 소리를 내게 하는 프로그램입니다.

 

public class Player {

                          public static void main(String[] args) {

                                        Zealot z = new Zealot();                   // z 질럿을 만듭니다.

                                        z.sound();                                                                 // z 질럿을 소리내게 합니다.

                          }

}

예제 2 - 17

 

프로그램을 실행하기에 앞서 컴파일을 해보겠습니다.

 

그림 2 - 1 [compile (Zealot.java)]

Zealot.java 파일은 성공적으로 컴파일 되었는데, Player.java 파일은 컴파일되지 않았습니다.

이렇게 Player.java 파일이 컴파일되지 않은 이유는 단 한군데 바뀐 곳 질럿 클래스에서

패키지 선언이 추가 되었기 때문입니다.

 

Player클래스가 컴파일을 성공하지 못한 첫째의 이유는

예제 2 - 17의 다음 문장이 더 이상 성립하지 않습니다.

 

             Zealot z = new Zealot();    

 

왜냐하면 더 이상 Zealot 클래스는 없기 때문입니다.

Zealot 클래스가 protoss 패키지에 속했기 때문에 Zealot클래스는 더 이상 존재하지 않고

존재하는 것은 클래스의 정식 이름은 protoss.Zealot 클래스입니다.

즉 패키지.클래스만 존재합니다. 흔히 Vector클래스라고 부르는데, 정확히 말하면 자바에는

Vector클래스는 없고, java.util.Vector 클래스가 있는 셈입니다.

물론 Date클래스가 있는게 아니고 java.util.Date 클래스 혹은 java.sql.Date 클래스가 있다

는 것이 옳겠습니다.

 

예제 2 - 17 Player클래스가 컴파일이 되게 하기 위해서는 질럿을 만드는 부분은

다음과 같이 수정해야만 합니다.

 

             protoss.Zealot z = new protoss.Zealot();  

 

Player클래스에서 질럿을 만들고자 할 때 모두 Zealot대신에 protoss.Zealot으로 고쳐주면

됩니다. 객체를 만들 때 해당 생성자의 패키지를 포함하여 전체 클래스의 불러 주어야 하기

때문이지요. 특정한 패키지에 속하지 않은 클래스는 패기지 이름을 적어주지 않아도 되지만

Zealot 클래스는 protoss 패키지에 속하기 때문에 반드시 패키지 이름을 써주어야 합니다.

Zealot protoss.Zealot은 전혀 다르기 때문이지요.

 

패키지의 쉬운 이용 import

 

하지만 Player클래스에서 모든 질럿에 대해서 protoss.Zealot으로 쓰기에는 이름도 길고, 의미도 중복되는 점이 있어서 꺼려지는 부분입니다.

다른 방법이 있는데 바로 import 키워드를 이용하는 것입니다.

import 키워드의 위치는 package 선언보다는 늦게 class 선언보다는 먼저 하면 됩니다.

            

             import protoss.*;                              // protoss 패키지를 사용합니다.

public class Player2 {

                          public static void main(String[] args) {

                                        Zealot z = new Zealot();                   // z 질럿을 만듭니다.

                          }

}

예제 2 - 18 Player2.java

 

package는 클래스를 구분하고 나누어 관리를 하기 위해서 사용이 됩니다.

import는 원하는 클래스의 객체를 사용할 때 모든 레퍼런스와 생성자에 패키지 이름을 붙이는 것이 불편하므로 이를 해결하는 방법입니다..

 

패키지 클래스의 컴파일 II

Player2클래스를  컴파일 해보겠습니다.

그림 2 - 2 [compile incorrect (Player2.java)]

 

예상과는 다르게 Player2.java의 컴파일이 또 다시 안됩니다.

여기에는 또 다른 문제가 숨어 있기 때문입니다.

 

자바 컴파일러는 클래스를 컴파일을 할 때 컴파일이 완료 때까지 나타나는 모든 클래스를

찾아서 확인합니다.

Player클래스는 protoss.Zealot 클래스를 사용하기 때문에 protoss.Zealot클래스를 찾기

시작 합니다. 어디에서 찾기 시작할까요? 그건 프로그래머가 지정해주어야 합니다.

만약 지정해주지 않은다면 자바 컴파일러는 클래스패스라는 환경변수에 지정된 디렉토리에

서 찾습니다.

 

% 자바에서 classpath 지정방법 %

 

Windows 환경이라면 MS-DOS창에서

             c:>echo %CLASSPATH% <enter>

 

Unix 또는 Linux환경이라면 Shell 에서

             shell> echo $CLASSPATH <enter>

 

하게 되면 나타나는 디렉토리들이 자바가 클래스파일을 찾는 디렉토리입니다.

디렉토리가 나타나면  그 디렉토리의 하위 디렉토리에서까지 자동으로 찾아주나요?

천만의 말씀입니다. 딱 그 디렉토리에서만 찾습니다. 없으면 어떻하냐구요?

당근 컴파일 안됩니다.

 

프로그래머가 자바에게 클래스파일을 찾도록 지정해주는 방법은 컴파일시나 실행시에

옵션 –classpath (또는 –cp)과 함께 찾고자 하는 디렉토리를 나열해 주면 됩니다.

 

Windows 환경이라면 MS-DOS창에서

             c:tutorial\chapter2>javac –classpath classes Sample.java <enter>

 

Sample.java를 컴파일할 때 c:\tutorial\chapter2\classes 디렉토리에서 찾으라고

알려주는 내용입니다.

 

Unix 또는 Linux환경이라면 Shell 에서

             home>javac –cp  .:/home/myclasses:/home/myclasses/utility Sample.java <enter>

 

Sample.java를 컴파일할 때 현재 디렉토리 , /home/myclasses 디렉토리 그리고

/home/myclasses/utility 디렉토리에서 찾으라고 알려주는 내용입니다.

 

Windows Unix 또는 Linux의 클래스패스 구분자는 Windows는 세미콜론이고

Unix Linux는 콜론입니다.

 

 

Player2.java를 컴파일하기 위해서는 protoss.Zealot가 있는 디렉토리를 자바 컴파일러한테

classpath옵션을 이용해서 컴파일러에게 알려주어야 합니다.

그렇다면 protoss.Zealot 클래스가 어느 디렉토리에 일단 어디에 있는지 알아내야 합니다.

Zealot.java를 컴파일해서 얻어진 클래스는 Zealot.class라는 이름으로 생깁니다.

그런데 Zealot.class Zealot클래스일까요? protoss.Zealot 클래스인가요? 아니면 이것도 저것도 아니고 제 3의 클래스일까요? 이름만 가지고는 어떤 클래스인지 알 수 없습니다.

오히려 혼란스럽기 까지 합니다.

적어도 여기에서의 Zealot.class protoss.Zealot 클래스입니다.

패키지.클래스형태의 클래스 파일, protoss.Zealot 클래스등이 파일시스템에 존재하고자

한다면 지켜야 할 규칙이 있는데 존재하는 곳이 반드시 디렉토리 구조로 이루어져야 한다는

것입니다.

protoss.Zealot.class protoss 디렉토리 아래에 Zealot.class  저장되어야 한다는 것

입니다. 만약 이런 구조로 되어 있지 않다면 Player2.java를 컴파일 할 때처럼 프로그램은

아무 문제가 없어도 컴파일이 되지 않습니다.

 

com.lotteria.BigRib.class com 디렉토리 아래 lotteria 디렉토리 아래 BigRib.class로 표현되어야 하겠습니다. 이렇게 표현되어 있지 않다면 실제로 BigRib.class com.lotteria 패기지에 속한 BigRib클래스라하더라도 사용할 수 없게 되는 것입니다.

 

자바의 컴파일러에는 –d 라는 옵션이 있습니다. 컴파일된 결과 파일을 특정한 폴더 아래로

모으라는 뜻입니다. 컴파일되는 클래스는 즉, protoss.Zealot클래스인데요 classes 디렉토리 아래에 위치하게 됩니다.  classes 디렉토리를 조회해 보면 protoss라는 디렉토리가

생겨있고, protoss디렉토리 아래에 Zealot.class가 있는 것을 알 수 있습니다.

이처럼 패키지.클래스는 디렉토리 아래에 클래스 파일형태로 남아있어야 합니다.

 

이제 Player2.java를 컴파일 해볼까요?  Player2 클래스는 protoss.Zealot클래스를

사용하기 때문에 protoss.Zealot가 있는 디렉토리의 위치를 컴파일러에게 반드시

알려주어야 합니다. classpath라는 옵션을 사용해서 알려주도록 하겠습니다.

protoss.Zealot 클래스는 classes 디렉토리에 있습니다. protoss.Zealot클래스는 패키지.클래스의 형태이므로 classes 디렉토리에 아래에 패키지 아래에 클래스 파일이 자리 잡고 있기 때문이지요. 그리고 Player2 클래스의 위치도 classes 디렉토리로 모으겠습니다.

그림 2 - 3 [compile correct (Player2.java)]

Player.java가 성공적으로 컴파일 되었습니다. classes 디렉토리에를 조회해보면

Player.class가 생긴 것을 볼수 있는데 이는 Player클래스는 어떤 패키지에도 속하지 않았기 때문에 지정한 디렉토리 classes 에 그냥 생기게 됩니다.

 

그다음 실제로 Player클래스를 실행해보고 있는데요, Player클래스에는 특별한 메소드

public static void main(String[] args) 메소드를 포함하고 있기 때문에 실행을 시킬수 있고

해당 메소드에서는 protoss.Zealot객체를 하나 만들고 소리를 내게 하고 있습니다.

 

그림 2 - 4 [ run (Player2 )]

질럿이 한마디 하지요. 언제 들어도 멋있네요.

Posted by
,