[Java] CLI 환경에서의 컴파일

오늘·2022년 6월 4일

컴파일러 과제를 진행하다가 Java를 CLI(Command-Line Interface) 환경에서 컴파일하고 실행시킬 일이 있었다.
단순히 java, javac 만 알면 손쉽게 될 줄 알았지만 예상치 못한 여러 에러들을 만나서 정리를 해 두려고 한다.

환경

  • windows 11, 64bit
  • cmd

CLI에서 기본적인 Java 컴파일

우선 CLI 환경에서 가장 기본적인 컴파일 방법을 알아보자.

Java는 '컴파일러 언어'이면서 '인터프리터 언어'이다. High-level language로 작성되는 .java 파일을 컴파일러가 컴파일하여 '바이트 코드(Byte code)'라고 불리는 중간코드 파일인 .class 파일을 생성한 후, .class 파일을 인터프리터를 통해 실행하는 방식이다.

여기서 .java 파일을 .class 파일로 만드는 것은 javac를 통해, .class파일을 실행하는 것은 java를 통해 이루어진다.

가령 main 메소드를 포함하고 있는 Test.java라는 파일을 CLI 환경에서 실행시키고자 한다면

javac Test.java

를 실행하게 되면 Test.class 파일이 생성되고,

java Test

를 실행하게 되면 Test 클래스 내부의 main method가 실행되고 결과를 확인할 수 있다. 실행시 주의할 점은 .class라는 확장자를 제외한 이름만 사용해야 한다는 것이다.

여러 개의 파일을 한 번에 컴파일 및 실행

참고한 블로그
https://kamang-it.tistory.com/entry/Ant%EC%9E%90%EB%B0%94%ED%8C%8C%EC%9D%BC-%EC%97%AC%EB%9F%AC%EA%B0%9C-%EC%BB%B4%ED%8C%8C%EC%9D%BC%ED%95%98%EA%B3%A0-%EC%8B%A4%ED%96%896?category=712828
이 블로그에서는 Ant를 사용했지만, 나는 Ant 대신 cmd에서 직접 컴파일했다.

CLI환경에서 javac와 java를 사용할 때 반드시 알아두어야 할 점이 있다.

어떤 클래스가 다른 클래스의 멤버를 사용하고 있다면, 두 클래스 파일이 한 번에 컴파일 되고 실행되어야 한다는 것이다.

예를 들어 Test.java에서 Class1.java와 Class2.java의 멤버를 사용하고 있다고 하자.
이때 Test.java를 실행하기 위해서는 Class1.java와 Class2.java를 함께 컴파일 및 실행을 해 주어야 한다.

그 과정을 순서대로 살펴보자.

기본 컴파일 방법

우선 컴파일 과정이다. 이때 컴파일로 생성되는 클래스 파일들이 하나의 폴더에 들어 있어야한다. 이를 위해 -d 옵션을 이용한다.

javac -d classes Test.java Class1.java Class2.java

이처럼 컴파일을 하면, Test.class, Class1.class, Class2.class 파일이 classes 폴더에 위치하게 된다.

흩어진 .java 파일들을 컴파일하는 방법

함께 컴파일해야할 .java 파일들이 너무 많거나 다른 폴더에 흩어져 있을 때 사용할만한 유용한 방법이 있다. (위의 블로그를 참고함)

위의 파일들의 디렉토리 구조가 아래와 같다고 가정하자.
/Project
+-----/Test
+----------Test.java
+-----/Class1
+----------Class1.java
+-----/Class2
+----------Class2.java

이때 Test, Class1, Class2 폴더의 상위 폴더인 Project로 이동 후 다음 명령을 실행한다.

dir /s/b *.java > source_list.txt

/s/b 는 현재 폴더부터 하위 폴더의 모든 파일경로를 출력하는데, 이를 *.java를 통해 java 파일만 걸러준 것이다. 이를 source_list.txt 파일에 저장한다.

그리고 아래와 같이 실행한다.

javac -d classes @source_list.txt

실행이 끝나면, 마찬가지로 Test.class, Class1.class, Class2.class 파일이 classes 폴더에 위치한다.

실행 방법

컴파일이 끝난 후, 필요한 .class 파일을 한 번에 실행해야 한다. 실행할 때는 실행할 class 파일이 어디에 위치했는지, 즉 클래스 패스를 명시해주어야 하는데, 이때 -classpath 또는 -cp 옵션을 이용한다.

java -cp classes; Test

이렇게 하면 Test 클래스에 있는 main 메소드를 실행한다.

추가 - jar 파일을 포함할 때

참고한 블로그
https://dev-nicitis.tistory.com/8

.java 파일 내부에 import문 등으로 실행가능한 jar 파일의 내용을 포함하는 경우가 있다. 이때 -cp 옵션을 javac, java 모두 명시해주면 된다.

만약 Test.java를 실행할 때 lib폴더에 있는 something.jar 파일이 필요하다면,

javac -d clsses -cp lib/something.jar Test.java

의 형태로 컴파일 후,

java -cp classes;lib/something.jar; Test

의 형태로 실행할 수 있다. -cp 옵션에서 ;은 여러개의 클래스 패스를 구분하기 위한 구분자로, Linux 환경에서는 :을 이용하면 된다.

발생했던 에러

마지막으로 위의 컴파일 및 실행 과정을 진행하면서 발생했던 에러들과 해결 방법을 하나씩 정리해보려고 한다.

cannot find symbol

코드 상에서 다른 클래스의 멤버인 변수 또는 함수를 찾지 못하는 경우이다.
만약 Test.java 에서 Class1.java의 멤버변수 name을 사용한다고 가정해보자

Test.java

package package1;

import package1.Class1;

public class Test {
    public static void main(String[] args) {
        Class1 c1 = new Class1();
        System.out.println("Hello " + c1.name);
    }
}

Class1.java

package package1;

public class Class1 {
    public String name = "Jay";
}

이 상태에서 Test.java를 컴파일하기 위해 아래와 같이 실행한다면,

javac -d classes Test.java

cannot find symbol 에러가 발생한다.

이는 위에서 언급했던 여러개의 파일을 한 번에 컴파일하지 않아서 발생한 에러이다.

위의 명령 대신

javac -d classes Test.java Class1.java

를 실행하면 정상적으로 컴파일된다.

(나는 이 에러를 해결하는데 시간이 꽤 걸렸다. pacakage 명시와 import를 모두 잘 했다고 생각해서 하나씩 컴파일해도 문제가 없을거라고 생각했지만 그것이 아니었다.)

could not find or load main class ~

참고한 블로그
https://lastcow9000.github.io/java/Java-CLI-%EC%8B%A4%ED%96%89-%EC%98%A4%EB%A5%98-%EC%8B%9C(could-not-find-or-load-main-class-/.)/

이 에러는 위처럼 정상적으로 컴파일을 마친 후 java로 실행을 시도할 때 발생하는데, 두 가지 경우가 있다. 이 두가지가 발생하는 상황이 어떻게 다른지는 정확히 알 지 못해서, 두 가지 모두 발생하지 않도록 한 해결 방법 위주로 적어보려고 한다.

Caused by : java.lang.NoClassDefFoundException

이 경우 내 추측으로는 클래스패스의 명시가 올바르지 않을 때 발생하는 것 같다.

Caused by : java.lang.ClassNotFoundException

이 경우는 실행하는 파일명의 명시가 올바르지 않을 때 발생하는 것 같다.

해결한 방법

세 가지를 적용했다.

  • 실행 시 -cp 옵션을 통해 클래스 파일의 위치를 표시
  • 파일 명을 적을 때 패키지 명을 포함
  • java 명령을 패키지보다 상위의 디렉토리에서 실행

첫 번째 것은 상단의 흩어진 java 파일을 컴파일하는 방법에서 적은 내용과 동일하다.

아래의 것들에 대해 조금 자세히 이야기 해보자.

위의 cannot find symbol 에러에서 사용했던 예제 Test.java 와 Class1.java의 상단을 보면 모두

package package1;

처럼 패키지를 명시했다.

이 경우, Test.class를 실행하려면 아래와 같이 패키지명을 포함한 파일명을 명시해주어야 한다.

java -cp classes; pacakge1.Test

그리고 이 명령의 실행을 package1보다 상위 디렉토리에서 실행했다.

이 방법을 적용하니 could not find or load main class ~ 에러가 해결되었다.

글을 마치며

사실 과제 자체는 실행만 하면 되는 단순한 것이었는데, Java에 대한 이런 자세한 부분을 몰라서 꽤 오랜 시간 삽질을 하고 말았다....

Java를 좀 더 공부해야겠다는 생각과 함께 IDE에 대해 감사한 마음을 가지게 되었다...ㅎ
아마 IDE상에서 과제를 진행했다면 훨씬 빨리 끝났겠지만, 다음에 CLI 환경에서 Java를 다루어 볼 일이 생기면 그때는 삽질을 하지 않을테니 꽤 좋은 경험을 했다 생각한다.

profile
Junior Mobile 개발자

0개의 댓글