Daily Notebook

[Java] Lombok 소화하기

by 배부른코딩로그
💡 귀찮은 반복 작업을 완벽하게 해소해주는 롬복(Lombok). 한 번 제대로 정리해보자!

목표

  • Lombok 라이브러리가 무엇인지 왜 사용하는지 설명할 수 있다.
  • Lombok 관련 애노테이션 종류와 각각의 쓰임을 설명할 수 있다.
  • Lombok 관련 예제를 정리하는 공간으로 활용할 수 있다.

 

롬복은 자바 개발에 널리 사용되는 매우 편리한 라이브러리이다

생성자나 Getter, Setter 등 보일러 플레이트성 코드를 매번 수동으로 작성할 필요가 없기 때문일 것이다.

작성을 안했는데 언제 생성이 되는 것일까? 이를 컴파일 타임(Compile-time)에서 자동으로 생성해준다.

게다가, IDE 내에서도 모든 메서드에 적상적으로 접근할 수 있다.

 

롬복에서 일반적으로 사용하는 애노테이션은 다음과 같다.

  • @Data
  • @Builder and @SuperBuilder
  • @NonNull
  • @Cleanup
  • @RequiredArgsConstructor(onConstructor = @__(@Inject))
  • var, val

 

Lombok

@Data

JPA에서 DTO를 만들 때, 주로 사용하는 애노테이션이다.

이에 대한 예제는 다음과 같다.

@Data
public final class Person {
private String firstName;
private String LastName;
private int age;
private Company company;
}

@Data 애노테이션에는 아래와 같은 애노테이션 집합이 하나에 묶여 있음을 알아야 한다.

  • @Getter
  • @Setter
  • @ToString
  • @EqualsAndHashCode
  • @RequiredArgsConstructor

Getter/Setter와 toString(), hashCode(), equals() 등 일반적인 메서드를 기본적으로 생성한다.

따라서, 사용자 지정으로 toString() 또는 equals()에 구현할 수 있는 유연성을 갖추고 싶다면

클래스 정의에 직접 추가하면 Lombok 기본 보일러 플레이트 내용을 덮어쓸 수 있다.

 

@Builder and @SuperBuilder

@Builder

Builder 패턴의 보일러 플레이트이며, 클래스에 필드가 여러 개 있어 초기화가 복잡할 때 유용하다.
사용 예제는 다음과 같다.

@Builder
public final class Person {
private String firstName;
private String LastName;
private int age;
private Company company;
}

Builder 패턴을 사용한 객체 생성 예제는 다음과 같다.

final Person codify = Person.builder
.firstName('dify')
.lastName('Co')
.age(30)
.build();

Builder 패턴의 장법은 각 파라미터에 따른 생성자 코드를 작성하지 않아도 됨에 있다.

객체 생성 초기화 시, lastName이 필요가 없다면 없는대로 생성이 가능하다.

 

그런데, @Builder는 상속에서 어떻게 작동될까?

@SuperBuilder를 사용하여 상속에 따른 Builder 패턴을 적용할 수 있다.

 

@SuperBuilder

@SuperBuilder
public final class Person {
private String firstName;
private String LastName;
private int age;
private Company company;
}
@SuperBuilder
public class Staff {
private String position;
private int yearOfExperience;
private List<String> abilities;
}
final Person codify = Staff.builder()
.firstName('dify')
.lastName('Co')
.age(30)
.position("developer")
.yearOfExperience(3)
.abilities(Collections.singletonList("Spring", "React"))
.build();

위와 같이 @Data와 @Builder의 함께 사용하면, 컴파일 중 오류가 발생한다.

그 이유는 @Data와 @Builder 만 사용했기 때문에 기본 생성자가 누락되었기 때문이다.

이 경우, Java의 직렬화/역직렬화 라이브러리가 실행됨에 기본 생성자가 누락되어 역직렬화 단계에서 오류가 난다.

 

이를 위해 @NoArgsConstructor 혹은 @AllArgsConstructor를 추가한다.

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Company {
private String name;
public String changeA2B() {
Company company = Company.builder().name("CODIFY").builder();
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString();
Company company2 = mapper.readValue(json, Company.class);
System.out.println(company2); // Company [name=CODIFY]
}
}

@NoArgsConstructor 및 @AllArgsConstructor 애노테이션 이름에서 의미가 드러난다 : )

 

// @NoArgsConstructor
public Company() {}
// @AllArgsConstructor, 필드가 n개면, n개의 args
public Company(String name) {
this.name = name;
}

 

@NonNull

롬복은 @NonNull 애노테이션을 통해 null-check도 손쉽게 도와준다.

if (param == null) throw new NullPointerException("Cannot invoke ~ because 'param' is null");
컴파일시, 메서드 스코프 맨 위에 삽입된다.
public void printPerson(@NonNull Person person) {
System.out.println(person.toString()); // Here, no need to worry about NPE!
}
public String upperCase(@NonNull String str) {
return str.toUpperCase(); // Here, no need to worry about NPE!
}

단, @NonNull 애노테이션은 롬복이 생성한 생성자나 메서드에서만 작동한다는 점이다

다음과 같은 경우 @NonNull이 무시된다.

@Getter
@Setter
public class LombokTest {
@NonNull
private Integer id;
@NonNull
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

@Getter와 @Setter를 사용하지 않기 위해 Override 처리한 getName과 setName의 경우가 이에 해당된다.

public class LombokTest {
@NonNull
private Integer id;
@NonNull
private String name;
@NonNull
public Integer getId() {
return this.id;
}
public void setId(@NonNull Integer id) {
if (id == null) {
throw new NullPointerException("id is null");
}
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

컴파일 시 위와 같이 작성됨을 주의해야 한다.

 

@ Cleanup

@Cleanup 애노테이션은 스코프 내에서 리소스를 참조 완료 후 리소스를 릴리즈함을 간편하게 할 수 있다.

File I/O를 통해 간단한 예제를 가져와봤다.

public byte[] read(String infile, String outfile) {
@Cleanup InputStream in = new FileInputStream(infile);
@Cleanup OutputStream out = new FileOutputStream(outfile);
byte[] b = new byte[10000];
while (true) {
int r = in.read(b);
if (r == -1) break;
out.write(b, 0, r);
}
return b;
}

롬복은 생성된 InputStream 및 OutputStream은 참조 후 자동으로 닫아준다.

// if else check, then
in.close();
// if else check, then
out.close();

try~catch~finally를 통해 닫을 때 보일러 플레이트성 코드를 매번 기입했는데,

이를 애노테이션 한 방에 처리를 할 수 있다는 점에서 아주 유용하다.

 

@RequiredArgsConstructor

여기서는 Singleton Pattern과의 "통합"을 위한 다른 용도를 추가적으로 정리한다.

  • @RequiredArgsConstructor(onConstructor = @__(@Inject)): 해당 생성자에 @Inject 을 생성.
  • @RequiredArgsConstructor(onConstructor = @_(@Autowired)): 해당 생성자에 @Autowired 를 생성.

 

 

출처

 

단어

  • *보일러플레이트 코드(Boilerplate code)
    컴퓨터 프로그래밍에서 최소한의 변경으로 여러 곳에서 재사용되며, 반복적으로 비슷하게 작성되는 코드를 의미한다.

 

 

Last Updated. 2022. 06. 07.

 

블로그의 프로필 사진

블로그의 정보

배부른코딩로그

배부른코딩로그

활동하기