[Library] Lombok

Lombok이란?

Lombok은 Java 기반 애플리케이션에서 VO, DTO, Entity 등을 보다 쉽게 작성하기 위해 사용되는 라이브러리이다.
어노테이션을 이용하여 반복되는 로직을 만들 수 있기 때문에 가독성 및 퍼포먼스가 좋다.
하지만 특정 Annotation의 무분별한 사용은 오히려 문제가 될수 있어 사용시 주의가 필요 하다.

@Getter / Setter

  • 클래스와 필드에서 모두 사용 가능하며, 반복되는 Getter / Setter를 생성해 준다.
  • 생성된 메소드들은 기본적으로 public 이다.

속성

  • AccessLevel : 접근제한자를 설정해 줄 수 있다.
    • 종류 : PUBLIC, PROTECTED, PACKAGE, PRIVATE
    • AccessLevel.NONE을 붙이면 해당 필드는 lombok이 메소드를 생성하지 않는다.

주의

Setter 같은 경우 해당 클래스가 무결성(변경하면 안되는)을 보장해야 하는 경우 사용을 지양.

@Getter		// 모든 변수에 대해서 Getter를 생성
public class User {
    private String id;
    @Setter(AccessLevel.PRIVATE)	// password의 Setter가 private으로 생성
    private String password;
}

NonNull

  • 변수나 파라미터에 선언하게 되면, 해당 값에 null이 올 수 없다.
  • Lombok에서 null-check 로직을 자동으로 생성.
  • Setter에 null값이 들어오면 NullPointerException예외를 일으킨다.
  • 변수에 @NonNull이 달려있으면 해당 변수에 값을 설정하는 메소드들에도 null-check 코드를 생성.
  • null값이 들어왔을 때 exception이 기본은 NullPointerException이 발생하지만 lombok.nonNull.exceptionType 설정값을 IllegalArgumentException으로 변경할 수 있다.

주의

  • Lombok이 생성한 메소드나 생성자에만 효과가 있다.
  • 불필요하게 branch coverage를 증가시킨다.(남발하지 말 것)
public class User{
    @NonNull	// id 값에 null이 올 수 없습니다.
    private String id;
    private String password;
}

NoArgsConstructor

parameter가 없는 디폴트 생성자를 생성해준다.

속성

access : AccessLevel을 이용하여 접근 제한자를 설정해줄 수 있다.

주의

  • 기본 생성자를 public(default)로 열어두면 안전성이 심각하게 저하된다.
  • static 변수들은 스킵
  • 필드들이 final로 생성되어 있는 경우에는 필드를 초기화 할 수 없기 때문에 생성자를 만들 수 없고 에러 발생.
    • @NoArgsConstructor(force = true) 옵션을 이용해서 final 필드를 0, false, null 등으로 초기화를 강제로 시켜서 생성자를 만들 수 있다. (@NonNull같은 제약조건은 무시된다)
  • @NonNull 같이 필드에 제약조건이 설정되어 있는 경우, 생성자 내 null-check 로직이 생성되지 않는다.
    • 후에 초기화를 진행하기 전까지 null-check 로직이 발생하지 않는 점을 염두하고 코드를 개발.

권장하는 방법

@NoArgsConstructor(access = AccessLevel.PROTECTED)를 사용하여 객체 생성시 안전성을 보장해주는것을 권장

@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User{
    @NonNull
    private String id;
    private String password;
}

AllArgsConstructor와 @RequiredArgsConstructor (사용 지양)

  • @AllArgsConstructor : 모든 변수를 parameter로 받는 생성자를 생성해준다.
  • @RequiredArgsConstructor : final 혹은 @NonNull인 변수만 parameter로 받는 생성자를 생성해준다.

속성

  • access : AccessLevel을 이용하여 접근 제한자를 설정해줄 수 있다.
  • staticName : 생성자 관련 어노테이션은 모두 사용할 수 있다. 생성자를 private으로 생성하고, private 생성자를 감싸고 있는 static factory 메소드를 추가할 수 있다.

주의

@AllArgsConstructor
public class User{
    private String id;
    private String pwd;
}

User user = new User("userId","userPwd");
  • 위와 같이 초기설정을 했는데, 개발자가 변수의 위치가 마음에 안들어서 바꾸게 된다면, 생성자의 위치도 바뀌지만 입력에는 둘다 String이라 오류가 발생하지 않는다. 이를 개발자가 인지하지 못하게 되는 경우가 생길 수 있다.
  • 그렇기 때문에 @AllArgsConstructor, @RequiredArgsConstructor 두 어노테이션들은 사용하지 않는것이 좋다. 다만, @RequiredArgsConstructor는 멤버변수가 final이고, 변수의 수가 많을 때 일일이 생성자를 만드는 방법보다 훨씬 유용하기 때문에 따로 생성자를 이용해서 생성하지 않고 DI를 이용해 생성할 때 사용하면 좋다.

권장하는 방법

public static class User{
    private String pwd;
    private String id;

    @Builder
    public User(String pwd, String id){
        this.pwd = pwd;
        this.id = id;
    }
}

// service class
User user = User.builder().pwd("userPwd").id("userId").build();

@Builder어노테이션은 순서가 아닌 이름으로 입력받기 때문에 개발자가 실수하는 것을 최대한 방지할 수 있다.

Builder

  • 빌더 패턴을 사용할 수 있도록 코드를 생성

주의

  • @Builder도 private으로 만들긴 하지만 @AllArgsConstructor를 내포
    • 해당 클래스의 다른 메소드에서 이렇게 자동으로 생성된 생성자를 사용하거나 할 때 문제 소지가 있다.

권장하는 방법

  • @Builder를 Class보다는 직접 만든 생성자 혹은 static 객체 생성 메소드에 붙이는 것을 권장한다.
  • private 생성자를 구현하여 @Builder 를 지정한다.
    • @Builder 를 Class에 적용시키면 생성자의 접근 레벨이 default이기 때문에, 동일 패키지 내에서 해당 생성자를 호출할 수 있는 문제가 있다.
public class User {
    private String pwd;
    private String id;
    private String name;

    @Builder
    private UserProfile(String pwd, String id, String name) {
        this.pwd = pwd;
        this.id = id;
        this.name = name;
    }
}

Slf4j

  • log를 생성해준다.
    // as-is
    @Controller
    public class HomeController {
        private static final Logger LOG = LoggerFactory.getLogger(클래스명.class);
        ...
    }

    // to-be
    @Slf4j
    @Controller
    public class HomeController {
        ...
    }

RequiredArgsConstructor

  • 초기화 되지않은 final 필드나, @NonNull 이 붙은 필드에 대해 생성자를 생성해 준다.
  • @Autowired를 사용하지 않고 의존성 주입
  • 스프링 개발팀에서 생성자 주입을 사용할 것을 권장하는 이유는 한번 의존성 주입을 받은 객체는 프로그램이 끝날 때까지 변하지 않는 특징을 가지므로 불변성을 표시해주는 것이 좋기 때문이다. -> 객체의 불변성(Immutability) 보장

특징

  • 코드 변이에 대한 안전성
    • @RequiredArgsConstructor는 생성자 중에서 final 키워드가 붙은 주입에만 생성자를 만들어준다. final이 붙어있기 때문에 인스턴스가 생성될 때 1번만 참조되므로 코드 변이의 걱정은 사라진다.
  • 순환 참조에 대한 안전성(A 클래스가 B 클래스의 Bean 을 주입받고, B 클래스가 A 클래스의 Bean 을 주입받는 상황)
    • 생성자 어노테이션을 사용할 경우에 순환 참조가 일어날 시  Exception이 발생하여 컴파일 중에 에러가 발생한다. 이를 통해 Test 단계에서 순환 참조를 파악하여 수정할 수 있다.
// as-is
@RestController
@RequestMapping("/example")
public class RequiredArgsConstructorControllerExample {

    private final FirstService firstService;
    private final SecondService secondService;
    private final ThirdService thirdService;

    public RequiredArgsConstructorControllerExample(FirstService firstService, SecondService secondService, ThirdService thirdService){
        this.firstService = firstService;
        this.secondService = secondService;
        this.thirdService = thirdService;
    }

    ...
}

// to-be
@RestController
@RequiredArgsConstructor
@RequestMapping("/example")
public class RequiredArgsConstructorControllerExample {

    private final FirstService firstService;
    private final SecondService secondService;
    private final ThirdService thirdService;

    ...
}

ToString

  • ToString() 메서드를 생성해준다.
  • 기본적으로 static을 제외한 전체 변수에 대한 ToString()을 생성.

속성

  • exclude : ToString()을 만들 때 제외할 변수를 설정.
  • of : ToString()을 만들 때 포함할 변수를 설정.
  • callSuper : true로 설정할 경우, 상속받은 클래스의 정보까지 출력. (Default = false)

주의할 점

  • JPA 사용 시, 객체가 양방향 영관 관계 일 경우 @ToString을 호출하게 되면 무한 순환 참조 발생할 수 있다.

권장하는 방법

  • @ToString(exclude={“필드명”})을 권장.
  • of보다 비용이 적음 (새로 객체를 추가해 줄 때마다 수정 비용이 적다.)
@ToString(exclude = "password")		// 토큰값, 비밀번호 등과 같은 민감한 정보를 exclude를 통하여 제외할 수 있다.
public class User {
    private String id;
    private String password;
    private String Name;
}

Data (사용 지양)

@Getter, @Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstructor 등을 자동으로 생성.

주의

무분별하게 @Getter,@Setter를 사용하게 되고 @RequiredArgsConstructor등을 포함하기 때문에 사용하지 않는게 좋다.

권장하는 방법

@Data를 사용하는 것 보다 @Getter,@Setter 등 필요한 어노테이션을 각각 선언하는것을 권장.

@Value (사용 지양)

  • Immutable(불변성) 객체를 선언합니다.
  • 해당 어노테이션을 사용할 경우 setter 메소드는 사용이 불가능하다.

주의

@Value역시 @AllArgsConstructor등을 포함하기 때문에 사용하지 않는게 좋다.

권장하는 방법

Immutable(불변성) 객체를 만들어야 할 때는 직접 만들어 주고 @Getter등을 붙여주는 것이 좋다.

lombok.config 설정

  • Lombok에서도 일부 기능에 대해 사용금지가 필요하다고 느꼈는지, Configuration System lombok.config를 통해 다양한 설정이 가능하게 해두었다.

lombok.config파일을 작성한뒤 Proejct root path에 위치시킨다.

lombok.Setter.flagUsage = error
lombok.AllArgsConstructor.flagUsage = error
lombok.ToString.flagUsage = warning
lombok.data.flagUsage= error

© 2023 Lee. All rights reserved.