m1ndy5's coding blog

About Setter (feat. Builder) 본문

백엔드 with java/spring

About Setter (feat. Builder)

정민됴 2024. 1. 17. 23:59

스프링을 공부하다 보면 Setter 사용하지 마세요라는 말은 귀에 딱지 앉게 들어봤을 것이다.

이제부터 그 이유를 알아보자!

 

1. 의도를 파악하기 힘듦

member.setName("minsung");
member.setAge(20);

setter로 값을 변경하게 됐을 때는 왜 이 값을 변경했는지 setter 코드만 봐서는 유추가 힘들다.

이게 새롭게 값을 넣어주는 것인지, 기존의 값을 바꿔주는 것인지 한눈에 알기 쉽지 않다.

예를 들어보자

public void updateAge(int age){
	this.age = age;}

setAge보다 더 직관적인 메서드명을 가지고 있다.

이 메서드를 사용한다면 아 기존의 age를 바꿔주는구나라고 더 직관적으로 이해가 가능하다.

2. 객체의 일관성을 유지하기 힘듦

setter을 사용하게 되면 어디에서나 값을 변경할 수 있게 되고 그렇기 때문에 객체의 일관성을 유지하기 힘들다.

하지만 이 말이 잘 이해가 가지 않았다.

setter을 사용하지 않고 위처럼 updateAge메서드를 사용한다고 해도 결국 public 으로 선언이 되어있고 어디에서나 불러올 수 있는데 왜 setter만 객체의 일관성을 유지하기 힘들다는 건지 이해가 안됐다...

이해가 안가서 이곳 저곳에 문의를 해봤고 그 결과 제대로 설명한 블로그를 찾을 수 있었다.

 

예를 들어서 member의 grade를 변경하는 메서드가 있다고 생각해보자

public class 회원관리{
	public void updateMemberGrade(Member member){
    
  	if (member.getGrade().equals("VVIP")){
        	// 대충 VVIP등급 고객은 따로 관리한다는 코드
            return
        }
    	if (member.getGrade().equals("VIP")){
        	회원.setGrade("VVIP");
        }
        else if (member.getGrade().equals("NORMAL")){
        	회원.setGrade("VIP");
        }
        .
        .
        .
    }
}

이런식으로 회원관리 class의 updateMemberGrade에 Member의 grade를 올려주는 로직이 있다고 가정하자.

이 때, 코드에서 보면 Grade가 VVIP인 고객은 따로 등급을 관리한다.

근데 이 시스템을 몰랐던 개발자가 다른 코드에서 grade를 올리는데 VVIP는 따로 고려를 해주지 않았다고 했을 때

그 코드에서는 VVIP는 따로 체크하는 조건이 없었으므로 아무런 고려가 되지 않았을 것이고 만약 setter를 통해 VIP로 내려버린다고 해도 바로 내려졌을 것이다.

그럼 거기에도 VVIP만을 위한 코드를 또 넣어줘야하고 다른 곳도 변경가능한 곳이 있다면 계속해서 코드를 넣어줘야할 것이다.

그럼 만약에 VVIP만을 위한 코드가 바꼈다면?? 또 일일히 찾아서 넣어줘야할 것이다.

이는 OCP 원칙, 확장에는 개방적이고 수정에는 폐쇄적이어야한다는 원칙을 무시해버린다.

그렇다면 어떻게 바꿔야할까?

@Getter
public class Member{
	private int 구매금액;
    private String grade;
    
    public void upgradeMemberGrade(){
    	if (checkVVIP()){
        	return
        else {
        	//...
        }
   }
    
    private Boolean checkVVIP(){
    	if(this.grade.equals("VVIP")){
        	// ...
            return true
        }
        return false
    }

이런식으로 만들게 되면 다른 코드에서도 memberGrade를 변경하기 위해 upgradeMemberGrade메서드를 호출할 때 VVIP인지를 먼저 체크하게되고 혹시 vvip를 위한 로직이 변경된다 하더라도 checkVVIP코드만 변경하면 되기 때문에 간편하다.

또한 @Setter은 모든 필드에 변경 권한을 주지만 메서드를 정의해 사용하게 되면 변경을 원하는 필드만 변경할 수 있다.

 

그렇다면 객체를 생성할 때는 어떻게 생성할까?

바로 Builder 패턴을 사용해주면된다.

Builder의 장점으로는

1. 필요한 데이터만 설정해서 객체를 만들 수 있음

예를 들어 id값이나, createDate, updateDate과 같은 내가 직접설정해주지 않아도 되는 필드들은 굳이 값을 넣지않고 빌더를 통해 객체를 생성할 수 있다.

2. 가독성을 높일 수 있음

어? 그럼 필요한 데이터만을 설정해서 객체를 만들 수 있는건 생성자를 만들어서 하면되는데? 할 수 있다.

아래 예시를 보자

Member member = new Member("테스트", 19, 181, 121);

각 숫자가 무엇을 의미하는지 한번에 알 수 없다.

하지만 빌더를 사용하면

Member member = Member.builder()
		.name("테스트")
            	.age(19)
           	.height(181)
            	.iq(121).build();

이렇게 명확하게 각 값들이 어떤 필드에 들어가게 될지 정확하게 알 수 있다.

 

 

참고 블로그 : https://velog.io/@backfox/setter-%EC%93%B0%EC%A7%80-%EB%A7%90%EB%9D%BC%EA%B3%A0%EB%A7%8C-%ED%95%98%EA%B3%A0-%EA%B0%80%EB%B2%84%EB%A6%AC%EB%A9%B4-%EC%96%B4%EB%96%A1%ED%95%B4%EC%9A%94#%EB%AC%B8%EC%A0%9C%EC%A0%90-2%EC%9E%98%EB%AA%BB-%EC%A0%95%EC%9D%98%ED%95%9C-%EB%A9%94%EC%8B%9C%EC%A7%80%EC%9D%84-%ED%95%B4%EA%B2%B0%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95