Java setter vs builder

김정훈·2024년 1월 7일
0

서론

서비스를 구현하다 보면 객체를 수정하는 서비스를 만들 때가 있다. 입문 단계에서는 Entity에 @Data를 쓰는 경우가 많아 setter를 많이 쓰게 된다. 하지만 점차 공부를 하다 보면 이런 글들이 보인다.
"setter는 코드의 불안전성을 야기하니 builder 패턴을 쓰자"
대체 왜 builder를 선호하는걸까?

본론

나는 setter도 장점이 있지만 builder는 setter의 장점도 커버할 수 있고 setter보다 더 큰 장점이 있기 때문에 builder를 쓴다.

builder의 장점

1. 객체의 필드가 많을 때 유용하다.
  • 회원가입 기능을 예로 들면
public Member join (JoinDto dto){
    Member member = new Member();
    member.setEmailId(dto.getEmailId());
    member.setPassword(dto.getPassword());
    member.setUsername(dto.getUsername());
    member.setRole("ROLE_MEMBER");
    }
           

이게 내가 builer를 사용하는 가장 큰 이유인데 setter는 한 줄 마다 매핑을 해줘야 하지만

public Member join (JoinDto dto){
	Member member = Member.builder()
                   .emailId(dto.getEmailId())
                   .password(dto.getPassword())
                   .username(dto.getUsername())
                   .role("ROLE_MEMBER")
                   .build();
   }

builder는 이렇게 간편하게 작성할 수 있어 필드가 많을 수록 편하다.

  1. 가독성이 좋다.
  • 프로필 수정 기능을 보겠다.
public Member updateName (UpdateNameDto dto){
	//repo에서 찾고
	Member member=memberRepository.findBytId(dto.getId());
    //수정
    member.setUsername(dto.getUsername());
        }

1번의 코드와 비교했을 때 setter는 메소드의 이름이 안보이는 상황(라인이 길어서)이라면 이게 수정인지 생서인지 구분이 어렵다.

public Member updateName (UpdateNameDto dto){
	//repo에서 찾고
	Member member=memberRepository.findBytId(dto.getId());
    //수정
    member.updateName(dto.getUsername());
        }

builder는 이렇게 커스텀하여 쓸 수 있기 때문에 구분이 쉽다.

  1. 무분별한 수정을 막는다.
  • 위의 코드를 보면 알 수 있듯이 setter는 setFieldName()만 쓰면 바로 수정을 할 수 있어 아주 위험한 상태라고 볼 수 있다.
  • 어떻게 생각해보면 builder도 updateName()만 하면 바로 수정하는데 뭔 차이냐고 생각할 수 있지만 updateName()은 내가 수정을 목적으로 할 때는 쓴다고 명확히 인지를 할 수 있지만 setName()은 수정, 생성, Dto 변환 등 많은 곳에서 똑같은 setter명을 쓰기 때문에 안좋다고 생각한다.
  • 하지만 builder를 다르게 보면 똑같은 기능을 여러 이름으로 구현해놓는 점에서 보면 setter가 더 경제적으로 보이기도 한다.

builder의 단점

하지만 builder에도 단점이 있는데
1. 필드가 적을 때
  • 필드가 적은 객체를 수정할 때 언뜻 보기에 setter와 builder의 코드 수가 비슷하다고 생각할 수 있다.
  • builder 코드를 저렇게 쓰려면 객체에 updateName 기능을 따로 만들어야 한다.
@Builder
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Schema(description = "회원 이메일")
    private String emailId;
    @Schema(description = "회원 비밀번호")
    private String password;
    @Schema(description = "회원 이름")
    private String username;
    @Schema(description = "권한")
    private String role;

    public void updateName(String username){
        this.username=username;
    }

}

builder 단점 보완

@Builder(toBuilder = true)

이렇게 toBuilder를 허용해주면 개별적인 수정이 가능하다.

  member = member.toBuilder()
                .username(dto.getUsername())
                .build();

그래서 뭐가 더 좋은건데?

여기에 대한 정답은 없다고 생각한다. 개인 프로젝트라면 오히려 setter가 더 효율적이라는 생각도 든다. 하지만 협업 프로젝트를 하게 되면 이 사람이 수정을 하는건지 생성을 하는건지.... 하는 가독성의 문제, 이상한 버그가 발생해서 발생 지점을 찾으려 해도 온통 다 setter라서 발생 지점을 찾는데 시간을 쓰는 문제 등 수많은 문제가 발생할 수 있기 때문에 builder 패턴이 더 좋다고 생각한다.
profile
백엔드 개발자

0개의 댓글