[SpringBoot] @Builder vs @Setter

지구본욱·2025년 4월 9일

Entity에 DTO에 있는 값을 넣어 생성하려고 할 때 주로 Builder 방식과 Setter 방식이 사용된다.
이 둘의 가장 큰 차이는 무엇이고, Setter 방식으로 Entity를 생성하는 것을 왜 지양하는지 알아보고자 한다.

우선 예제로 사용될 Entity는 다음과 같다.

@Entity
@Setter
@Getter
public class Temp {
    @Id @GeneratedValue
    private Long id;
    private String name;
}

1. @Setter

: 우선 Setter 는 "객체명.setXXX(값)" 방식으로 Entity를 생성한다.
예를 들면 아래와 같다.

@Service
public class TempService {
    public void update(Long id, String name){
        Temp temp=tempRepository.findById(id).orElse(null);
        temp.setName(name);
        tempRepository.save(temp);
    }
    public void create(String name){
        Temp temp=new Temp();
        temp.setName(name);
        tempRepository.save(temp);
    }
}

Temp 클래스의 temp 객체를 만들고 setName 함수를 통해 이름을 할당했다.
이렇게 했을 때 다음과 같은 문제들이 생긴다.

문제점 1. 이게 무슨 코드야?

: update 함수, create 함수 모두 코드과 굉장히 비슷하다,,, 언뜻보면 둘의 차이가 뭔지 헷갈릴 수 있다.
현재는 update, create 로 기능이 명확해서 체감이 안될 수 있지만, 로직이 복잡한 함수라면 그 뜻이 헷갈릴 수 있게 되는 것이다.

문제점 2. 수정이 너무 쉽잖아 . . .

: 객체 지향 프로그래밍에는 몇 가지 원칙이 존재한다. 그 중 하나가 바로 OCP(Open-Closed Principle) 이다.
OCP 는 쉽게 말해, 확장에 대해서는 열려있어야 하고, 수정에 대해서는 닫혀있어야 한다는 뜻이다.

Setter 방식은 코드에 set 어쩌구 식으로 드러나있기 때문에 사용자가 수정하기 너무 쉽다.
이는 명백히 OCP 를 위반하는 것이다 !

2. @Builder

: 그렇다면 Builder 방식은 무엇일까?
Builder 방식을 적용하기 위하여 Entity 코드를 약간 수정해보겠다.

@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class Temp {
    @Id @GeneratedValue
    private Long id;
    private String name;
    
    public void changeName(String name){
        this.name=name;
    }
}

@Builder 어노테이션이 생긴걸 볼 수 있고, @NoArgsConstructor & @AllArgsConstructor 어노테이션이 추가되었다.
뒤에 두 어노테이션을 적어야 하는 이유는 뒤에서 설명하겠다.

그러면 위의 Entity를 생성하고 업데이트하는 코드를 살펴보자.

@Service
public class TempService {
    public void update(Long id, String name){
        Temp temp=tempRepository.findById(id).orElse(null);
        temp.changeName(name);
        tempRepository.save(temp);
    }
    public void create(String name){
        Temp temp=Temp.builder()
                .name(name)
                .build();
        tempRepository.save(temp);
    }
}

우선 위의 "문제점 1. 이게 무슨 코드야 ?" 가 명확하게 해결되었다.
create는 빌드로 누가봐도 생성하는 코드이며, update는 수정하고 있음이 명확해졌다.

그 다음 "문제점 2. 수정이 너무 쉬워" 역시 해결되었다.
create에서 set으로 값을 설정하는 방식이 막혀있고 수정 역시 changeName과 같은 함수로 값을 변경한다.
이를 통해 사용자가 수정하기 까다로워 졌다.

3. @NoArgsConstructor & @AllArgsContructor

: 우선 두 어노테이션이 의미하는 바를 먼저 살펴보겠다.
@NoArgsConstructor 는 매개변수를 받지 않는 기본 생성자를 생성해주고,
@AllArgsContructor 는 모든 매개변수를 받는 생성자를 생성해준다.

예를 들면,

@NoArgsConstructor
public class Diary {
    private String title;
    private String content;
}

// 생성해주는 생성자
public Diary() {
    // 아무 것도 없음 (기본 생성자)
}
@AllArgsConstructor
public class Diary {
    private String title;
    private String content;
}

// 생성해주는 생성자
public Diary(String title, String content) {
    this.title = title;
    this.content = content;
}

이제 이해가 되었을 것이다 !

그렇다면 이 두 어노테이션을 왜 사용해야하는지 정리해보겠다.
1. @Entity는 기본 생성자가 없으면 예외를 발생시킬 수 있다.
-> 따라서 @NoArgsConstructor 가 꼭 필요하다 !
2. @Builder 어노테이션은 생성자가 이미 존재한다면 굳이 전체 생성자를 만들어 주지 않는다.
-> 따라서 전체 생성자가 존재하지 않는 상태인것이다.
-> 근데 builder 로 만들어준 값은 전체 값이 있어야 한다 -> 근데 전체 생성자가 없네 ? -> 에러발생
따라서 @AllArgsConstructor 로 전체 생성자를 만들어줘야하는 것이다.

profile
어제보다 1cm라도 나아가는 사람 (위로)

0개의 댓글