ABOUT

성능과 운영 안정성을 함께 끌어올리는 개발자입니다.

92% Positional Error Reduction
79% p95 Latency Improvement
90%+ Long Tasks Reduction

2022.02 · 한국장학재단

우수 멘티

한국장학재단 사회 리더 대학생 멘토링 IT

2022.10 · 동작구청

우수 인재상

동작구청 우수 SW 인재

2025.05 · (주) 그랩

프로그래밍 우수상

(주) 그랩 우수 프로그램 개발

2025.05 · AWSKRUG

AWS한국사용자모임 발표

AI agent 스크립트 튜닝 관련 발표

ComputerScience

Development

Engineering

Trouble Shooting

GUESTBOOK

첫 마음부터
함께 나누는 온기

방명록 작성하러 가기

SUBSCRIBE

최신소식을
편하게 만나보세요.

javac

도입

javac는 Java 소스 코드를 바이트코드 클래스 파일로 컴파일하는 JDK의 핵심 컴파일러 명령이다

Java를 배울 때 가장 먼저 마주치는 명령 중 하나가 바로 javac입니다. 많은 입문자는 이것을 단순히 “.java 파일을 .class 파일로 바꾸는 명령” 정도로 이해하지만, 실제 javac는 훨씬 더 많은 역할을 합니다.

Oracle의 공식 매뉴얼에 따르면 javac는 Java 언어로 작성된 module, package, type 선언이 들어 있는 소스 파일을 읽어 JVM에서 실행되는 class 파일로 컴파일합니다. 또한 단순 컴파일뿐 아니라 annotation processing도 직접 지원하며, 클래스패스·모듈패스·출력 디렉터리·대상 Java 릴리스·경고·디버그 정보까지 폭넓게 제어할 수 있습니다.

:contentReference[oaicite:1]{index=1}

필요성

javac를 이해하면 Java 소스 코드가 실제 실행 가능한 바이트코드로 바뀌는 빌드 흐름이 명확해지고, 오류와 옵션을 더 정확하게 해석할 수 있다

Java 개발에서 소스 코드 작성은 시작일 뿐입니다. 실제로는 컴파일 단계에서 타입 오류, 접근 제한, 경고, annotation processing, 의존성 위치, 출력 경로 같은 여러 요소가 함께 결정됩니다. 따라서 javac를 이해하면 단순한 문법 학습을 넘어 Java 빌드 시스템의 중심 축을 이해할 수 있습니다.

javac 이해가 특히 중요한 이유
  • 컴파일 오류와 실행 오류를 구분할 수 있다.
  • 클래스패스와 모듈패스 문제를 해석하기 쉬워진다.
  • --release, -d, -cp 같은 핵심 옵션을 목적에 맞게 쓸 수 있다.
  • annotation processing이 빌드에 어떻게 개입하는지 이해할 수 있다.
  • JDK 도구 전체에서 javac가 어떤 위치에 있는지 보이기 시작한다.

:contentReference[oaicite:2]{index=2}

정의

javac는 Java 선언을 읽고 class 파일로 컴파일하는 명령이며, JDK의 시스템 Java 컴파일러이자 command-line equivalent다

JDK 25 공식 문서의 이름 설명은 매우 직설적입니다. javac“read Java declarations and compile them into class files”라고 정의됩니다. 또한 jdk.compiler 모듈 문서에서는 이 모듈이 system Java compiler와 그 command line equivalent인 javac의 구현을 정의한다고 설명합니다.

:contentReference[oaicite:3]{index=3}

javac와 java의 차이

javac는 컴파일을 담당하고, java는 JVM을 시작해 클래스나 JAR를 실행하는 런처라는 점에서 역할이 분명히 다르다
명령 무엇을 하나 출력 / 결과
javac Java 소스를 읽어 컴파일 .class 파일 생성
java JVM 시작, 클래스 로드, main() 호출 프로그램 실행

즉, javac빌드 단계의 중심이고, java실행 단계의 중심입니다. 둘을 구분하지 못하면 “컴파일은 됐는데 왜 실행이 안 되지?” 같은 문제를 제대로 분리해서 보기 어렵습니다.

:contentReference[oaicite:4]{index=4}

컴파일 흐름

javac를 중심으로 보면 Java 프로그램의 기본 흐름은 소스 코드 → class 파일 → JVM 실행 구조로 이해하는 것이 가장 명확하다
.java 소스 파일 ↓ javac ↓ .class 바이트코드 파일 ↓ java ↓ JVM 시작 ↓ 클래스 로드 + main() 호출 ↓ 프로그램 실행

공식 매뉴얼은 javac가 소스 파일을 읽어 class 파일로 바꾸고, java는 JVM을 시작해 지정한 클래스를 로드하고 그 클래스의 main() 메서드를 호출한다고 설명합니다. 이 흐름을 정확히 잡아 두면 Java 도구 체계가 훨씬 덜 헷갈립니다.

:contentReference[oaicite:5]{index=5}

입력과 출력

javac는 source file 이름을 직접 받거나 argument file을 통해 입력을 받고, 기본적으로는 source 파일과 같은 디렉터리에 class 파일을 생성한다

공식 매뉴얼에 따르면 javac는 소수의 소스 파일일 때는 파일 이름을 직접 나열해서 받고, 소스가 많을 때는 @filename 형식의 argument file을 통해 입력을 받을 수 있습니다. 또한 여러 소스 파일을 함께 컴파일할 때는 이를 그룹으로 처리하고, 선언 사이의 의존성을 자동으로 해결합니다.

기본 동작은 각 소스 파일에서 생성된 class 파일을 같은 디렉터리에 두는 것이지만, 공식 문서는 -d 옵션으로 별도의 출력 디렉터리를 지정하는 것을 권장합니다.

:contentReference[oaicite:6]{index=6}

핵심 옵션

javac를 실무에서 제대로 쓰려면 모든 옵션을 외우기보다 출력 위치, 의존성 탐색 위치, 대상 릴리스, 경고, 디버그 정보 옵션부터 구분하는 것이 중요하다
옵션 역할 실무 감각
-d class 출력 디렉터리 지정 빌드 결과를 한 폴더로 분리
--class-path / -classpath / -cp 클래스패스에 라이브러리 위치 지정 모듈이 아닌 라이브러리 의존성 연결
--module-path / -p 모듈패스에 모듈형 라이브러리 위치 지정 모듈 시스템 기반 컴파일
--release 지정한 Java SE 릴리스 규칙과 API 기준으로 컴파일 과거 버전 호환 타깃 빌드
-source / -target 소스 언어 규칙 / 생성 class 타깃 제어 --release와 섞어 쓰면 안 됨
-g 디버그 정보 생성 디버깅 친화적 빌드
-Xlint 권장 경고 활성화 컴파일 시 품질 점검 강화
-deprecation deprecated 사용 상세 표시 구버전 API 점검
@filename 옵션과 파일 목록을 인자 파일로 전달 긴 커맨드 정리

:contentReference[oaicite:7]{index=7}

--release가 중요한 이유

현대 javac에서 과거 Java 버전 호환성을 의도할 때는 보통 -source와 -target만 따로 보기보다 --release를 먼저 떠올리는 편이 더 안전하다

공식 문서는 --release가 지정한 Java SE 릴리스의 언어 규칙에 따라 소스를 컴파일하고, 그 릴리스를 대상으로 하는 class 파일을 생성하며, 해당 릴리스의 Java SE와 JDK API를 기준으로 컴파일한다고 설명합니다. 또한 --release를 사용할 때는 -source-target을 함께 쓸 수 없다고 명시합니다.

:contentReference[oaicite:8]{index=8}

클래스패스와 모듈패스

javac는 참조할 라이브러리가 모듈이 아닌지, 모듈인지에 따라 class path와 module path를 다르게 사용한다

공식 매뉴얼은 플랫폼 외 라이브러리를 참조할 때, 라이브러리 코드가 모듈이 아니라면 class path에 두고, 모듈이라면 module path에 두라고 설명합니다. 또한 class path와 module path 옵션은 상호 배타적이지는 않지만, 모듈 컴파일에서 class path를 함께 쓰는 것은 흔한 형태가 아니라고 안내합니다.

또한 --class-path, -classpath, -cp를 지정하지 않으면 기본 사용자 클래스패스는 CLASSPATH 환경 변수 값이거나, 그마저 없으면 현재 디렉터리입니다.

:contentReference[oaicite:9]{index=9}

Annotation Processing

javac는 단순 컴파일러를 넘어 annotation processor를 직접 실행할 수 있는 도구이며, 생성된 새 소스가 있으면 여러 라운드에 걸쳐 처리할 수 있다

Oracle 문서는 javac가 annotation processing을 직접 지원한다고 설명합니다. 처리 방식은 -processor, --processor-path, --processor-module-path 같은 옵션이나 -proc:full, -proc:only, -proc:none으로 제어할 수 있습니다.

또한 processor가 새로운 소스 파일을 생성하면, 그 새 소스를 다시 스캔해 다음 라운드의 annotation processing이 이어질 수 있습니다. 즉, javac는 단순 번역기라기보다 컴파일 단계의 코드 생성 파이프라인 일부를 담당하기도 합니다.

:contentReference[oaicite:10]{index=10}

자주 보는 실전 예시

javac를 실제로 쓸 때는 전체 옵션을 외우기보다, 가장 자주 쓰는 명령 패턴 몇 개를 몸에 익히는 것이 훨씬 실용적이다
javac Hello.java

javac -d out src/com/example/Hello.java

javac -cp libs/mylib.jar -d out src/com/example/App.java

javac --release 17 -d out src/com/example/App.java

javac @options @sources

이 다섯 가지 정도만 익혀도 입문, 실습, 라이브러리 참조, 하위 버전 호환 빌드, 긴 명령 관리까지 상당수 상황을 커버할 수 있습니다.

:contentReference[oaicite:11]{index=11}

API로도 쓸 수 있다

javac는 셸 명령으로만 쓰는 도구가 아니라 JavaCompiler API와 ToolProvider를 통해 프로그램 안에서도 호출할 수 있다

jdk.compiler 모듈 문서와 javac 도구 가이드는 javac를 API로도 호출할 수 있다고 설명합니다. JavaCompiler API는 가장 유연한 방식이고, ToolProvider.findFirst("javac")는 command-line equivalent 기능을 프로그램에서 얻는 방식입니다.

즉, IDE나 빌드 도구가 내부적으로 Java 컴파일을 수행할 때 꼭 외부 프로세스로 javac를 띄우지 않아도 되는 구조가 이미 JDK에 제공됩니다.

:contentReference[oaicite:12]{index=12}

환경 변수와 긴 명령 관리

javac는 argument file과 JDK_JAVAC_OPTIONS를 통해 길고 복잡한 컴파일 명령을 관리할 수 있다

공식 문서는 @filename 형식의 argument file을 사용하면 매우 긴 옵션과 소스 파일 목록을 관리하기 쉬워진다고 설명합니다. 또한 JDK_JAVAC_OPTIONS 환경 변수의 내용은 실제 명령줄 인수 앞에 prepend된다고 명시합니다.

즉, 큰 프로젝트에서 반복되는 컴파일 옵션이 길어질수록, 무조건 한 줄 커맨드에 몰아 쓰기보다 argfile이나 빌드 도구 설정으로 분리하는 습관이 훨씬 좋습니다.

:contentReference[oaicite:13]{index=13}

자주 하는 오해

javac를 처음 배울 때는 컴파일과 실행을 섞어 생각하거나, -source / -target / --release를 혼동하거나, classpath와 module path를 구분하지 못해 실수가 많이 난다
  • javac가 프로그램을 실행한다고 생각함 → 실행은 보통 java가 담당합니다.
  • -d를 주지 않아 빌드 결과가 소스 폴더에 흩어짐 → 공식 문서도 별도 출력 디렉터리 사용을 권장합니다.
  • --release-source/-target를 함께 씀 → 공식적으로 함께 사용할 수 없습니다.
  • 모듈 라이브러리와 일반 라이브러리를 같은 방식으로 경로 지정함 → class path와 module path를 구분해야 합니다.
  • annotation processing이 빌드에 영향을 준다는 점을 놓침-proc, -processor-path 계열을 함께 봐야 합니다.
  • 경고를 무시함-Xlint, -deprecation이 문제를 더 빨리 드러내 줄 수 있습니다.

:contentReference[oaicite:14]{index=14}

공부 루틴

javac를 공부할 때는 옵션 이름만 외우기보다 Hello.java 하나를 가지고 출력 디렉터리, 클래스패스, 릴리스 타깃을 바꿔 보며 흐름을 체감하는 것이 가장 좋다
  1. javac Hello.java로 가장 기본 컴파일을 먼저 본다.
  2. -d out으로 출력 위치를 분리해 본다.
  3. -cp로 외부 라이브러리 참조를 붙여 본다.
  4. --release로 하위 버전 타깃 개념을 익힌다.
  5. -Xlint-g를 켜서 경고와 디버그 정보를 체험해 본다.
  6. 필요하면 @argfileJavaCompiler API까지 확장한다.

:contentReference[oaicite:15]{index=15}

디버깅과 분석 포인트

javac 문제를 볼 때는 문법 오류인지, 경로 문제인지, 버전 타깃 문제인지, processor 문제인지 먼저 나눠서 보는 것이 가장 중요하다
1
컴파일 단계 문제인지, 실행 단계 문제인지 먼저 분리한다.
2
-d를 빠뜨려 결과물이 예상 위치에 없는 것은 아닌지 본다.
3
라이브러리가 안 잡히면 class path와 module path를 다시 구분한다.
4
하위 버전 호환이 목표면 --release 설정을 먼저 의심한다.
5
빌드 과정에 코드 생성이 끼어 있으면 annotation processor 경로와 -proc 설정을 확인한다.
6
경고가 쌓이면 -Xlint-deprecation 출력부터 읽어 본다.

:contentReference[oaicite:16]{index=16}

요약

javac는 Java 소스를 바이트코드 class 파일로 바꾸는 핵심 컴파일러이며, 출력 위치·의존성 탐색·릴리스 타깃·annotation processing·경고 제어까지 담당하는 Java 빌드의 중심 도구다
  • javac는 Java 선언을 읽어 JVM용 class 파일로 컴파일한다.
  • ✅ 단순 컴파일뿐 아니라 annotation processing도 직접 지원한다.
  • -d, -cp, --module-path, --release, -Xlint, -g 같은 옵션이 실전 핵심이다.
  • --release는 특정 Java SE 릴리스를 목표로 컴파일할 때 중요한 옵션이며 -source/-target과 함께 쓸 수 없다.
  • @argfileJDK_JAVAC_OPTIONS로 긴 명령을 관리할 수 있다.
  • javac는 CLI 도구이면서 동시에 JavaCompiler API와 ToolProvider를 통해 프로그램 안에서도 호출할 수 있다.

:contentReference[oaicite:17]{index=17}

728x90