Post

[Spring Boot] 롬복으로 리팩토링 하기🛠

롬복(Lombok)이란?

어노테이션 기반으로 코드를 자동완성 해주는 라이브러리

  • Getter, Setter, toString, constructor() 과 같은 코드를 자동완성 시킬 수 있다.
  • 코드의 가독성을 향상시킨다.
  • 코드 자동 생성을 통해 생산성을 향상시킨다.
  • 빌더 패턴이나 로그 생성 등 다양한 방면으로도 활용할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ArticleForm {
    private String title;
    private String content;

    public ArticleForm(String title, String content) {
        this.title = title;
        this.content = content;
    }

    @Override
    public String toString() {
        return "ArticleForm{" +
                "title='" + title + '\'' +
                ", content='" + content + '\'' +
                '}';
    }
}

이렇게 생성자, toString() 메서드가 들어간 DTO 코드를

1
2
3
4
5
6
@AllArgsConstructor
@ToString
public class ArticleForm {
    private String title;
    private String content;
}

이렇게 줄일 수 있다!

롬복 설치하기

  • IntelliJ 2020.03 이후 버전에서는 기본 Plugin으로 Lombok이 설치되어 있다.
  • 설치되어 있지 않다면, File > Settings > Plugins > Marketplace에 lombok을 검색하고 설치해 준다.
  • Spring Boot 프로젝트에서는 다음과 같이 build.gradle 파일 dependencies에 추가 후 동기화하면 롬복 관련 라이브러리를 인터넷에서 자동으로 다운로드 해준다.
1
2
	compileOnly 'org.projectlombok:lombok'
	annotationProcessor 'org.projectlombok:lombok'

Lombok installed

롬복 사용하기

@Slf4j

println() 문으로 확인하고 싶은 데이터를 검증하면 기록에 남지 않을 뿐더러 서버의 성능에 악영향을 끼친다. 로깅 기능으로 로그를 남겨 서버에서 일어나는 일을 기록해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Slf4j
@Controller
public class ArticleController {
    @Autowired
    private ArticleRepository articleRepository;
    @GetMapping("/articles/new")
    public String newArticleForm() {
        return "articles/new";
    }

    @PostMapping("/articles/create")
    public String createArticle(ArticleForm form) {
        Article article = form.toEntity();
        log.info(article.toString());

        Article saved = articleRepository.save(article);
        log.info(form.toString());
        return "";
    }
}

롬복의 @Slf4j 어노테이션을 사용하면 log.info()를 통해 원하는 내용을 로그에 남길 수 있다.

logging

@ToString

  • Lombok이 ToString 메서드를 생성해준다. includeFieldNames 옵션으로 필드명을 포함할지 설정할 수 있다.
  • 추가 옵션을 설정하지 않으면 MyClass(foo=123, bar=234) 형태로 출력된다.
  • 출력을 원하지 않는 변수에는 @ToString.Exclude 어노테이션을 붙여준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class LombokTestForm {
    private String name;
    private Integer age;
    private String gender;
    private String phoneNumber;

    @Override
    public String toString() {
        return "LombokTestForm{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                ", phoneNumber='" + phoneNumber + '\'' +
                '}';
    }
}
1
2
3
4
5
6
7
@ToString
public class LombokTestForm {
    private String name;
    private Integer age;
    private String gender;
    private String phoneNumber;
}

@Getter/@Setter

  • Getter, Setter 메서드를 만들어 준다.
1
2
3
4
5
6
7
8
9
@Getter
@Setter
@ToString
public class LombokTestForm {
    private String name;
    private Integer age;
    private String gender;
    private String phoneNumber;
}

@NonNull

  • null 검사문을 생성한다.

With Lombok

1
2
3
4
5
6
7
8
public class NonNullExample extends Something {
  private String name;
  
  public NonNullExample(@NonNull Person person) {
    super("Hello");
    this.name = person.getName();
  }
}

Vanilla Java

1
2
3
4
5
6
7
8
9
10
11
12
13
public class NonNullExample extends Something {
  private String name;
  
  public NonNullExample(@NonNull Person person) {
    super("Hello");
    // Null 검사문을 생성한다.
    // 만약 person이 존재하지 않으면 NullPointerException을 던진다.
    if (person == null) {
      throw new NullPointerException("person is marked non-null but is null");
    }
    this.name = person.getName();
  }
}

@NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor

  • @NoArgsConstructor : 매개변수가 없는 생성자를 생성한다.
  • @RequiredArgsConstructor : 특별한 처리가 필요한 각 필드에 대해 1개의 매개변수가 있는 생성자를 생성한다. 초기화되지 않은 모든 최종 필드는 매개변수를 가지며 선언된 위치에서 초기화되지 않은 @NonNull로 표시된 모든 필드도 가져온다. @NonNull로 표시된 필드의 경우 명시적 Null 검사도 생성된다.
  • @AllArgsConstructor : 클래스의 각 필드에 대해 1개의 매개변수가 있는 생성자를 생성한다. @NonNull로 표시된 필드는 해당 매개변수에 대해 null 검사를 수행한다.

With Lombok

1
2
3
4
5
6
7
8
9
10
11
@RequiredArgsConstructor(staticName = "of")
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class ConstructorExample<T> {
  private int x, y;
  @NonNull private T description;
  
  @NoArgsConstructor
  public static class NoArgsExample {
    @NonNull private String field;
  }
}

Vanilla Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class ConstructorExample<T> {
  private int x, y;
  @NonNull private T description;
  
  // @RequiredArgsConstructor에 의해 만들어지는 메서드
  // 특정 변수만을 활용한 생성자를 만들어준다.
  private ConstructorExample(T description) {
    if (description == null) throw new NullPointerException("description");
    this.description = description;
  }
  
  public static <T> ConstructorExample<T> of(T description) {
    return new ConstructorExample<T>(description);
  }
  
  // @AllArgsConstructor에 의해 만들어지는 메서드
  // AccessLevel을 Protected로 설정했으므로 protected로 만들어진다.
  // 모든 매개변수가 있는 생성자를 생성하고 description에 대해 Null 검사를 수행한다.
  @java.beans.ConstructorProperties({"x", "y", "description"})
  protected ConstructorExample(int x, int y, T description) {
    if (description == null) throw new NullPointerException("description");
    this.x = x;
    this.y = y;
    this.description = description;
  }
  
  // @NoArgsConstructor에 의해 만들어지는 메서드
  // 매개변수가 없는 생성자를 생성한다.
  public static class NoArgsExample {
    @NonNull private String field;
    
    public NoArgsExample() {
    }
  }
}

@Data

  • @ToString, @EqualsAndHashCode, @Getter/Setter, @RequiredArgsConstructor를 자동완성 시켜준다.
  • 실무에서는 무겁고 객체의 안정성을 지키기 위해 잘 사용되지 않는다고 한다.

@EqualsAndHashCode

  • 클래스에 대한 equals 함수와 hashCode 함수를 자동으로 생성해준다.
  • 예를 들어, name, gender, phoneNumber가 같은 객체는 같은 객체로 판단하고 싶다면 아래와 같이 작성하면 된다.

With Lombok

1
2
3
4
5
6
7
8
9
@EqualsAndHashCode(of = {"name", "gender", "phoneNumber"})
public class LombokTestForm {
    private Long id;
    private String name;
    private Integer age;
    private String gender;
    private String phoneNumber;
    private String address;
}

Vanilla Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class People {
    private Long id;
    private String name;
    private Integer age;
    private boolean gender;
    private String phoneNumber;
    private String address;

    public People(String name, Integer age, String phoneNumber) {
        this.name = name;
        this.age = age;
        this.phoneNumber = phoneNumber;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof People people)) return false;
        return gender == people.gender && Objects.equals(name, people.name) && Objects.equals(phoneNumber, people.phoneNumber);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, age, gender, phoneNumber, address);
    }
}

🔗 참고자료

📚 더 공부해야 할 것

  • Log 관련 어노테이션이 많은데(Log, Log4j, Log4j2, Slf4j, XSlf4j, JBossLog…) 각각의 차이점이 뭘까? 어떤 어노테이션을 사용하는게 좋을까?
This post is licensed under CC BY 4.0 by the author.