Technology/Java

Java의 패키지에 대해 알아보자! (feat. 클래스패스, import)

ikjo 2022. 9. 1. 02:53

패키지란?

패키지란 '서로 관련된 클래스(인터페이스 등 포함)들의 그룹'으로 클래스를 효율적으로 관리할 수 있다. 이때 클래스의 실제 이름은 클래스 자체의 이름뿐만 아니라 패키지명을 모두 포함한 이름으로, 이를 FQCN(Fully Qualified Class Name)이라고 한다. 따라서 클래스 이름이 같더라도 패키지만 다르면 다른 클래스인 것이다.

 

패키지에 포함된 클래스에 대한 소스코드를 작성할 때는 항상 패키지를 첫 번째 문장(주석, 공백 제외)에 선언해주어야한다. 이때 패키지 안에 또 다른 패키지가 존재할 수도 있는데, 이는 .을 이용하여 구분한다. 

 

package ikjo.java; 

class Example { // 최상위 패키지 ikjo에 속한 java 패키지 그리고 java 패키지에 속한 Example 클래스

    public static void main(String[] args) {
        System.out.println("hello!!");
    }
}

 

참고로 패키지명의 경우 대소문자를 모두 허용하기는 하지만, 클래스명과 쉽게 구분하고자 일반적으로 소문자로 작성한다.

 

만일, 어떤 클래스에 대한 소스코드를 작성할 때 별다른 패키지에 포함되있지 않은 경우, 위와 같이 package를 별도로 선언하지 않아도 되는데, 이 경우 자동으로 자바에서 기본적으로 제공하는 '이름없는 패키지(unnamed package 또는 default package)'에 속하게 된다.

 

 

클래스패스란?

앞서 생성한 ikjo.java.Example 클래스 파일을 자바 실행기(java.exe)로 실행시켜보자.

 

 

하지만 결과는 ClassNotFoundException 예외와 함께 에러가 발생했다. 이는 자바 실행기가 해당 클래스 파일을 찾을 수 없기에 발생한 에러이다. (해당 클래스 파일 내 main 함수를 불러오지 못했을 경우에도 해당 에러가 발생할 수 있다.)

※ 소스코드 파일을 찾는 것이 아님에 유의

 

자바 실행기는 클래스패스(classpath)를 기준으로 클래스 파일을 찾는데, 클래스패스(classpath)란 자바 실행기, 컴파일러, JVM 등이 클래스 파일의 위치를 찾는데 사용되는 경로이다. 이때 별도로 클래스패스를 지정하지 않을 경우 기본적으로 현재 디렉토리(.)가 클래스패스로 지정된다. 즉, 명령 프롬프트 기준으로 현재 위치는 "C:\User\user"에 해당되므로 "C:\User\user\ikjo\java\Example.class"에 해당하는 클래스 파일이 존재하는지 찾는 것이다.

 

물론, 현재 디렉토리 위치가 해당 클래스 파일이 존재하는 디렉토리(ikjo 패키지의 상위 디렉토리)에 있다면 클래스 파일을 찾을 수 있다. 하지만 매번 프로그램을 실행할 때마다 클래스 파일이 존재하는 디렉토리로 이동하는 것은 매우 번거로운 일이므로 이러한 번거러움을 없애고자 클래스 파일들을 실행하기 앞서 클래스패스를 지정해준다.

 

이때 클래스 파일의 루트 디렉토리(ikjo 패키지의 상위 디렉토리)를 클래스패스로 지정해줘야 하는데, 현재 ikjo 패키지의 상위 디렉토리는 "C:\인텔리제이\codingtest\out\production\codingtest"이므로, 이를 클래스 패스로 지정해주면 제대로 동작할 것이다.

 

Windows 10 기준 '시스템 속성 - 고급 - 환경 변수 - 시스템 변수'에서 '새로 만들기' 또는 '편집'을 통해 CLASS라는 시스템(환경) 변수의 값에 "C:\인텔리제이\codingtest\out\production\codingtest"을 할당해주면된다.

 

 

이때 여러 개의 클래스패스를 지정할수도 있는데, 각각의 경로를 세미콜론 ;으로 구분해주면 된다. 이제 다시 실행해보자.

 

 

이전과 다르게 현재 위치가 "C:\User\user"임에도 불구하고, 해당 클래스 파일이 정상적으로 실행되는 것을 확인할 수 있다.

 

-classpath 옵션

보통 자바 실행기 보다는 인텔리제이와 같은 IDE로 프로그램을 실행하는 경우가 많다. IDE로 자바 프로그램을 실행할 때도 이러한 클래스패스가 적용된다.

 

여기선 인텔리제이를 기준으로 main 메서드가 있는 Example 클래스를 Run 버튼을 눌러 실행시켜보자.

 

 

앞서 자바 실행기로 실행시켰을 때와 달리 -classpath 옵션(-cp 옵션)과 함께 클래스 파일이 저장되어 있는 디렉토리가 지정되는데, 이때 -cp 옵션은 자바 프로그램 실행 시 일시적으로 클래스패스를 지정해주는 역할을 한다.

 

참고로 인텔리제이는 기본적으로 자바 소스코드 파일에 대한 클래스 파일(컴파일 결과물)을 ~out/production/(모듈명) 디렉토리에 저장하는데, 자바 프로그램 실행 시 이 경로를  -cp 옵션에 지정해준다. 따라서 사용자가 인텔리제이를 통해 해당 프로그램을 실행하기 전 별도로 클래스패스를 지정해줄 필요가 없는 것이다.

 

Complier Output path

 

이처럼 클래스패스를 지정하는데에는 앞서 다루었던 환경 변수로 직접 설정하는 방법-cp 옵션으로 일시적으로 설정하는 방법이 있다. 이때 자바 공식 문서에 따르면 환경 변수로 직접 설정하는 방법 보다는 -cp 옵션을 사용하는 것을 권장하고 있는데, 그 이유는 각각의 자바 프로그램별로 독립적으로 클래스패스가 지정됨으로써 특정 프로그램의 클래스패스가 다른 프로그램에 영향을 주지 않기 때문이다.

 

The class path is the path that the Java runtime environment searches for classes and other resource files. The class search path (more commonly known by the shorter name, "class path") can be set using either the -classpath option when calling a JDK tool (the preferred method) or by setting the CLASSPATH environment variable. The -classpath
 option is preferred because you can set it individually for each application without affecting other applications and without other applications modifying its value.

 

 

import 키워드를 통해 다른 패키지의 클래스에 쉽게 접근하기

어떤 클래스에 대한 소스 코드를 작성할 때 다른 패키지에 있는 클래스와 의존관계가 있을 수 있다. 이때 다른 패키지에 있는 클래스에 접근하려면 해당 클래스의 FQCN을 작성해주어야 하는데, 이는 굉장히 번거로운 일이다.

 

package ikjo.java; 

class Example {

    public static void main(String[] args) {
        ikjo.spring.Test test = new ikjo.spring.Test();
    }
    
    // ...

 

ikjo - spring 패키지에 있는 Test 클래스의 인스턴스를 생성하기 위해 패키지명을 일일이 붙여주고 있는데, import 키워드를 이용하면 아래와 같이 패키지명을 생략함으로써 간결하게 작성할 수 있게 된다.

 

package ikjo.java;

import ikjo.spring.Test; // 또는 import ikjo.spring.*;

class Example {

    public static void main(String[] args) {
        Test test = new Test();
    }
    
    // ...

 

이때 ikjo.spring 패키지 내에 Test외에 다른 클래스들에도 접근하는 경우 ikjo.spring.*로 표기하여 해당 패키지에 속한 모든 클래스를 import 해줄수도 있다. 물론, 그 하위 패키지에 있는 클래스에 접근 가능해지는 것은 아니다.

 

이러한 import문은 컴파일러에게 해당 클래스에 사용된 또 다른 클래스들의 패키지에 대한 정보를 제공하는 역할을 한다. 

 

참고로 java.lang 패키지에 속한 클래스들은 패키지명 없이 사용할 수 있는데, 이들은 자주 사용되고 중요한 클래스들(String, System 등)이기에 별도 지정하지 않아도 "import java.lang.*;"으로 선언되기 때문이다.

 

static import

다른 패키지에 속한 클래스에 접근할 때 역시 마찬가지로 정적(static) 멤버에 접근할 때는 다음과 같이 클래스명으로 접근해야한다. (물론, 인스턴스를 생성해서 접근할 수도 있다.)

 

package ikjo.java;

import ikjo.spring.Test;

class Example {

    public static void main(String[] args) {
        Test.staticMethod();
    }
    
    // ...

 

이때 static import문을 사용하면 클래스명을 생략하고 간편하게 정적 멤버에 접근할 수 있다.

 

package ikjo.java;

import ikjo.spring.Test.*;

class Example {

    public static void main(String[] args) {
        staticMethod();
    }
    
    // ...

 

 

참고자료

  • 도우출판 "자바의 정석"
  • 위키북스 "스프링 입문을 위한 자바 객체 지향의 원리와 이해"
  • https://docs.oracle.com/javase/7/docs/technotes/tools/windows/classpath.html