lombok과 그 사용법

4 minute read

NOTE: 본 글은 IDINCU 기술 블로그에 썼던 글을 약간의 각색/수정을 해서 다시 올린 것이다.

자바로 코딩해 본 사람이라면 누구나 간단한 객체 하나 만드는데 getter/constuctor/equals/toString 등을 기계적으로 타이핑하거나 혹은 IDE의 도움으로 자동으로 생성되는 getter/setter 등을 만들어본 경험이 있을 것이다. 필요한 것은 하나의 클래스와 몇 개의 멤버 변수 정도의 작은 클래스인데 코드는 그에 비해 무척 길게 된다. 마치 배보다 배꼽이 큰 격! 게다가 변수명이라도 바꾸려 하면 변수명 변경 -> getter/setter 함수 이름 변경 -> 매개변수 이름 변경과 같은 순으로 작업을 하는 경우가 부지기수일 것이다. 물론 IDE를 쓰면 알아서 쓱쓱 잘 바꿔준다. 하지만 이것조차도 귀찮은 일이다. 원래 프로그래머는 귀찮은 것을 극도로 싫어하는 닝겐인간 아니겠는가?

이런 귀찮은 작업을 해결하기 위한 방법을 찾던 중 Lombok 프로젝트를 찾았고 이런 작업을 더 빠르게 처리하고 문제해결에 집중하기 위해 프로젝트에 도입했다.

Lombok이란?

Lombok은 간단히 말하면 @Getter, @Setter 같은 annotation 기반으로 이런 작업을 없애주는 프로젝트다. Lombok 홈페이지에서는 Spice up your java 라고 한다. 자세한 설명은 나중에 하고 바로 코드로 얘기해보자. 아래 코드는 평소에 작성하는 멤버 변수 몇 개를 가지고 있는 클래스이 정의다.

public class Level {

    private Integer levelId;

    private String levelName;

    private long minPoint;

    private long maxPoint;

    public boolean equals(Object o) {
        if (o == this) return true;
        if (!(o instanceof Level)) return false;
        final LevelVO other = (Level) o;
        if (!other.canEqual((Object) this)) return false;
        final Object this$levelId = this.levelId;
        final Object other$levelId = other.levelId;
        if (this$levelId == null ? other$levelId != null : !this$levelId.equals(other$levelId)) return false;
        final Object this$levelName = this.levelName;
        final Object other$levelName = other.levelName;
        if (this$levelName == null ? other$levelName != null : !this$levelName.equals(other$levelName)) return false;
        if (this.minPoint != other.minPoint) return false;
        if (this.maxPoint != other.maxPoint) return false;
        return true;
    }

    public int hashCode() {
        final int PRIME = 59;
        int result = 1;
        final Object $levelId = this.levelId;
        result = result * PRIME + ($levelId == null ? 0 : $levelId.hashCode());
        final Object $levelName = this.levelName;
        result = result * PRIME + ($levelName == null ? 0 : $levelName.hashCode());
        final long $minPoint = this.minPoint;
        result = result * PRIME + (int) ($minPoint >>> 32 ^ $minPoint);
        final long $maxPoint = this.maxPoint;
        result = result * PRIME + (int) ($maxPoint >>> 32 ^ $maxPoint);
        return result;
    }

    public boolean canEqual(Object other) {
        return other instanceof Level;
    }

    public Integer getLevelId() {
        return this.levelId;
    }

    public String getLevelName() {
        return this.levelName;
    }

    public long getMinPoint() {
        return this.minPoint;
    }

    public long getMaxPoint() {
        return this.maxPoint;
    }

    public void setLevelName(String levelName) {
        this.levelName = levelName;
    }

    public void setMinPoint(long minPoint) {
        this.minPoint = minPoint;
    }

    public void setMaxPoint(long maxPoint) {
        this.maxPoint = maxPoint;
    }
}

이 코드에 Lombok을 적용하면 아래와 같이 간단하게 바뀐다.

import lombok.Getter;
import lombok.Setter;
import lombok.EqualsAndHashCode;

@EqualsAndHashCode
public class Level {

    @Getter    
    private Integer levelId;

    @Getter @Setter
    private String levelName;

    @Getter @Setter
    private long minPoint;

    @Getter @Setter
    private long maxPoint;   
}

위에서 확인할 수 있듯 사용법은 매우 간단하다. Annotation의 의미는 그야말로 문자 그대로 직관적으로 알 수 있다. @Getter 는 각 필드의 getter 함수를, @Setter_는 setter 함수를 생성하고 _@EqualsAndHashCode 는 equals 함수와 hashCode 함수를 자동으로 생성해주는 역할을 합니다. 가장 많이 사용하는 lombok annotation을 아래에 정리해봤습니다.

가장 많이 사용하는 lombok annotation 정리

@Getter / @Setter

  • Getter와 Setter 함수를 생성합니다.
  • getter/setter 관례에 따라서 get 필드명, set 필드명 메소드가 생성됨
  • 접근제어 : AccessLevel 지정을 통해서 접근레벨을 제한할 수 있습니다. (PUBLIC, PROTECTED, PACKAGE, PRIVATE)
    @Getter(AccessLevel.PACKAGE), @Setter(AccessLevel.PRIVATE) 
    

@EqualsAndHashCode

  • 코드에서 객체의 비교 등의 용도로 사용되는 equals(), hashCode() 메소드의 코드를 생성한다.
  • 특정 필드를 제외할 수가 있다.
@EqualsAndHashCode(exclude={field1, field2})

@ToString

  • 객체의 내용을 문자열로 변환해주는 toString() 메소드를 대신할 수 있다.
  • 역시 특정 변수를 제외할 수 있다. @ToString(exclude=”field1”)

@Log

  • 자동으로 logging을 위한 필드인 private static final Logger log 변수가 추가된다. 이후 로그를 출력하려는 곳에서 log.error(), log.warn(), log.debug(), log.info() 형태로 사용하면 된다.

@Data

  • 모든 필드에 대한 getter, setter와 toString, equals, hashcode, final로 지정됐거나 @NonNull로 명시된 필드에 대한 값을 받는 생성자 메소드 코드를 생성힌다.

@AllArgsConstructor

  • 모든 필드에 대한 값을 받는 생성자를 생성한다.
  • 접근제어 : AccessLevel 지정을 통해서 접근레벨을 제한할 수 있다. (PUBLIC, PROTECTED, PACKAGE, PRIVATE)

간략한 Lombok의 동작 원리

이번에는 lombok의 동작 원리를 간단히 설명해본다. Annotation 처리를 통해 새로운 파일을 생성하는 다른 annotation processor와 달리 lombok의 동작은 독특하다. Java source를 컴파일할 때 annotation processor로 등록된 lombok processor가 parsing 된 AST(Abstract Syntax Tree)에 있는 annotation을 확인하고 그에 적합한 메쏘드(정확히는 sub-AST)를 생성해서 parsing된 AST에 적절한 위치에 넣어준 이후 이를 byte code로 변환하게 된다. 좀 더 자세한 동작 원리는 조금 특이한데 이번 글에 담기에 꽤나 많으니 기회가 있을 때 설명하기로 한다.

프로젝트의 Lombok 적용 방법

가장 많이 사용하는 IDE인 Eclipse와 요즘 많이 사용하고 있는 IntelliJ에서 lombok을 도입해서 사용할 수 있습니다. 자세한 적용 방법은 아래와 같습니다.

dependency 추가

// build.gradle
compile 'org.projectlombok:lombok:1.12.2'
// pom.xml
<dependencies>
  ...
 
  <!-- Project lombok -->
  <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.12.2</version>
  </dependency>
  ...
</dependencies>

IDE에서 Lombok 환경을 설정하는 방법

Eclipse
  1. Lombok Download 에서 lombok.jar 를 다운로드한다.
  2. java -jar lombok.jar 로 Installer를 실행한다.
  3. Specify Location 을 클릭한 후 플러그인을 설치할 Eclipse의 바이너리를 선택한다.
  4. Install / Update 를 클릭한다.
IntelliJ
  1. Lombok IntelliJ Plugin : Lombok Plugin 를 다운로드해서 Settings(Preference) > Plugins > Install Plugins from Disk 에서 다운받은 ZIP 파일을 지정하면 설치가 된다. 또는 Browse Repository에서 lombok plugin을 찾아서 설치한다.
  2. modules setting에서 compiler->annotation processor에서 enable annotation processing을 활성화합니다.

Lombok의 장단점

자바는 syntatic sugar가 많지 않은 언어기에 작성할 코드가 많은 굉장히 힘든 언어지만 lombok을 사용하면 코드를 획기적으로 줄일 수 있다. 하지만 lombok을 사용해 만들어진 코드를 다른 팀원이 컴파일하기 위해서는 다른 팀원도 모두 lombok을 설치하고 IDE에 설정을 해야 한다는 단점이 있다. 물론 IDE에서 delombok/lombok 과 같은 변환 기능을 수행해주긴 하긴 하지만 그럴 경우 lombok을 쓰는 의미가 적어진다. 따라서 협업을 할때는 lombok을 사용할지 말지 사전에 협의를 해야 한다. 그리고 현재 JDK6/7에서 제공하는 annotation processor 를 위한 공식 API를 사용하는 것이 아니라 private API 를 사용하는 형태기 때문에 향후 version에서는 lombok을 사용할 수 없을 수도 있다. 하지만 공식 사이트에서 JDK8을 지원한다고 하는 것으로 확인할 수 있고 JDK9가 나올 시점까지는 괜찮을 것으로 생각한다.