인프런 커뮤니티 질문&답변

공부용님의 프로필 이미지
공부용

작성한 질문수

자바 ORM 표준 JPA 프로그래밍 - 기본편

영속성 전이(CASCADE)와 고아 객체

연관관계 설정 시에 ID값을 알고 있다면

작성

·

344

0

학습하는 분들께 도움이 되고, 더 좋은 답변을 드릴 수 있도록 질문전에 다음을 꼭 확인해주세요.

1. 강의 내용과 관련된 질문을 남겨주세요.
2. 인프런의 질문 게시판과 자주 하는 질문(링크)을 먼저 확인해주세요.
(자주 하는 질문 링크: https://bit.ly/3fX6ygx)
3. 질문 잘하기 메뉴얼(링크)을 먼저 읽어주세요.
(질문 잘하기 메뉴얼 링크: https://bit.ly/2UfeqCG)

질문 시에는 위 내용은 삭제하고 다음 내용을 남겨주세요.
=========================================
[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? (예/아니오)
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? (예/아니오)
3. 질문 잘하기 메뉴얼을 읽어보셨나요? (예/아니오)

[질문 내용]
안녕하세요. 간단한 내용이지만 조금 고민이 있어서 질문드립니다.

 

요청을 통해서 받은 데이터를 통해서나 로직상에서 연관관계에 필요한 Entity의 id값을 알고 있는 경우 필요한 Entity를 DB에서 조회하는 과정 없이 id값만 초기화된 Entity를 생성하여 주입하는 방식이 가능한 것으로 알고 있습니다.

 

이 때, 그렇게 주입된 Entity는 persist된 상태가 아니라는 점인데요. ( cascade 옵션이 적용 안 되었다는 가정 )

이런 경우에 두 가지 경우의 수를 고려하였습니다.

첫 번째는 연관관계 Entity가 연관관계 설정 외에는 사용될 일이 없는 로직의 경우이고

두 번째는 연관관계에 필요한 Entity가 이후에도 여러 로직에서 사용되는 경우입니다.

 

두 번째의 경우에는 사실 고민할 것 없이 ID를 이용해 Entity를 생성하는 것이 아닌 SELECT를 통해서 Entity를 조회하여 사용하는 것이 좋은 방법이고 EntityGraph를 맞추는 면에서도 당연히 해주어야 하는 것으로 사료됩니다.

하지만 첫 번째의 경우 굳이 Entity를 ID값을 통해 조회하는 과정이 필요한 지에 대한 의문입니다. 저런 단순한 로직의 경우 굳이 DB와의 정합성을 위해 조회 및 연관관계 편의메소드 등을 이용해서 EntityGraph를 맞춰줄 필요가 있나 싶었습니다.

이런 경우에서 실제 현업에서는 어떤식으로 처리하는 지 궁금합니다.

저렇게 각 상황에 따라 효율을 따져서 다르게 처리하는 지 아니면 ID를 통해 데이터를 조회하는 것은 크게 성능상 문제가 되지 않으니 불필요하더라도 로직의 통일성이나 혹시 모를 사이드 이펙트를 대비해서 조회한 뒤 연관관계 편의 메소드를 통해 EntityGraph까지 맞춰 주는 지 궁금합니다..!

저는 불필요하다고 생각하여 굳이 Entity그래프를 맞춰줄 필요가 없는 경우는 그런 과정을 생략하며 프로젝트를 하고 있었는데, 완성하고 보니 EntityGraph를 맞춰 주는 로직이 단 하나도 없는 걸 알게 되었습니다.

아무래도 복잡한 비즈니스 로직이 없어서 그럴 수도 있겠다는 생각은 들었지만 너무 심한 것 같아 제가 JPA를 제대로 쓰고 있는 게 맞나 싶어 질문드립니다..ㅠㅠ

답변 1

1

안녕하세요. 공부용님, 공식 서포터즈 y2gcoder입니다.

해당 질문을 제가 제대로 한 게 맞다면, 이부분을 그냥 id 필드로 하여 연결해주는 방법도 있지 않을까 싶습니다. 말씀해주신 것과 같이 연관관계를 통해 엔티티를 조회하는 로직이 필요없어 id만 가지고 초기화해준 엔티티를 연결하는 로직 뿐이라면 굳이 연관관계 엔티티를 설정해주지 않아도 되지 않을까 조심스럽게 생각합니다.

만약 연관관계에 해당하는 엔티티를 조회해야 할 일이 생긴다면 그때 제대로 매핑해줘도 괜찮지 않을까 생각합니다...!

 

감사합니다.

공부용님의 프로필 이미지
공부용
질문자

맞습니다! OneToMany entity들을 단방향으로 걸지 않고 객체적으로 손해를 보더라도 양방향으로 맞춰주다 보니 발생한 문제인 것 같습니다.

말씀하신대로 모두 ID필드로 변경하였습니다.

그런데 조금 궁금한 것이 있는데, 이렇게 변경을 하고 나니 언제 양방향이 필요한 것인 지 잘 감이 잡히질 않습니다..ㅠㅠ

예를 들어 게시물과 댓글이 있다고 할 때 댓글이 삭제되면 게시물의 전체 댓글 수를 감소시킨다고 하겠습니다.

댓글을 삭제하고 나면 게시글의 전체 댓글 수를 변경감지로 감소시키려 하는데 (동시성 고려 안 함), 이런 경우 양방향이 아니기 때문에

댓글에서 게시글을 조회하는 것이 아닌 id 필드를 이용해 게시물을 따로 조회하고 변경감지를 사용하는 것이 맞는 건가요?

요구 사항적으로는 댓글을 통해 게시글을 조회하는 경우가 없으니 단방향이 맞는 것 같은데 에플리케이션적으로 봤을 때에는 댓글의 delete 메소드 안에 게시물 엔티티의 총 댓글 수를 줄이는 로직이 함께 포함되어 있는 게 맞는 것 같기도 하고..

제가 도메인 주도적인 개발을 하고 있지는 않은 상태입니다만, 개발 방식에 따라 트레이드 오프를 생각하는 것이 맞는 건가요?

만약 도메인 주도적으로 개발을 한다고 가정한다면 양방향을 걸어 주는 것이 맞을까요?

혹시 그렇다고 한다면 양방향이 걸린 시점에서 댓글과 게시물 사이에는 연관관계와 관련된 로직을 저처럼 생략하면 안 되고 반드시 포함되도록 작성하는 게 맞을까요?

위처럼 두 Entity가 동시에 필요한 경우가 조금씩 발생하는데, 이럴 때에는 조인을 통해서 두 Entity를 불러오도록 하는 방법이 없을까요?

말씀하신 게시글과 댓글의 관계에서야말로 연관관계 엔티티가 있으면 좋은 환경 같습니다

여기서는 @ManyToOne으로 게시글을 조회할 수 있게 만들어놓으면 삭제할 때 좀 더 편하게 게시글을 불러와서, 게시글이 관리하고 있는 전체 댓글 수를 조정할 수 있을 것 같습니다!

도메인 주도 개발을 한다고 하면 도메인 엔티티와 JPA 엔티티를 따로 분리할 수도 있고, 그렇게 되면 영속성 레이어의 구현을 좀 더 자유롭게 할 수 있을 것 같습니다. 그래서 DDD를 적용한다면 어떻게 설계하시는가에 따라 다를 것 같습니다:)

공부용님의 프로필 이미지
공부용
질문자

계속 같은 질문 드려 죄송합니다..ㅠㅠ

만약 두 관계를 양방향으로 잡는다면

public void deleteComment(Long commentId){
    Comment comment = commentRepository.findById(commentId)
    
    // 삭제 전 검증 로직 등...
    ...
    ...
    ...

    commentRepository.delete(comment)
}

기존에 위처럼 작성되던 코드를

public void deleteComment(Long commentId){
    Comment comment = commentRepository.findById(commentId)
    
    // 삭제 전 검증 로직 등...
    ...
    ...
    ...

    comment.delete()

    commentRepository.delete(comment)
}

이렇게 바꾸어 주게 되는데, 이 때 comment.delete()는

public void delete(){
    this.board.getComments().remove(this);
    this.board.reduceTotalComment(this.totalReply + 1);
}

이런 형태가 될 것 같습니다.

 

그럼 처음에 제가 질문한 것처럼

this.board.getComments().remove(this);

해당 부분을 수행하기 위해 board의 모든 댓글이 다 불러와 지는 일이 생기는데 이는 크게 문제되지 않는 부분인가요?

만약 게시물의 댓글이 수백 수천개라고 가정하면 delete작업 하나를 위해서 board의 그 많은 댓글들이 불러와 지는 게 메모리적으로나 DB 조회 쿼리가 한 번 더 나가는 것으로 봤을 때 너무 아깝다는 생각이 자꾸 들었습니다 ㅠㅠ.. JPA를 사용하면 이 정도는 감수하고 코드를 작성하는 게 맞는걸까요?

댓글을 하나 삭제하는 로직이라면 그게 과연 게시글의 하위 메서드로 있는게 맞는지 잘 모르겠습니다!
댓글에 삭제 로직을 두고 댓글 id로 댓글을 삭제하면 되지 않을까요? 양방향 연관관계기는 하나 엄밀히 말하면 게시글 쪽에서 불러오는 댓글 목록들은 조회를 위함이지 수정이나 추가를 위한 경우는 드물다고 생각합니다.

댓글을 삭제하는 부분은 게시글을 기준으로 삭제하는 게 아니라면 양방향과는 상관없는 부분이라 생각합니다.

공부용님의 프로필 이미지
공부용
질문자

아..! 제가 코드를 너무 설명이 부족하게 적은 것 같네요.

위의 두 메소드는 service의 예상 로직 부분이고 delete메소드는 Comment Entity의 메소드입니다!

Service에서 comment를 굳이 조회하는 이유는 검증에 필요하다고 가정했습니다.

 

답글해 주신 내용으로는 양방향 연관관계더라도 생성과 삭제(생명주기가) 가 다른 쪽에 의존적이지 않다면 연관관계 편의 메소드는 굳이 사용하지 않아도 괜찮다는 뜻일까요?

몇가지 다른 질문들을 찾아 보면서 영한님의 댓글을 보았는데, 정확히 정리된 건 아니지만

"cascade옵션과 orphanremoval같은 옵션을 사용해서 Entity의 생명 주기를 다른 Entity가 관리하게 하는 경우가 있고 repository에 의존하는 경우도 있다. 이는 설계에 따라 달라질 수 있다."라고 말씀하시는 느낌이 들었습니다.

저의 경우 게시물을 통해 댓글의 생명주기를 관리하는 형태가 아니고 서로 조회하는 용도이기 때문에 양방향을 잡더라도 굳이 댓글이 삭제될 때 게시물의 List에서 댓글 삭제 과정이 필요하지 않을 것 같았습니다. repository를 이용해서 삭제를 하고 만약 로직상에서 Entity 그래프를 맞춰야 하는 때가 오면 그런 로직에서만 연관관계 편의 메소드를 호출해서 맞추면 될 것 같기도 합니다..! 이렇게 이해했는데 괜찮을까요?

네 정리해주신 의견에 저도 공감합니다!

위에서 전달해주신 comment의 delete 메서드 처럼 board에서 전체 댓글 수를 관리하는 필드가 있다면 말씀해주신 것처럼 댓글 삭제시 게시글을 업데이트 해주는 로직도 필요하기 때문에 연관관계 편의 메서드가 필요할 것 같습니다!

그리고 이 환경에서는 엔티티끼리의 연관관계를 설정해놓는 것이 도움이 될 것 같습니다.

 

공부용님의 프로필 이미지
공부용
질문자

현재 상황의 경우 위의 코드 처럼 Comment의 delete 메소드를

Public class Comment{

private Long id;
private Board board; ( 부모인 Board )
private String content;
private Integer totalReply; (대댓글 수)
...
...
..

public void delete(){      
    this.board.getComments().remove(this);      
    this.board.reduceTotalComment(this.totalReply + 1);  
}

}

이런 형태로 사용하라는 말씀이실까요?

사이드 이펙트를 방지하고 remove시에 발생되는 lazy loading은 성능적으로 조금 손해를 보는 형태라고 봐야겠군요..ㅎㅎ

댓글을 삭제하는 비즈니스 로직에서 board에 있는 comments를 활용할 일이 있을지 잘 모르겠습니다.

그래서 일단 댓글을 삭제할 때 결국 게시글에 있는 총 댓글 수 필드를 어떻게 처리해줘야 하냐가 문제일 것 같습니다.

제가 공부용님의 프로젝트 요구사항을 제대로 먼저 여쭤봤어야 했는데 죄송합니다.

정리하자면

게시글:댓글 이 1:N 관계이고, 게시글에 댓글과 관련된 필드(총댓글수)가 존재하는 상황이라고 이해했고,
이때 제가 서비스의 메서드 단에서 댓글을 삭제하려고 한다면

  1. 댓글 Id로 댓글 엔티티를 조회

  2. 댓글 엔티티에 연관된 board(1)을 지연로딩으로 불러와(혹은 댓글 엔티티 조회시 Fetch join으로 한꺼번에 조회하면 성능적으로는 더 괜찮을 것 같습니다.) 총 댓글 수를 업데이트한다.

  3. repository의 기능을 이용해 댓글을 삭제한다.

로 작성할 것 같습니다. 여기서 연관관계 편의 메서드를 사용하지 않는 이유는 댓글 삭제 로직에서 댓글 엔티티의 게시글 엔티티의 댓글 목록을 굳이 조회해서 사용할 일이 없기 때문입니다.

공부용님의 프로필 이미지
공부용
질문자

맞습니다 ㅎㅎ! 제가 설명을 두서없이 해서 이해시켜드리지 못 했던 것 같습니다.

지금 답변해 주신 내용이 제가 궁금했던 내용을 명확하게 설명해 주신 것 같습니다 ㅎㅎ.

말씀하신대로

"연관관계 편의 메서드를 사용하지 않는 이유는 댓글 삭제 로직에서 댓글 엔티티의 게시글 엔티티의 댓글 목록을 굳이 조회해서 사용할 일이 없기 때문입니다."

해당 부분이 궁금하던 부분이었습니다.

댓글 삭제 로직에서 게시물이 댓글을 조회해 올 필요가 없는데 성능을 조금 포기하고 사이드 이펙트 방지를 위해 연관관계 편의 메소드가 delete 메소드에 포함되어 있어야 하는 게 맞는가에 대한 질문이었습니다!

 

다른 커뮤니티에도 질문해 본 결과 트레이드 오프가 있다는 답변을 받았습니다!

성능을 가져가고 혹시 모를 객체 그래프에 사이드 이펙트를 방지하기 위해 코드 리뷰를 전제로 하거나 신경쓰면서 개발을 진행하느냐 또는 사이드 이펙트를 완전 배제하고 성능을 조금 떨어트리느냐의 문제였던 것 같습니다.

저의 경우는 자꾸 성능이 눈에 밟히고 현재 프로젝트 설계상 사이트 이펙트 발생이 없을 것으로 예상되어 쓰지 않는 것이 적절하다고 판단했는데 확신이 서지 않아 질문드렸던 것 같습니다ㅠㅠ

항상 친절하 답변 감사합니다!

파이팅입니다!

공부용님의 프로필 이미지
공부용

작성한 질문수

질문하기