엔티티를 작성할 때 제가 생각하는 몇가지 원칙이 있습니다.
그중 엔티티(객체)의 Setter 사용 금지 원칙(?) 에 대해 알아보겠습니다.
엔티티를 작성할 때 습관적으로 모든 필드에 Setter
를 생성하는 경우가 있습니다.
하지만 Setter
를 무분별하게 남용하다 보면 여기저기서 객체(엔티티)의 값을 변경할 수 있으므로 객체의 일관성을 보장할 수 없습니다.
그리고 setter
는 그 의도를 알기 힘들다고 생각합니다.
예를 들어 아래의 코드의 경우 멤버 객체를 set매소드를 통해 변경하는데 무엇을 하는건지 한번에 알 수 없습니다.(예제의 경우 간단한 변경이지만 복잡해질 경우 객체의 값을 변경하는 행위가 무엇을 위해 변경하는지 한 눈에 알기 힘들 수 있습니다.)
Member member = new Member();
member.setName("신이삼");
member.setTel("01050503453");
...
member.set...
아래의 코드 처럼 객체에서 매소드를 제공하여 변경하면 변경 의도를 한번에 알 수 있고, 객체 자신의 값을 자신이 변경하는 것이 객체 지향 관점에도 더 맞는다고 생각합니다.
// 멤버의 기본정보를 수정한다는 것을 한눈에 알 수 있다
member.changeBasicInfo("신이삼","01050502323");
//Member 엔티티 내부에 매서드 생성
public void changeBasicInfo(String name, String tel) {
this.name = name;
this.tel = tel;
}
객체의 일관성을 유지하기 위해 객체 생성 시점에 값들을 넣어줌으로써 Setter
사용을 줄일 수 있습니다.
// 객체의 생성자 설정 (필드가 많을경우 롬복의 @Builder 사용하면 좋다)
@Builder
public Member(String username, String password, String name, String tel, Address address) {
this.username = username;
this.password = password;
this.name = name;
this.tel = tel;
this.address = address;
}
// 객체 생성 시 값 세팅(빌더패턴 사용)
Member member = Member.Builder()
.username("name")
.password("1234")
.name("name);
.tel("01012345677")
.address(address)
.build();
아래와 같이 기본 생성자 접근자를 protected
로 변경하면 new Member()
사용을 막을 수 있어 객체의 일관성을 더 유지할 수 있습니다.
(protected로 설정하는 이유는 JPA 기본 스펙 상 기본 생성자가 필요한데 protected로 제어하는 것 까지 허용되기 때문입니다.)
// Member 엔티티
@Entity
@Getter
@Table(name = "member")
public class Member{
// 기본 생성자 protected로 접근 제어
protected Member(){};
...
}
롬복을 사용한다면 @NoArgsconstructor 어노테이션 설정을 통해 간단하게 설정할 수 있습니다.
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Table(name = "member")
public class Member {
}
무분별한 Setter 사용금지는 엔티티 뿐 아니라 객체 생성 및 변경 시 모두 해당하는 부분입니다. 객체의 일관성을 유지할 수 있어야 프로그램의 유지 보수성을 끌어 올릴 수 있기 때문에 일급 컬렉션 사용 등 많은 개발자들이 객체의 일관성을 유지하기 위해 노력을 기울이고 있다고 생각합니다.