Language/Java

[Java] Javac의 동작 과정은 어떻게 될까?

PgmJUN 2024. 11. 21. 01:44

 

 

문제상황

 

Java/Spring을 Kotlin/Spring으로 마이그레이션 하는 과정에서 Kotlin의 Lombok 사용에 어려움을 겪었다.

그리고 그 이유 Lombok이 Javac Annotation Processing 과정에서 처리되는데, Javac보다 Kotlinc가 더 먼저 작동하여 Javac에서 Annotation Processing 과정을 통해 생성되는 코드를 Kotlin 코드에서는 알 수가 없기 때문임을 알게 되었다.

 

이 과정에서 Javac의 Annotation Processing을 포함한 전반적인 과정을 흝어보고 싶어 학습하여 기록해 두게 되었다.

 

 

javac의 동작 과정

 

javac의 동작과정에 대해 자세히 파헤쳐보자.

 

javac는 대략 아래 7가지 단계로 컴파일을 수행한다.

 

1. Parsing: 소스 코드를 읽고 AST 생성.

2. Annotation Processing: 애노테이션 처리 및 코드 생성.

3. Semantic Analysis: 타입 검증과 의미 분석.

4. Intermediate Code Generation: 중간 코드 생성.

5. Optimization: 코드 최적화.

6. Bytecode Generation: 바이트코드 생성.

7. Class File Output: .class 파일 저장.

 

 

1. Parsing (구문 분석)

Javac는 입력받은. java파일의 소스 코드를 읽고, 이를 추상 구문 트리(Abstract Syntax Tree, AST)로 변환한다.

 

세부내용

 토큰화(Tokenization): 코드의 각 요소를 토큰으로 나눈다. (ex. public class HelloWorld  [public] [class] [HelloWorld])

 구문 분석(Syntax Analysis): 토큰을 기반으로 코드 구조를 파악하고, AST를 생성한다.

 이 단계에서 구문 오류(Syntax Error)를 발견하면 컴파일이 중단된다.

 

 

2. Annotation Processing (애노테이션 처리)

@Override, @Deprecated, 또는 사용자 정의 애노테이션(예: Lombok) 등을 처리한다.

 

세부내용

 Annotation Processor가 등록된 애노테이션에 대해 지정된 작업을 수행한다.

 Lombok 라이브러리는 이 단계에서 보일러플레이트 코드를 생성한다.

 출력: 수정된 AST 또는 추가 생성된 파일(예: 자동 생성된 클래스).

 

 

3. Semantic Analysis (의미 분석)

코드의 의미를 검증한다.

 

세부내용

 변수나 메서드가 선언된 위치와 호출 위치를 연결한다.

 타입 체크(Type Checking): 변수와 메서드 호출이 올바른 타입으로 사용되는지 확인한다.

 이 단계에서 발견된 오류는 컴파일 에러로 표시된다.

 

 

4. Intermediate Code Generation (중간 코드 생성)

AST를 바탕으로 JVM이 이해할 수 있는 중간 표현(Intermediate Representation, IR)을 생성한다.

 

세부내용

 이 단계는 소스 코드를 바이트코드로 변환하기 위한 중간 단계로, 구체적인 JVM 명령어로 변환되지 않았지만 구조가 더 단순화된 상태이다.

 

 

5. Optimization (최적화)

생성된 중간 코드를 최적화한다.

 

세부내용

 중복된 연산 제거.

 불필요한 코드 제거.

 더 효율적인 명령어로 대체.

 이 단계는 JVM의 Just-In-Time(JIT) 컴파일러 최적화보다는 덜 강력하지만, 일부 성능 최적화를 수행한다.

 

 

6. Bytecode Generation (바이트코드 생성)

최적화된 중간 코드를 JVM에서 실행될 수 있는 바이트코드(.class 파일)로 변환한다.

 

 

7. Class File Output (클래스 파일 출력)

최종적으로 생성된 .class 파일을 디스크에 저장한다.