본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성하였습니다.
롬복(Lombok)
롬복(Lombok)이란
Java 라이브러리로 반복되는 getter, setter, toString .. 등의 반복 메서드 작성 코드를 줄여주는 코드 다이어트 라이브러리이다.
Java에서 보통 model 클래스나 Entity 같은 도메인 클래스 등에는 수많은 멤버변수가 있고 이에 대응되는 getter, setter 와 toString() 메서드, 때에따라 멤버변수에 따른 여러개의 생성자를 만들어주게 되는데
거의 대부분 이클립스나 인텔리제이같은 IDE에서 자동생성 기능이 있지만 이 역시도 번거로운 작업이 아닐 수 없다.
뿐만 아니라 코드 자체가 반복 소스코드로 인해 복잡해지게 된다.
Lombok은 여러가지 @어노테이션을 제공하고 이를 기반으로 반복 소스코드를 컴파일 과정에서 생성해주는 방식으로 동작하는 라이브러리이다.
즉, 코딩 과정에서 롭복과 관련된 어노테이션만 보이고 getter,setter 등의 생략되지만 실제로 컴파일된 결과물 .class 파일에는 코드가 생성되어 있다는 말이다.
Lombok 장점
어노테이션 기반의 코드 자동생성을 통한 생산성 향상
반복코드 다이어트를 통해 가독성 및 유지보수성 향상
Getter/Setter 외 빌더 패턴이나 로그생성 등 다양한 방면으로 활용 가능
Lombok 단점 및 주의사항
롬복 라이브러리는 개발자마다 호불호가 나뉠 수 있다. 일부 개발자들은 코드가 직접 눈에 보이는 직관성을 유지하는 것이 좋다고 보는 의견도 있는 만큼 자신의 프로젝트나 성향에 따라 사용하면 좋을 것이다.
또한 API설명과 내부동작을 어느정도 숙지하고 사용해야 한다.
예를 들어 롬복의 @Data 어노테이션이나 @toString 어노케이션으로 자동 생성되는 toString()메서드는 순환 참조 또는 무한재귀호출 문제로 인해 StackOverflowError가 발생할 수도 있다.
물론 이 문제를 인지한 롬복에서 해결할 수 있는 속성을 제공하지만 롬복이 편리하다는 이유만으로 마구 사용한다면 여러가지 예외문제가 발생할 수 있음을 인지해야 한다.
Lombok과 일반 Java코드 차이
public class BoardCategory {
private String category_id;
private String category_name;
private Date regdate;
private int board_cnt;
private int seq;
public String getCategory_id() {
return category_id;
}
public void setCategory_id(String category_id) {
this.category_id = category_id;
}
public String getCategory_name() {
return category_name;
}
public void setCategory_name(String category_name) {
this.category_name = category_name;
}
public Date getRegdate() {
return regdate;
}
public void setRegdate(Date regdate) {
this.regdate = regdate;
}
public int getBoard_cnt() {
return board_cnt;
}
public void setBoard_cnt(int board_cnt) {
this.board_cnt = board_cnt;
}
public int getSeq() {
return seq;
}
public void setSeq(int seq) {
this.seq = seq;
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("BoardCategoryBean{");
sb.append("category_id='").append(category_id).append('\\'');
sb.append(", category='").append(category_name).append('\\'');
sb.append(", regdate=").append(regdate);
sb.append(", board_cnt=").append(board_cnt);
sb.append(", seq=").append(seq);
sb.append('}');
return sb.toString();
}
}
위 코드를 Lombok을 이용하면 다음과 같이 축소할 수 있다.
클래스에 @Getter, @ToString 등의 어노테이션을 붙여주기만 하면 된다.
import lombok.*;
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class BoardCategoryLombok {
private String category_id;
private String category_name;
private Date regdate;
private int board_cnt;
private int seq;
}
또는 이 다섯개의 어노테이션을 @Data 어노케이션 하나만 붙여 사용할 수도 있다.
@Getter, @Setter
필드에 @Getter/@Setter 을 붙이면, 해당 필드에 대한 기본 getter/setter 메소드를 생성해준다.
접근 제한자 AccessLevel
@Getter/@Setter 에서의 접근 제한자 설정을 할 수 있다.
만약 생성되는 getter/setter 에 AccessLevel을 명시해주지 않으면 접근 제한자는 public이 된다.
- PUBLIC
- PROTECTED
- PRIVATE
- PACKAGE
- NONE
- MODULE
@Getter/@Setter 는 필드가 아닌 클래스에도 사용할 수 있는데, 클래스에 사용할 경우 static이 아닌 전체필드에 getter/setter가 적용된다. 만약 특정 필드에서 @Getter/@Setter의 생성을 막고 싶다면 AccessLevel.NONE을 사용하면 해당 필드는 getter/setter 메소드를 생성하지 않는다.
예제 코드
public Class Sample {
@Getter
private String attr1;
@Getter(AccessLevel.PRIVATE)
private String attr2;
}
// 실제 자바 코드
public Class Sample {
private String attr1;
private String attr2;
public String getAttr1() {
return this.attr1;
}
private String getAttr2() {
return this.attr2;
}
}
@Getter
public Class Sample {
private String attr1;
@Getter(AccessLevel.NONE)
private String attr2;
}
// 실제 자바 코드
public Class Sample {
private String attr1;
private String attr2;
public String getAttr1() {
return this.attr1;
}
}
Constructor Annotation
생성자를 자동으로 생성해주는 애노테이션
@NoArgsConstructor
파라미터가 없는 생성자를 생성한다
이를 @NoArgsConstructor(AccessLevel.PROTECTED) 이렇게 사용할 경우 모든 필드에 대한 값이 들어가야함을 보장하고 싶을 때, 기본 생성자 호출을 막음으로써 이후에 발생할 수 있는 문제를 사전 차단할 수 있다.
예제 코드
@NoArgsConstructor
public Class Sample {
private String attr1;
public Sample(String attr1) {
this.attr1 = attr1;
}
}
// 실제 자바 코드
public Class Sample {
private String attr1;
public Sample(String attr1) {
this.attr1 = attr1;
}
public Sample() {
}
}
주의점
- 필드들이 final로 생성되어 있는 경우에는 필드를 초기화할 수 없기 때문에 생성자를 만들 수 없어 에러가 발생된다.이 때, @NoArgsConstructor(force=true) 옵션을 이용해 final 필드를 0, false, null 등으로 강제 초기화시켜 생성자를 만들 수 있다.
- @NonNull 같이 필드에 제약조건이 설정되어 있는 경우, 생성자 내 null-check 로직이 생성되지 않는다.후에 초기화를 진행하기 전까지 null-check 로직이 발생하지 않는 점을 염두해야 한다
@RequiredArgsConstructor
초기화되지 않은 모든 final 필드, @NonNull로 마크되어 있는 필드들에 대한 생성자를 자동으로 생성한다.
예제 코드
@RequiredArgsConstructor
public Class Sample {
private String attr1;
private final String attr2;
@NonNull
private String attr3
}
// 실제 자바 코드
public Class Sample {
private String attr1;
private final String attr2;
@NonNull
private String attr3
public Sample(String attr2, @NonNull String attr3) {
if(attr3 == null) {
throw new NullPointerException("attr3 is marked non-null but is null");
} else {
this.attr2 = attr2;
this.attr3 = attr3;
}
}
}
주의점
- 파라미터의 순서는 클래스에 있는 필드 순서에 맞춰 생성자가 생성한다.
- @NonNull 필드들은 null-check 가 추가적으로 생성되며, @NonNull이 마크되어 있어도 파라미터에서 null 값이 들어온다면 생성자에서 NullPointerException이 발생한다.
@AllArgsConstructor
클래스에 존재하는 모든 필드에 대한 생성자를 자동으로 생성한다.
@NonNull 마크되어 있는 필드는 생성자 내에서 null-check 로직을 자동적으로 생성한다.
예제 코드
@AllArgsConstructor
public Class Sample {
private String attr1;
private String attr2;
@NonNull
private String attr3
}
// 실제 자바 코드
public Class Sample {
private String attr1;
private String attr2;
@NonNull
private String attr3
public Sample(String attr1, String attr2, @NonNull String attr3) {
if(attr3 == null) {
throw new NullPointerException("attr3 is marked non-null but is null");
} else {
this.attr1 = attr1;
this.attr2 = attr2;
this.attr3 = attr3;
}
}
}
staticName option
위의 세 생성자 애노테이션은 static factory를 만들 수 있는 옵션이 있다.
staticName 옵션을 사용해 생성자를 private으로 생성하고, private 생성자를 감싸는 static factory 메소드를 추가할 수 있다.
예제 코드
@RequiredArgsConstructor(staticName = "of")
public Class Sample {
private String attr1;
private final String attr2;
@NonNull
private String attr3
}
// 실제 자바 코드
public Class Sample {
private String attr1;
private final String attr2;
@NonNull
private String attr3
public Sample(String attr2, @NonNull String attr3) {
if(attr3 == null) {
throw new NullPointerException("attr3 is marked non-null but is null");
} else {
this.attr2 = attr2;
this.attr3 = attr3;
}
}
public static Sample of(String attr2, @NonNull String attr3) {
return new Sample(attr2, attr3);
}
}
생성자 애노테이션 사용 시 주의점
- static 필드는 스킵된다.
- 파라미터의 순서는 클래스에 있는 필드 순서에 맞춰 생성하기 때문에 주의해야 한다.만약, 클래스 필드의 순서를 바꾸었을 때 롬복이 자동적으로 생성자 파라미터 순서를 바꾸기 때문에 버그가 발생할 수 있다.
- AccessLevel을 꼭 설정해주어야 한다 (기본 값 = public)아래 애노테이션 모두 접근 제한자를 AccessLevel로 설정할 수 있다.
@EqualsAndHashCode
equals, hashCode를 자동생성해준다.
- equals : 두 객체의 내용이 같은지 동등성을 비교한다
- hashCode : 두 객체가 같은 객체인지 동일성을 비교한다
- callSuper 속성을 통해 메소드 자동 생성 시 부모 클래스의 필드까지 고려할지의 여부를 결정할 수 있다.default = false (자신 클래스 값만 고려한다)
주의점
• 변경 가능한(Mutable) 객체에 아무런 파라미터 없이 그냥 사용할 경우 문제가 발생할 수 있다.
@EqualAndHashCode
@AllArgsConstructor
@Setter
public static class Sample {
private Long sampleId;
private String sampleName;
}
Sample sample = new Sample(lL, "sample_one");
Set<Sample> samples = new HashSet<>();
samples.add(sample); //set에 객체 추가
samples.contains(sample); //true
sample.setSampleName("sample1");
samples.contains(sample); // false
위처럼 동일한 객체 (sample)임에도 set에 저장한 뒤에 해당 객체의 필드 값을 변경하면 hashCode가 변경되어 찾을 수 없게 된다. 그렇기 때문에
- 불변(Immutable) 클래스를 제외하고는 아무 파라미터 없는 @EqualsAndHashCode 사용은 지양하자.
- @EqualsAndHashCode(of={"필드"}) 형태로 동등성 비교에 필요한 필드를 명시하는 형태로 사용하도록 하자.
@Data
다음 애노테이션을 한 번에 설정해준다.
- @Getter / @Setter
- @RequiredArgsConstructor
- @ToString
- @EqualsAndHashCode
@NonNull
필드에 지정해주면 null-check 를 해주며, null 값이 들어왔을 경우 NPE 발생시킨다.
@ToString
toString 메소드를 대체하는 애노테이션으로 callSuper 속성을 통해 상속받은 클래스의 정보까지 출력여부를 결정할 수 있다.
@Value
@Data와 비슷하지만 불변으로 만들어준다고 생각하면 된다.
한 번 생성하면 변경할 수 없는 불변 객체를 만들기 위한 클래스 선언할 때는 @Data 대신 @Value를 사용하면 된다.
private 접근제어자와 final이 붙은 상수가 된다. 기본 생성자를 만들어주나 private이다.
@Wither
원하는 프로퍼티를 다시 할당할 때 해당 객체의 필드를 변경하는 것이 아닌 원하는 값으로 필드를 세팅한 새로운 객체를 반환해준다.
예제 코드
public class witherSample {
private final Long id;
@Wither
private final String name;
public witherSample(Long id, String name) {
this.id = id;
this.name = name;
}
}
witherSample sample = new witherSample(1L, "sample");
wtiehrSample sample2 = sample.withName("sample2");
@Builder
다수의 필드를 가지는 복잡한 클래스의 경우, 생성자 대신 빌더를 사용하는 경우가 많다.
빌더 패턴을 직접 작성해보면 코딩량이 의외로 상당함을 깨닫게 된다.
이 때, @Builder를 사용하여 자동으로 해당 클래스에 빌더를 추가해줄 수 있다.
예제 코드
@Builder
public class User {
private Long id;
private String username;
private String password;
@Singular
private List<Integer> scores;
}
User user = User.builder()
.id(1L)
.username("dale")
.password("1234")
.score(70)
.score(80)
.build();
// User(id=1, username=dale, password=1234, scores=[70, 80])
@Log , @Slf4j, @Log4j2
자동으로 로그 필드를 만들고, 해당 클래스의 이름으로 로커(Logger) 객체를 생성하여 할당해준다.
'패스트캠퍼스 강의' 카테고리의 다른 글
[20일차] 50일 포트폴리오 챌린지 (1) | 2023.08.27 |
---|---|
[19일차] 50일 포트폴리오 챌린지 (0) | 2023.08.26 |
[17일차] 50일 포트폴리오 챌린지 (0) | 2023.08.24 |
[16일차] 50일 포트폴리오 챌린지 (0) | 2023.08.23 |
[15일차] 50일 포트폴리오 챌린지 (0) | 2023.08.22 |