실무에서 Lombok 똑똑하게 사용하기

Jiwon Jung·2023년 4월 26일
0
post-thumbnail

Lombok(롬복)이란 애노테이션 기반으로 코드를 컴파일 과정에서 자동완성 해주는 Java Library 입니다.

실무에서 웹 애플리케이션을 개발해보고 공부해보면서 느낀 저의 lombok 사용법에 대해 소개해드리는 것으로, 각자의 개발 환경에 맞게 사용하는 것을 권합니다.

우선 자주 사용되는 롬복 애노테이션은 다음과 같습니다.

@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
@RequiredArgsConstructor
@Builder
@Data
@EqualsAndHashCode
@NonNull

롬복 사용시 코드의 양을 많이 줄일 수 있어 생산성 및 가독성, 유지보수성 향상에 도움이 됩니다. 하지만 매우 편리한 만큼 내부동작을 어느정도 숙지하고 사용해야지 안그러면 여러 가지 예외문제가 발생할 수 있습니다.

몇가지 제가 생각하는 올바른 lombok 사용법을 설명드리겠지만,
결론적으로 말씀드리면, 상황에 맞춰 필요한 lombok 애노테이션만 추가하여 사용하는 것을 권장합니다.

@Data 사용을 지양 하자

@Data를 사용하면 @Getter, @Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstructor을 한번에 자동완성 시킵니다.

@Entity
@Data
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "name", nullable = false)
    private String name;
    
    @Column(name = "email", nullable = false)
    private String email;

    @CreationTimestamp
    @Column(name = "create_at", nullable = false, updatable = false)
    private LocalDateTime createDate;

    @UpdateTimestamp
    @Column(name = "update_at", nullable = false)
    private LocalDateTime updateDate;
}

이유 1. setter 남용

@Data를 사용시 setter를 지원하는데, setter가 모든 필드에 적용이 되기 때문에 변경이 되어서는 안되는 필드도 언제든지 변경될 수 있어 객체의 안정성을 보장받기 힘듭니다.
만약, 회원가입 후 이메일 변경이 되어서는 안된다고 하면, setter도 email 필드에는 적공하지 않도록 하여 잘못된 값 변경을 막을 수 있어야합니다.
따라서 필요한 필드에만 setter를 쓰는 것을 권장합니다.

이유 2.ToString으로 인한 양방향 연관관계시 순환 참조 문제

@Entity
@Data
@Table(name = "member")
public class Member {
    ...

    @OneToMany
    @JoinColumn(name = "ticket_id")
    private List<Ticket> tickets = new ArrayList<>();
}

@Entity
@Data
@Table(name = "ticket")
public class Ticket {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    private Member member;

    public Ticket(Member member) {
        this.member = member;
    }

}

위와 같이 양방향 매핑관계일 때, ToString()을 호출하게 되면 무한 순환 참조 문제가 발생합니다.
이에 대한 간단한 해결방법은 @Data 대신 @ToString() 사용을 별도로 하여, ticket은 제외하는 것입니다.

@ToString(exclude = "tickets")
public class Member {...}

@NoArgsConstructor 사용 시 접근 권한은 protected로 주자

JPA에서는 프록시 생성을 위해 기본 생성자를 반드시 하나 생성해야 합니다.
기본 생성자를 아무 이유 없이 열어두는 것은 객체 생성의 안정성을 떨어트립니다.
따라서 외부에서 생성을 열어둘 필요가 없습니다. protected로 아래처럼 접근을 제한 하는 것이 좋습니다.

@NoArgsConstructoer(access = AccessLevel.PROTECTED)
public class Member {...}

그리고 기본 생성자 접근을 PROTECTED로 하게 되면 외부에서는 해당 객체의 기본 생성을 할 수 없으므로, 아래처럼 직접 변경해야할 필드가 담긴 생성자를 통해서만 객체 생성이 가능하게 됩니다.

@Builder
public Product(String email, String name) {
    this.email = email;
    this.name = name;
}

@Builder는 생성자나 static 객체 생성 메소드에서 사용하자

@Builder는 기본적으로 @AllArgsConstructor를 내포합니다.
만약 @Builder를 클래스에 사용하게 되면 해당 클래스가 갖는 모든 멤버필드에 대해 매개변수를 허용하게 됩니다. 이게 왜 문제가 되냐면 예를 들면 createDate, updateDate는 각각 @CreationTimestamp, @UpdateTimestamp 애노테이션이 각자 붙어 있습니다. 자동으로 날짜를 생성해주는 일을 담당하므로, 개발자가 직접 객체 생성시 받으면 안되는 멤버필드들입니다. 하지만 @Builder로 인해 이 두 컬럼 모두 값을 잘못 넘겨 받을 수 있어 문제의 소지가 있습니다.

따라서 가급적 클래스 보다는 받아야하는 생성자를 필요조건에 따라 지정하고 그 위에 @Buidler를 붙이는게 바람직합니다.

public class Member {

    @Builder
    public Member(String email, String name) {
        this.email = email;
        this.name = name;
    }
}

결론

개인 공부용이 아닌 실무에서 프로젝트할 때는 @Data 하나로 많은 기능을 자동완성 하려기보다는 저는 가급적 @Getter, @Setter, @ToString, @Builder 등 직접 필요한 롬복 애노테이션을 하나씩 붙여가며 사용하는 것이 좋다고 생각합니다.

정답은 없겠지만, 가장 중요한 것은 롬복 라이브리의 편리함에 가려진 내부적인 부분을 이해하지 못한채 막 쓰게되면 예상치 못한 에러들을 만날 수 있기 때문에 사용 시 문서도 찾아보며 똑똑하게 롬복을 쓰실 수 있으면 좋겠습니다.

profile
게을러지고 싶어 부지런한 개발자

0개의 댓글