본문 바로가기
IT 공부/자바와 웹 애플리케이션

[lombok] - 롬복(lombok) 작동 원리 (어노테이션 프로세서)

by exdus3156 2024. 1. 2.

1. 롬복(lombok)이란?

롬복(lombok)이란 위와 같이 소스코드에 어노테이션을 붙여 자동으로 getter, setter, builder 메소드를 소스코드 파일에 편집해주는 라이브러리다. 사실상 문서 편집기다.

물론 이클립스나 인텔리제이에서 단축키로 메소드들을 빠르게 만들 수 있지만, 롬복을 사용하는 것이 훨씬 더 간편하기 때문에 자주 사용된다. 왜냐하면 롬복은 아예 소스코드 자체를 생략해주는 효과가 있기에 가독성이 좋아지기 때문이다.

( ※ 어노테이션을 모르면 롬복 원리를 전혀 이해할 수 없다! 까먹었으면 복습하자! → 링크 )

 

 


2. 작동 원리 - 어노테이션 프로세서

롬복을 보통은 인텔리제이와 같이 IDE로 사용하기 때문에 그 원리를 딱히 파악할 필요는 없지만, 이왕 공부한 거 간단한 원리 정도를 요약해 정리하는 것도 나쁘지 않을 것 같았다.

먼저, 롬복은 프레임워크나 실행 프로그램, 라이브러리와는 다르게 동작한다는 사실을 알아야 한다. 왜냐하면 롬복은 어노테이션을 통해 자바 컴파일러가 직접 소스코드(문자열)을 붙이고 난 다음 컴파일하는 식으로 작동하기 때문이다.

즉, 롬복 라이브러리의 어노테이션을 import 해 사용하면, 이것은 "자바 컴파일러"에 의해 직접 소스코드에 문자열을 삽입하여 각종 getter, setter, constructor, ... 를 기입하는 방식이다.

난 처음에 롬복이 메이븐이나 그레이들의 플러그인과 연계되는 줄 알았는데 아니었다. 자바 컴파일러가 롬복을 실행하는 것이다. 그래서 빌드 툴과는 상관없다. 그러니까, 롬복 jar 파일만 있으면 메모장 소스코드에서 롬복을 사용하고 콘솔에서 컴파일해도 정상 작동한다는 것이다.

 


그렇다면 자바 컴파일러(javac)는 어떻게 롬복을 식별하고 또 사용하는 것일까? 

일단 롬복 공식 홈페이지(링크)를 보면, 롬복을 사용하게 만드는 javac 명령어는 의외로 단순하다!

Just put lombok on the classpath when compiling with any javac (version 1.6 - 1.8): 
javac -cp lombok.jar ....

자바 컴파일러를 실행할 때 클래스패스(cp) 옵션으로 롬복(jar 파일)을 지정해주고 있다. 이것만으로도 신기하게도 어노테이션을 읽어 소스코드를 편집하는 롬복 기능이 수행된다.

 

그렇다면 고작 클래스패스(cp)의 옵션으로 롬복 jar을 어떻게 사용하는 걸까? 자바8 공식 홈페이지의 javac 스펙(링크)을 보면 -cp 옵션에 대해 아래와 같은 설명이 있다.

Specifies where to find user class files, and (optionally) annotation processors and source files. This class path overrides the user class path in the CLASSPATH environment variable.

If neither CLASSPATH, -cp nor -classpath is specified, then the user class path is the current directory.

If the -sourcepath option is not specified, then the user class path is also searched for source files.

If the -processorpath option is not specified, then the class path is also searched for annotation processors.

 

클래스패스(링크)는 기본적으로 javac에게 소스코드의 루트 디렉토리 위치를 지정해줄 때 사용한다. 하지만 이것으로 끝나는 것이 아니다. 위 공식 설명을 보면, -cp 설정은 어노테이션 프로세서도 식별할 수 있다고 나와 있다.

어노테이션 프로세서란, javax.annotation.processing.Processor 인터페이스를 구현한 클래스로서, 자바 컴파일러는 소스코드를 컴파일하기 전에, 클래스패스로부터 Processor 인터페이스를 구현한 코드를 [META-INF] - [services] 디렉토리에서 찾아 어노테이션 프로세서로 자동 등록하고 실행한다.

lombok.jar 디렉터리 구조

롬복이 이렇게 어노테이션 프로세서를 구현하고 이 폴더에 넣어서 배포했기 때문에, 자바 컴파일러에게 자동으로 등록되고 실행되어 소스코드의 @getter, @setter 들을 읽어 소스코드를 편집해주는 것이다. 즉, cp 옵션이 자동으로 어노테이션 프로세서를 찾는데 활용되기 때문에 위와 같은 간단한 javac 명령만으로도 프로세서를 등록해 롬복 기능을 사용할 수 있다.

 

 


3. 빌드 툴과 함께 사용될 때

사실 롬복을 저렇게 콘솔 창에서 javac -cp 옵션으로 사용하는 경우는 거의 없다. 설마 메모장에 소스코드를 작성하고 따로 javac로 컴파일하지는 않을 것이다..

대부분 개발할 때는 메이븐이나 그레이들과 같은 빌드 도구와 함께 사용한다. 빌드툴이 롬복을 활용하는 원리 또한 지금까지 설명한 원리 그대로다. 즉, 컴파일러에게 롬복 어노테이션 프로세서를 등록하면 된다. 빌드 과정에는 컴파일 태스크가 포함되어 있으며, 따라서 빌드툴의 설정 파일에서 이를 표현해주면 되는 것이다.


예를 들어, 그레이들(gradle)은 아래와 같은 방식으로 롬복을 프로세서에 등록한다. 롬복 홈페이지에서 공식으로 권장하는 그레이들 사용 방식이다. 아래 코드는 홈페이지에서 말 그대로 복사 붙여넣기했다.

그레이들은 (편리하게도!) 구조와 사용 방식을 메이븐보다는 명시적으로 드러내서 아주 쉽게 이해된다. 대놓고 어노테이션 프로세서(annotationProcessor)를 등록하는 모습이다. 롬복은 소스코드를 편집하는 기능에 불과하기 때문에 (실제로 @retention 정책은 source다!) 컴파일 단계에서만 라이브러리에 의존하면 될 것이다. 즉, 롬복 라이브러리는 배포될 때는 제외되어야 한다. (compileOnly)

test 태스크 단계도 따로 지정해줘야 한다. 당연하다. 테스트 코드라도 결국 내가 작성한 클래스 파일을 빌드해 사용하는 것인데, 바로 그 클래스 파일에 롬복 어노테이션을 사용했으니 말이다.


메이븐도 비슷한 원리다.

<scope> provided 태그는 의존성 라이브러리를 컴파일 시 클래스패스에 추가한다. 위에서 설명한대로 javac가 자동으로 클래스패스에서 어노테이션 프로세서를 식별하고 실행한다.