[Lombok] 설정 및 기본 사용법
- -
project lombok
Java 애플리케이션을 만들다 보면 계층 간 데이터 교환에 사용되는 DTO(Data Transfer Object)를 만들게 된다.
DTO를 만들 때는 일반적으로 다음의 규칙을 따른다.
- 멤버 변수(field)는 private 접근 제한자를 사용한다.
- 해당 멤버 변수에 접근하기 위해 public setter/getter를 포함한다.
- 기본 생성자를 정의한다.
- 선택 사항으로, 여러 파라미터 생성자를 오버로딩 하거나, toString(), equals(), hashCode() 등을 재정의 할 수 있다.
이렇듯 DTO를 만드는 작업은 대체로 간단해서 대부분 IDE들이 제공하는 기능을 통해 코드를 쉽게 생성할 수 있다. 하지만 프로젝트 진행 과정에서 리펙토링이 필요하거나 속성이 변경될 경우, DTO 코드의 수정이 필요하게 되고 이는 예상외로 많은 작업을 요구할 수 있다.
이런 번거로움을 줄이기 위해 lombok 라이브러리는 간단한 어노테이션을 통해 DTO 코드를 컴파일 타임에 필요한 코드를 생성해 줌으로 써 개발자의 수고를 덜어줄 수 있다.
지정학적으로 롬복은 인도네시아의 섬인 자바 옆에 위치하고 있어서 아주 밀접하게 관계가 있다. ㅎㅎ
다운로드 및 설치
intellij나 vscode에서는 별도로 설치할 필요가 없지만 Eclipse 계열에서는 설치해주어야 한다.
lombok은 https://projectlombok.org에서 다운로드 받을 수 있다.
다운로드한 경로로 이동해서 java 명령으로 실행시킨다.
가끔 STS를 실행 중인 상태에서 실행 시 오류가 나기도 한다. STS를 종료시킨 후 실행하자.
cd "다운로드 경로"
java -jar lombok.jar
그럼 아래와 같은 GUI가 동작하는데 먼저 (1)을 이용해서 STS의 위치를 지정하고 (2)에 툴의 경로가 표시되게 한다. 마지막으로 (3) install / update를 이용해 설치를 마친다.
설치와 별도로 프로젝트에는 lombok 라이브러리가 build path에 추가되어야 한다.
lombok의 애너테이션
lombok은 간단히 애너테이션 선언만으로 DTO의 필요한 코드를 생성할 수 있다.
annotation | 설명 |
@Getter/@Setter | 멤버 변수들에 대해 getter / setter 메서드를 생성한다. |
@ToString | toString() 메서드를 재정의 한다. |
@EqualsAndHashCode | equals()와 hashCode()를 재정의 한다. |
@NonNull | 객체형에 대해 Null 값이 될수 없음을 표시한다. Lombok에 의해 생성되는 setter 또는 생성자에는 null check 로직이 삽입되며 spring 등 다른 코드에는 가독성을 높이는 효과만 있다. |
@AllArgsConstructor | 모든 멤버 변수를 파라미터로 하는 생성자를 생성한다. |
@NoArgsConstructor | 기본 생성자를 명시적으로 생성한다. |
@RequiredArgsConstructor | blank final 과 @NonNull로 지정된 멤버 변수들을 이용해서 생성자를 만든다. |
@Data | @ToString, @EqualsAndHashCode, @Getter, @Setter 그리고 다른 생성자가 없을 경우는 @RequiredArgsConstructor까지 모두 적용한다. |
@Builder | builder 패턴을 이용해서 객체를 생성할 수 있도록 한다. |
@Slf4j | Slf4j의 Logger 타입의 변수 log를 생성한다. |
동작 확인
실제 간단한 DTO를 만들어보고 생성되는 코드를 살펴보자.
@Getter
@RequiredArgsConstructor
public class Main {
private final String name;
private int age;
public static void main(String[] args) {
Main m = new Main("hong");
System.out.println(m.getName());
}
}
클래스에는 단지 두 개의 멤버 변수만 선언되어있지만 main 메서드를 살펴보면 @RequiredArgsConstructor에 의해 생성된 파라미터 생성자를 사용하기도 하고 @Getter에 의해 생성된 getName 메서드를 호출할 수도 있게 되었다. @Data는 두 개의 annotation을 모두 포함하므로 하나로 대체할 수도 있다.
@Data가 편할것 같지만 실제로 사용하다 보면 여러가지 복잡한 이유로 사용하지 않는 것이 정신건강에 유리할 경우가 많다.
아무튼 이제 번거로운 코드를 작성하느라 고생하지 말자.
Lombok 사용 시 주의 사항
@Builder
@Builder는 @AllArgsConstructor와 사용하면 매번 생성자를 만들 필요 없이 다양한 조합으로 객체를 생성할 수 있는 아주 막강한 녀석이다. 하지만 @Builder를 이용한 객체 생성 방식을 잘 알고 사용해야 한다.
만약 BuilderExample을 아래와 같이 생성하면 어떻게 될까?
@Builder
@ToString
public class BuilderExample {
private String name = "hong";
public static void main(String[] args) {
BuilderExample be = BuilderExample.builder().build();
System.out.println(be);
}
}
name에 default로 hong을 할당해 두었으므로 name에 'hong' 이 출력될 것 같지만 출력 결과는 BuilderExample(name=null)이다. 우리의 이름은 어디로 사라진 것일까?
다음은 lombok 공홈에 있는 예의 일부이다. created와 name 모두 기본 값이 있는 상태에서 created는 @Builder.Default가 설정되어있다.
@Builder
public class BuilderExample {
@Builder.Default private long created = System.currentTimeMillis();
private String name = "hong";
}
// 실제 생성되는 자바 코드 일부
public class BuilderExample {
private long created; // 할당했던 값들은 없어짐
private String name;
BuilderExample(long created,String name) {
this.created = created;
this.name = name;
}
private static long $default$created() { // @Default에 값을 공급하는 메서드
return System.currentTimeMillis();
}
public static BuilderExampleBuilder builder() {
return new BuilderExampleBuilder();
}
public static class BuilderExampleBuilder {
private long created;
private boolean created$set;
private String name;
public BuilderExampleBuilder name(String name) { // 전달받은 값 할당
this.name = name;
return this;
}
public BuilderExampleBuilder created(long created) {
this.created = created; // 전달받은 값 할당
this.created$set = true; // 새로운 값 할당 여부
return this;
}
public BuilderExample build() {
// 최종적으로 객체 생성
return new BuilderExample(created$set ? created : BuilderExample.$default$created(), name);
}
}
}
즉 @Builder는 기본적으로 클래스에 선언된 모든 변수들의 할당값을 사용하지 않고 전달받은 값만 사용한다. 만약 클래스의 field에 초기에 할당해놓은 값을 사용하기 위해서는 @Builder.Default를 추가해주어야 한다.
@ToString 순환 참조
@ToString은 객체의 모든 field들을 이용해서 toString()을 구성하는데 field가 객체인 경우 그 객체의 toString()이 계속 호출될 것이다. 만약 두 개의 객체가 서로를 field로 가지고 있으면 어떻게 될까?
@ToString @Setter
static class First{
private Second s;
}
@ToString @Setter
static class Second{
private First f;
}
public static void main(String[] args) {
First f = new First();
Second s = new Second();
f.setS(s);
s.setF(f);
System.out.println(f);
}
둘 사이에는 순환 참조가 발생해서 StackOverflowError가 발생한다. 이런 경우는 @ToString을 그냥 사용할 수 없고 toString()을 직접 재정의하거나 field들에 대해 @ToString.Exclude나 @ToString.Include를 이용해서 출력 대상을 제어할 수 있다. 기본적으로는 include이므로 제외할 대상에 대해 exclude 시켜주자.
'tools & libs > ETC' 카테고리의 다른 글
[synology] NAS에 Web Application 배포하기 - 2. 배포 (0) | 2020.09.20 |
---|---|
[synology] NAS에 Web Application 배포하기 - 1. 톰켓 설치 (0) | 2020.09.12 |
OBS (0) | 2020.04.30 |
[office 365] 타이틀 바에서 사용자 명 변경 (0) | 2020.04.26 |
[Windows]노트북이 절전모드로 가지 않을 때 (0) | 2019.12.25 |
소중한 공감 감사합니다