개발 고민

[Java, Spring] Lombok @Data 쓰지 말아야 하는 이유

woopii 2025. 11. 1. 09:48

 

코드 리뷰 중 @Data 쓰지 말라는 피드백이 보여서, 생각난 김에 정리한다.

@Data가 하는 일

@Data
public class User {
    private Long id;
    private String name;
}

이 한 줄이 @Getter, @Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstructor를 전부 생성한다.

문제는 너무 많은 걸 자동으로 한다는 거다.


실제로 겪은 문제

1. 불변 객체를 못 만듦

@Data
public class User {
    private Long id;
    private String email;
}

// setter가 자동 생성되어서 아래처럼 변경 가능
user.setId(999L);  // 엔티티 ID 변경

 

 

해결

@Getter
@Builder
public class User {
    private final Long id;
    private final String email;
}

 


2. 민감 정보 로그 노출

@Data
public class Member {
    private String username;
    private String password;
}

// 로그 찍으면...
System.out.println(member);
// Member(username=kim, password=1234)  <- 비밀번호 노출

해결

@Getter
@ToString(exclude = "password")
public class Member {
    private String username;
    private String password;
}

3. JPA 양방향 연관관계에서 무한루프

@Entity
@Data
public class Order {
    @OneToMany(mappedBy = "order")
    private List<OrderItem> items;
}

@Entity
@Data
public class OrderItem {
    @ManyToOne
    private Order order;
}

// StackOverflowError 발생
System.out.println(order);

toString()이 서로를 계속 호출함.

 

해결

@Entity
@Getter
@ToString(exclude = "items")
@EqualsAndHashCode(of = "id")
public class Order {
    @Id
    private Long id;
    
    @OneToMany(mappedBy = "order")
    private List<OrderItem> items;
}

4. 비즈니스 로직을 우회하여 직접 데이터 세팅 가능  

@Data
public class BankAccount {
    private BigDecimal balance;
}

// setter로 검증 없이 변경 가능
account.setBalance(new BigDecimal("-1000"));  // 음수 잔액?

 

해결

@Getter
@Builder
public class BankAccount {
    private BigDecimal balance;
    
    public void deposit(BigDecimal amount) {
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("양수만 가능");
        }
        this.balance = this.balance.add(amount);
    }
}

대신 이렇게 쓰자(예시)

DTO/VO

@Getter
@Builder
@AllArgsConstructor
public class UserDto {
    private final Long id;
    private final String name;
}

JPA Entity

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString(exclude = "연관관계필드")
@EqualsAndHashCode(of = "id")
public class Member {
    @Id
    private Long id;
    private String name;
}

간단한 불변 객체

@Value  // final + getter만 생성
public class Point {
    int x;
    int y;
}

정리

  • @Data는 너무 많은 걸 자동으로 함
  • 특히 JPA 엔티티에선 위험함
  • 필요한 것만 명시적으로 선택하자
  • Setter는 정말 필요할 때만