본문 바로가기
가치관 쌓기/개발 돌아보기

Lombok 동작 원리 이해하기(with. Annotation Processor) - 1

by simplify-len 2023. 7. 29.

lombok logo (https://github.com/projectlombok)

 

들어가기

Java 진영에서 Lombok 은 프로젝트 개발시 Must have Item 처럼 사용되곤한다. 그렇게 된 결정적인 이유는 Java 에서 관례적으로 주로 사용되는 Getter/Setter 를 애노테이션으로 쉽게 해결할 수 있기 때문이다.

그렇다면, Lombok 에서 자주 사용되는 Annotation 은 어떻게 동작하는 걸까? 그리고 Java의 Annotation 은 무엇일까? 엔지니어로 살아온 꽤 긴 시간동안 함께했지만, 깊게 살펴보지 못했던 Lombok 의 Annotation 동작 원리를 이해해보자.

Annotation 이란?

Java Annotation 은 Lombok 처럼 사용되기 위해 만들어진 것은 아니였다. wiki 를 살펴보면 다음과 같다. 

In the Java computer programming language, an annotation is a form of syntactic metadata that can be added to Java source code.[1] Classes, methods, variables, parameters and Java packages may be annotated. Like Javadoc tags, Java annotations can be read from source files. Unlike Javadoc tags, Java annotations can also be embedded in and read from Java class files generated by the Java compiler. This allows annotations to be retained by the Java virtual machine at run-time and read via reflection.[2] It is possible to create meta-annotations out of the existing ones in Java.[3] - https://en.wikipedia.org/wiki/Java_annotation

Java Annotation 은 Javadoc tags 처럼 메타데이터를 표현하기 위해 만들어졌으나, Java Annotation 을 활용해 reflection 을 활용해 코드를 관리할 수 있도록 할 수 있는 기능이 Java 1.6부터 등장하기 시작했다.

Lombok 에 대해서

Lombok 의 역사도 살펴보면, 2009년 Java 1.5  Annotation 이 처음 등장했을 때 만들어졌습니다. 그렇기 때문에 Lombok 프로젝트의 코드를 자세히 살펴보면 Java Compiler 가 Javac 도 있지만, 이클립트용 Compiler ECJ 가 있음을 알 수 있습니다.

https://github.com/projectlombok/lombok/

 

GitHub - projectlombok/lombok: Very spicy additions to the Java programming language.

Very spicy additions to the Java programming language. - GitHub - projectlombok/lombok: Very spicy additions to the Java programming language.

github.com

Lombok Github SourceCode 를 유심히 살펴보면 흥미로운 요소가 많습니다.

1. Ant 빌드 도구를 사용한다.

2. .class 파일을 .lombok 이라는 binary 를 Convert 한다.

3. Lombok 에서 사용할 수 있는 Custom된 Annotation을 만들 수 있었다. (즉 지금은 안된다)

 

1. Ant 빌드 도구를 사용한다.

Lombok 은 여전히 Ant 빌드 도구를 사용한다. Ant 빌드 도구는 분명히 Maven, Gradle 보다 성능적으로 좋지 않을 수 있다. 그러나, Lombok 에서 Ant 를 사용하는 이유는 Maven, Gradle 와 같이 최신 도구로 바꿔야할 만큼 이득이 있기 어렵기 때문이다. Ant 의 빌드 스크립트를 살펴보면 높은 복잡도를 가진 Task 가 있음을 알 수 있다. 그런 높은 복잡도를 Gradle 에 변환시켜야 한다면 이 또한 큰 공수가 될 것이다.

참고 자료 - https://github.com/projectlombok/lombok/wiki/LOMBOK-CONCEPT:-Why-does-lombok-build-with-ant

 

LOMBOK CONCEPT: Why does lombok build with ant

Very spicy additions to the Java programming language. - projectlombok/lombok

github.com

 

2. .class 파일을 .lombok 이라는 binary 를 Convert 한다.

lombok 을 사용하는 프로젝트를 살펴보면 lombok SourceCode 를 볼 수 없다. 왜 그럴까?

가장 핵심이 되는 AnnotationProcessor 를 사용하는 코드를 보고싶겠지만, 볼 수 없다. 왜 그럴까?

의존성 일부 캡쳐

결론부터 이야기하면- 오픈소스에 좀더 개발자들을 관여시키기 위해 그런 결정을 한 것이다.

Lombok 프로젝트 초기에는 '만약 네가 필요한 Annotation 이 있다면, Lombok 프로젝트를 Fork 한 뒤에 네가 필요한 Annotation 을 만들어서 PR 보내.' 라는 목적으로 lombok 이라는 확장자로 개발자에 노출되지 않도록 빌드 도구 변환시킨 것이다.

...
<mappedresources>
    <multirootfileset basedirs="${packing.basedirs}">
        <patternset refid="packing.shadowed" />
    </multirootfileset>
    <firstmatchmapper>
        <globmapper from="*.class" to="*.SCL.lombok" />
        <identitymapper />
    </firstmatchmapper>
</mappedresources>
...// complier.ant.xml

lombok 프로젝트 내에서 확인할 수 있다.

3. Lombok 에서 사용할 수 있는 Custom된 Annotation을 만들 수 있었다. (즉 지금은 안된다.)

과거 Lombok 1.5 버전대에서는 개발자가 직접 Lombok의 JavacAnnotationHandler 을 구현함으로써 Custom된 Annotation 을 구현할 수 있었다.

https://www.baeldung.com/lombok-custom-annotation

안타깝게도 Lombok 의 최신 버전에서는 해당 Class 는 남아있지만, Custom Annotation 을 만들 수 없게 되었다. Java 버전이 1.8 이 되면서 Javac 에 대한 도구지원이 변경되었고, 1.9부터는 Util로 사용되었단 Javac 가 없어지면서 더 이상 활용할 수 없게 되었다.

AST (Abstract Syntax Tree)/Annotation Prcoessor

Lombok 은 어떻게 코드를 생성할 수 있었을까? 이 것을 설명하기 위해서는 AST 와 Annotation Processor 를 이해해야만 한다.

AST 는 Java Complier 가 Class Code 를 이해하기 위해 Tree 구조로 Code 을 이해하기 위한 수단으로 사용된다.

만약에 다음과 같은 코드가 있다고 가정해보자.

package io.agistep.todo.domain;

import io.agistep.event.Event;
import io.agistep.event.EventHandler;
import lombok.Getter;

@Getter
public class Todo {

	private TodoIdentity id;
	private String text;
	private boolean done;
	private boolean hold;

	Todo(String text) {
		TodoCreated created = TodoCreated.newBuilder()
				.setText(text)
				.build();
		apply(created);
	}
  
	@EventHandler(payload = TodoCreated.class)
	void onCreated(Event anEvent) {
		this.id = new TodoIdentity(anEvent.getAggregateIdValue());
		this.text = ((TodoCreated) anEvent.getPayload()).getText();
		this.done = false;
	}
    ...
}

이 코드를 이해하기 위해 Java Compiler 는 줄을 세우기 시작한다.

가장 크게는 Package, Import, Annotation, Code 이렇게 나눌 수 있고, 생성자 Todo 코드에서는 각 문자열마다 트리 구조를 만들어 분리할 수 있다. 

댓글