Entity에 DTO에 있는 값을 넣어 생성하려고 할 때 주로 Builder 방식과 Setter 방식이 사용된다.
이 둘의 가장 큰 차이는 무엇이고, Setter 방식으로 Entity를 생성하는 것을 왜 지양하는지 알아보고자 한다.
우선 예제로 사용될 Entity는 다음과 같다.
@Entity
@Setter
@Getter
public class Temp {
@Id @GeneratedValue
private Long id;
private String name;
}
: 우선 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 함수를 통해 이름을 할당했다.
이렇게 했을 때 다음과 같은 문제들이 생긴다.
: update 함수, create 함수 모두 코드과 굉장히 비슷하다,,, 언뜻보면 둘의 차이가 뭔지 헷갈릴 수 있다.
현재는 update, create 로 기능이 명확해서 체감이 안될 수 있지만, 로직이 복잡한 함수라면 그 뜻이 헷갈릴 수 있게 되는 것이다.
: 객체 지향 프로그래밍에는 몇 가지 원칙이 존재한다. 그 중 하나가 바로 OCP(Open-Closed Principle) 이다.
OCP 는 쉽게 말해, 확장에 대해서는 열려있어야 하고, 수정에 대해서는 닫혀있어야 한다는 뜻이다.
Setter 방식은 코드에 set 어쩌구 식으로 드러나있기 때문에 사용자가 수정하기 너무 쉽다.
이는 명백히 OCP 를 위반하는 것이다 !
: 그렇다면 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과 같은 함수로 값을 변경한다.
이를 통해 사용자가 수정하기 까다로워 졌다.
: 우선 두 어노테이션이 의미하는 바를 먼저 살펴보겠다.
@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 로 전체 생성자를 만들어줘야하는 것이다.