학습 과정에서 작성한 글로, 잘못된 내용이 있을 수 있습니다!
피드백은 언제나 감사히 받겠습니다 🙇
연관 관계 편의 메서드가 필요한 이유
JPA를 공부하면서, 연관 관계 편의 메서드를 정의해 사용하는 것이 중요하다는 것을 배웠다.
좀 더 설명해보자면,
다대일 / 일대다 관계를 양방향 매핑하여 사용할 때에는 양 쪽 객체의 연관 관계 상태를 모두 신경써주어야 한다.
연관 관계의 주인이 아닌 쪽은 DB 반영과는 관계가 없다.
하지만 그럼에도 이게 중요한 이유는 ORM을 사용하는 만큼,
신뢰성 있게 객체 상태를 유지하고 객체 그래프 탐색을 가능하게 하기 위함이다.
그런데 이 때 각 객체에서 관계를 설정해주기보다는, 하나의 메서드에서 둘을 동시에 설정해주는 것이 안전하다.
이런 메서드를 연관 관계 편의 메서드 라고 부른다.
// Order 엔티티에 member 연관관계를 설정할 때, Member의 orders 연관관계도 같이 설정한다.
public void setMember(Member member) {
this.member = member;
member.getOrders().add(this);
}
그러면 Setter 꼭 써야 돼? 🥺
예제를 통해서 배우기만 할 때는 괜찮았다.
그러나 프로젝트에서 이를 적용하려고 하니 하나의 난관에 봉착했다.
연관 관계 설정을 위한 예시는 적어도 내가 찾아본 것 중에서는 모두 Setter를 사용함을 전제로 하고 있었다.
또, 연관 관계 편의 메서드는 이미 생성된 엔티티에 연관 관계를 설정할 때 사용할 수 있는 메서드이다.
하지만 지금 우리 팀에서 진행하고 있는 프로젝트의 주 도메인인 Topic과 Pic 엔티티를 작성하며 이에 대한 문제를 발견했다.
이에 대해 고민하고, 팀원들과 의견을 나누며 결정한 내용을 정리해보려고 한다.
생성 로직에서만 연관 관계를 설정해주기
해당 비즈니스 로직에서 Pin(핀)은 Topic(토픽)의 연관 관계의 주인인데,
한 번 생성된 Pin의 Topic은 변하지 않는다.
다른 Topic에 사용할 경우 이를 복제한 새로운 Pin을 생성해 사용하며,
정책 상 Pin은 수정이 아닌 삭제만 가능하다. (다시 찍고 싶을 경우 삭제 후 생성)
즉, Setter가 전혀 필요하지 않다는 것이다.
그런데 단지 연관 관계 편의 메서드를 사용하기 위해서 Setter를 열어야 할까?
좋은 선택이 아니라고 생각했다.
단지 JPA를 사용하는 것이 Setter를 여는 이유가 될 수는 없다.
근본적인 문제부터 다시 생각하자면,
연관 관계 편의 메서드를 사용하는 이유는
연관 관계를 설정할 때 매핑된 양 쪽을 함께 설정해주어야 하기 때문이다.
그런데 비즈니스 로직에서 Pin과 Topic의 연관 관계를 설정하는 시점은 오직 Pin을 생성할 때 뿐이다.
그래서 Pin의 생성 로직에서 연관 관계 편의 메서드와 같은 일을 해주기로 했다.
그러나 아래 코드처럼 생성자에서 같은 일을 해줄 수는 없다. Pin 엔티티가 아직 생성되기 이전 시점이기 때문이다.
(Location은 Topic과 마찬가지로, Pin이 관계 맺는 엔티티이다)
private Pin(
String name,
String description,
Location location,
Topic topic
) {
validateName(name);
validateDescription(description);
this.name = name;
this.description = description;
this.location = location;
this.topic = topic;
topic.addPin(this); // 안됨!
location.addPin(this); // 안됨!
}
정적 팩터리 메서드를 사용하기
그렇다면 정적 팩터리 메서드를 사용하자.
그럼에도 우려되는 점은, 이 방식이 결코 일반적이지는 않은 것 같다는 것이다.
다른 코드 사용자가 언제 연관 관계가 설정되는지 알 필요가 있다.
그렇지 않으면, 자신이 별도로 Topic.getPins().add() 와 같은 코드를 작성해야 한다고 판단할 수도 있다.
따라서 of, from처럼 관례와도 같은 네이밍 대신, 구차하더라도(?ㅎㅎ) 구체적인 메서드명으로 이를 확실히 알리기로 했다.
public static Pin createPinAssociatedWithLocationAndTopic(
String name,
String description,
Location location,
Topic topic
) {
Pin pin = new Pin(name, description, location, topic);
location.addPin(pin);
topic.addPin(pin);
return pin;
}
결론
정적 팩터리 메서드로 객체 생성 시점에 연관 관계 설정을 하면,
불필요한 setter의 사용 없이도 양쪽의 연관 관계 상태를 함께 관리할 수 있다.
아직은 잘 사용하고 있는데,
다른 일반적인 관례가 아닌 방법이다 보니 놓치고 있는 다른 문제점이 없는지 예의주시해야 할 듯하다.
지금 생각나는 것은
1. 정적 팩터리 메서드로만 객체 생성을 하도록 강제할 것
2. topic.addPin(), location.addPin()을 아무데서나 또 호출할 수 있다. 이에 대한 검증 로직이 필요하다.
또 무엇이 있을까?!
'공부 > JPA' 카테고리의 다른 글
JPA 기본키 생성 전략과 선택 기준 (0) | 2023.07.16 |
---|