새로운 프로젝트에서 오랜만에 Lombok을 사용하게 됐다. 분명히 @Setter를 추가했는데, 이상하게도 Repository에서 setter가 없다는 오류가 발생했다. 코드를 아무리 살펴보고, 구글링까지 해봤지만 도무지 이유를 찾을 수 없었다.

그러다 "Getter/Setter를 지양하는 방향으로 코드를 작성해야 한다"는 글을 발견했다.  오... 그러면 값 변경은 어떻게 하는건데 라면서 찾아본  Setter의 문제점과 그 대안을 작성 해보려고 한다. 

 

 

[Setter의 문제점]


Lombok의 @Setter는 코드량을 줄이고 가독성을 높이는 유용한 기능이지만, 무분별한 사용은 객체의 안정성과 유지보수성을 저하 시킬 수 있다. 그래서 최근에는 불변객체 패턴을 선호하는 경향이 있다고 한다. 

 

 

1. 객체의 불변성 보장 불가 

setter가 있으면 객체의 필드 값을 언제든지 변경할 수 있기 때문에 데이터의 일관성이 깨질 위험이 있다. 

Member member = new Member();
member.setUserId("user_0115");
member.setUserName("hennie");

// 다른 로직에서 예상치 못하게 값이 변경될 수도 있음
member.setUserId("hackedUser");

 

이렇게 객체의 필드 값이 아무제약 없이 변경 될 수가 있는데 이런 문제는 멀티스레드 환경이나 데이터 변경이 많은 서비스에서 문제를 불러 일으킬 수 있다. 

 

2. 유지보수성 하락 

Setter를 사용하면 필드가 많아 질 수록 객체 생성 및 값 설정 코드가 복잡해진다. 그리고 가독성도 완전 떨어진다.

Member member = new Member();
member.setUserId("user_0115");
member.setUserName("hennie);
member.setEmail("hennie0115@email.com");
member.setPhoneNumber("010-1234-5678");

 

여기서 어떤 값이 필수고 어떤 값이 선택 사항인지 한눈에 보기 가 어렵다. 또한, 필드 추가시 기존 setter 호출 코드도 작성해줘야 한다. 

 

3. 필수 값 검증이 어려움

Setter 방식에는 필드 값을 하나씩 설정하기 떄문에, 필수 값이 누락될 가능성이 있다. 

Member member = new Member();
member.setUserId("user_0115");
// userName을 설정하는 걸 깜빡함...

 

userName 을 설정하지 않고 객체를 사용하면 예상치 못한 NullPointException이 발생할 수 있다. 객체를 완성되지 않은 상태에서 사용할 가능성이 높아진다. 

 

 

[해결책]

1. 생성자를 활용하자 

@Getter
@AllArgsConstructor
public class Member {
    private final String userId;
    private final String userName;
}

// 사용 예시
Member member = new Member("user_0115", "hennie");

 

2. 빌더 패턴을 활용하자 

@Getter
@Builder
public class Member {
    private final String userId;
    private final String userName;
}

// 사용 예시
Member member = Member.builder()
    .userId("user_0115")
    .userName("hennie")
    .build();

 

 

[Build 패턴]

1.entity에 @Builder 추가 

이렇게 @Builder 를 생성 된 객체는 값이 변경되지 않으므로, 데이터의 무결성을 보장할 수 있다. 

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
public class Member {

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

    @Column(nullable = false, unique = true)
    private final String userId;

    @Column(nullable = false)
    private final String userName;
}

 

2. .builder() 를이용하여 가독성과 유지보수의 효율을 높이자 

한눈에봐도 코드가 간결하고 어떤 필드가 설정 되어 있는지 알 수 있다. 

Member member = Member.builder()
    .userId("user_0115")
    .userName("hennie")
    .email("hennie@email.com")
    .phoneNumber("010-1234-5678")
    .build();
해니01_15