최종 프로젝트를 진행하던 도중, 부모 엔티티와 자식 엔티티를 동시에 저장해야되는 상황이 있었다.
이를 해결하기 위해서 부모 엔티티를 먼저 저장한 뒤, 자식 엔티티를 저장하는 형태로 진행하려고 했다.
그러나, 생각해보니 JPA에서 제공해주는 좋은 기능이 있다는 것을 알게 되었다.
어떠한 상황인지, 어떻게 해결할 수 있을지 얘기해보는 시간을 가져보겠다.
필자가 진행중인 프로젝트의 ERD를 참고해보면 다음과 같다.
TB_RECIPE
가 부모이고 TB_RECIPE_FOOD
가 자식인 샘이며, 1대 N
구조를 가지고 있다.
즉, 하나의 레시피에는 여러 개의 레시피_식재료가 존재한다.
레시피를 생성하는 웹 페이지를 보면 다음과 같다.
여기서, 레시피에 대한 내용과 사용되는 재료를 등록하는 입력란이 존재한다. 이 부분이 레시피_식재료
부분에 해당된다.
하단의 등록 완료 버튼을 클릭하게 되면, 레시피 생성 API를 호출한 뒤, 레시피 식재료 생성 API를 차례대로 호출하게 된다. 그 이유는, 레시피 식재료 데이터는 부모 엔티티인 레시피가 존재해야만 하기 때문이다.
즉, 다음과 같이 프론트에서 API를 호출하는 구조를 가지게 된다.
fetch((레시피 생성 API), {method:'POST', body:(레시피에 대한 DTO)})
.then(res => res.json())
.then(data => {
recipeId = data.id;
fetch(레시피_식재료 생성 API, {method: 'POST', body:(레시피_식재료에 대한 DTO)})
.then(res => res.json())
.then(data => ...);
)
...
레시피 생성 API를 호출한 뒤, 성공했을 때 레시피_식재료 생성 API를 호출하는 형태이다.
차례대로 두 개의 API를 호출하는 방식보단, JPA의 특성을 활용해서 한 번에 처리할 수 있는 방법을 생각하던 도중, CascadeType.PERSIST
속성을 사용하면 어떨까라는 생각을 하게 되었다.
부모 엔티티에 자식 엔티티를 넣어둔 뒤, 부모 엔티티만 저장시키면 자식 엔티티도 자동으로 저장되게 해주는 것이다.
@Entity
public class Recipe extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy = "recipe", cascade = CascadeType.PERSIST)
List<RecipeFood> recipeFoods = new ArrayList<>();
...
}
이게 된다면, 프론트에선 레시피와 레시피 식재료에 대한 DTO를 한 번에 보내주면 될 것이다.
fetch((레시피 생성 API), {method:'POST', body:(레시피와 레시피_식재료에 대한 DTO)})
.then(res => res.json())
.then(data => ...)
...
양방향 매핑을 잘 설정해두면, API 호출 횟수를 줄일 수 있는 효과를 얻을 수 있으며, 좀 더 객체지향적인 프로그래밍을 할 수 있다.