상세 컨텐츠

본문 제목

터미널에서 Java 컴파일하기 (javac -d bin과 패키지 디렉토리 구조)

개발

by 코드스키 2026. 7. 24. 07:44

본문

 javac -d bin과 Java 패키지 구조 대표 이미지
javac -d bin과 Java 패키지 구조 대표 이미지

평소에 IntelliJ로 자바를 짜다 보니 빌드 버튼만 눌렀지 안에서 무슨 일이 일어나는지는 몰랐다. 그런데 과제 제출 형식이 .java 파일만 압축해서 올리는 거라서, IDE 없이 터미널에서 컴파일하고 실행까지 되는지 미리 확인해야 했다. 이 김에 javac/java 명령을 제대로 정리해두기로 했는데, 함정이 두 개 있었다. 하나는 package 선언이 들어가면 디렉토리 구조가 그거랑 일치해야 한다는 거고, 다른 하나는 -d 옵션으로 출력 디렉토리를 따로 지정하는 패턴이었다. 처음에는 error: file not found만 계속 떠서 30분을 날렸다.

제일 단순한 경우

패키지 선언 없는 한 파일짜리부터 보면 이렇다.

// Hello.java
public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello, world!");
    }
}
javac Hello.java   # → Hello.class 생성
java Hello         # → Hello, world!

javac.java를 컴파일해서 같은 폴더에 .class를 만들고, java Hello로 실행한다. 클래스 이름은 확장자를 빼고 적어야 한다. java Hello.class라고 쓰면 안 된다.

함정 하나 — 패키지 선언과 디렉토리 구조

자바는 package 선언이 강제하는 디렉토리 구조가 있다.

// src/com/myapp/Main.java
package com.myapp;

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello from package!");
    }
}

이 파일이 package com.myapp;라고 선언했다면, 파일이 있는 디렉토리가 정확히 com/myapp/Main.java여야 한다. 아니면 컴파일은 통과해도 실행할 때 클래스를 못 찾는다는 에러가 뜬다. 내가 처음에 저지른 실수가 이거였다.

~/java/study$ javac -d bin src/com/assignment01/Main.java
error: file not found: src/com/assignment01/Main.java

이 에러는 그 경로에 파일이 진짜 없다는 뜻이었고, 원인은 cd를 잘못된 위치에서 한 거였다. 컴파일은 항상 프로젝트 루트, 그러니까 src/bin/이 보이는 위치에서 해야 한다.

함정 둘 — -d 옵션이 실제로 하는 일

기본적으로 javac.class 파일을 .java 파일과 같은 디렉토리에 만든다. 소스랑 빌드 결과물이 섞이는 게 싫으면 -d 옵션으로 출력 폴더를 지정한다.

javac -d bin src/com/myapp/Main.java

이게 편한 이유는 -d가 패키지 구조까지 자동으로 만들어준다는 거다. 위 명령을 실행하면 bin/ 안에 com/myapp/Main.class가 자동으로 생긴다. bin/com/myapp/을 미리 만들어둘 필요가 없다.

프로젝트 루트
├── src/
│   └── com/myapp/
│       └── Main.java
└── bin/                  ← 컴파일 결과
    └── com/myapp/
        └── Main.class

실행할 때 또 걸리는 부분

컴파일이 끝나고 실행하려는데 여기서도 헷갈렸다.

# 안 됨
java bin/com/myapp/Main.class
java Main

# 이렇게
java -cp bin com.myapp.Main

-cp bin(또는 -classpath bin)은 클래스 파일을 찾을 루트 디렉토리를 지정하는 거고, com.myapp.Main은 패키지를 점으로 구분한 풀 클래스명이다. 슬래시가 아니라 점이고, .class 확장자는 안 붙인다. java가 클래스를 찾는 방식은 클래스패스의 루트(bin)에서 시작해서 패키지 이름을 폴더 경로로 바꾸고 그 안에서 Main.class를 찾는 식이라서, 내가 직접 그 디렉토리에 들어가서 실행할 필요가 없다.

여러 파일 프로젝트로 넘어가면

실습 과제 폴더 구조는 대략 이렇게 생겼다.

assignment/
├── src/
│   └── com/assignment01/
│       ├── Main.java
│       ├── Calculator.java
│       └── Helper.java
└── bin/

Main.javaCalculatorHelper를 쓴다고 하면, 같은 패키지 안에 있는 경우는 import 없이도 되고, 다른 패키지면 import com.assignment01.Calculator; 식으로 명시한다. 세 개를 일일이 컴파일할 필요 없이 와일드카드를 쓰면 된다.

javac -d bin src/com/assignment01/*.java

서브패키지가 여러 개면 와일드카드만으로 부족한데, Linux/macOS라면 find가 깔끔하다.

find src -name "*.java" > sources.txt
javac -d bin @sources.txt

@sources.txt는 argument file 패턴이다. javac@로 시작하는 인자를 만나면 그걸 파일로 보고 안의 내용을 명령줄 인자로 펼쳐서 처리하는데, 이 방식이면 파일 수가 수백 개여도 명령줄 길이 제한에 안 걸린다.

실행은 똑같이 java -cp bin com.assignment01.Main이다.

자잘하게 헷갈렸던 점들

javac는 사실 똑똑해서, Main.java만 컴파일 명령을 줘도 그 안에서 참조하는 Calculator, Helper를 알아서 같이 컴파일해준다. 단, 그 파일들 위치를 알 수 있어야 하는데 -sourcepath 옵션으로 소스 루트를 지정해주면 된다.

javac -d bin -sourcepath src src/com/assignment01/Main.java

서드파티 JAR를 쓸 때는 -cp에 같이 명시해야 한다.

javac -cp lib/mysql-connector.jar -d bin src/com/myapp/Main.java
java -cp bin:lib/mysql-connector.jar com.myapp.Main

여러 JAR면 Linux/macOS는 :, Windows는 ;로 구분한다. 이 차이 때문에 OS 간 스크립트를 짤 때 종종 사고가 난다.

학교에서 받은 과제 텍스트가 EUC-KR이거나 UTF-8 BOM이 섞여 있을 때가 있는데, IntelliJ에서는 파일 우하단 인코딩 표시를 클릭해서 바꿀 수 있고, 터미널에서는 -encoding 옵션으로 명시하면 된다.

javac -encoding UTF-8 -d bin src/com/myapp/*.java

package 선언이 없는 클래스는 default package에 속하는데, 다른 패키지의 클래스에서 default package 클래스를 import할 수 없다는 제약이 있어서 좀 큰 프로젝트라면 처음부터 패키지를 잡아두는 게 낫다.

-d binbin 디렉토리가 이미 존재해야 동작하고 자동으로 만들어주진 않는다. 이거 모르고 에러를 본 적이 있어서, 그냥 미리 만들어두는 습관을 들였다.

mkdir -p bin

참고한 자료는 Oracle 공식 javac 매뉴얼, Baeldung의 "Compile All Java Classes in Directory Structure with javac"와 "Compiling Java *.class Files with javac", 그리고 Berkeley CS61BL의 Command Line for Packages 문서다.

소스 경로와 클래스 출력 경로를 구분하기

패키지가 선언된 Java 파일을 컴파일할 때 -d bin은 클래스 파일의 루트 디렉터리를 지정한다. javac가 패키지 이름에 맞춰 bin/com/example/Main.class를 만든다. 실행할 때는 파일 경로가 아니라 클래스패스 루트와 완전한 클래스 이름을 준다.

Oracle javac 공식 문서
Oracle javac 공식 문서
dev.java 패키지 학습 문서
dev.java 패키지 학습 문서
Oracle class path 문서
Oracle class path 문서
Oracle jar 명령 공식 문서
Oracle jar 명령 공식 문서
javac -d bin src/com/example/Main.java
java -cp bin com.example.Main
jar --create --file app.jar -C bin .

ClassNotFoundException이 나면 클래스 파일이 없는지만 보지 말고 -cp가 패키지 디렉터리의 부모를 가리키는지 확인한다. 이 기준을 잡고 나니 IDE 밖에서 컴파일해도 디렉터리 구조가 덜 헷갈렸다.

공식 자료

관련글 더보기