Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[JPA] 순환참조 발생 #10

Closed
thals0 opened this issue Mar 22, 2023 · 1 comment
Closed

[JPA] 순환참조 발생 #10

thals0 opened this issue Mar 22, 2023 · 1 comment

Comments

@thals0
Copy link
Owner

thals0 commented Mar 22, 2023

🤯 문제점

각각의 게시글에 등록된 모든 댓글을 게시글과 같이 Client에 반환하기 구현 과정

  • 게시글에 등록된 모든 댓글과 게시글 같이 반환하기 위해서 comment를 돌면서 postid가 같으면 모두 꺼내서 list로 반환 + 해당 postId의 post 반환 하면 반환이 2번이 일어나야 하는데 ? 근데 한 함수에서 반환 2번 못하지 않나? 로 시작된 문제
  • 지금 이 전에 모든 비지니스 로직을 Comment entity에 post를 ‘@manytoone’ 형태로 넣었다 (FK는 다관계에 있어야 한다고 들어서 … )
  • 그런데 ? Board객체에 모든 comment리스트를 넣으려면 Board 앤티티에 ‘@onetomany’를 넣어야 하나?의 생각이 들어서 양방향 매핑을 하였다 .. 그랬더니 무한 재귀 즉, 순환참조가 발생했다.

👩🏻‍💻 시도해본 것들

  1. @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class)
    • Board와 Comment 엔티티 제일 위에 @JsonIdentityInfo(generator = ObjectIdGenerators.IntSequenceGenerator.class) 이 코드를 추가해주니까 순환참조가 멈췄다.
    • 근데 이상한 id값이 계속 생겼음 ..
    • 근본적인 문제(Dto를 사용안함)을 해결하려 하지 않고 제대로 알지도 못하는 어노테이션을 무지성으로 쓰니까 문제가 해결된 듯 보이더라도 다른 문제가 발생하는거라고 .. 알고 쓰자 !!

🥕 해결 방법

  • 순환참조가 발생하는 가장 큰 요인 중 하나가 BoardService에서 아래와 같이 List 형태로 받아올때.. 라고 했다
  • 이를 해결하기 위해서는 Dto를 사용해야 한다 !
  • 앤티티 객체를 직접 넘겨주다보니 MVC 패턴에도 위배가 되고, 객체에서 순환참조가 계속 일어나게 된 거였음

수정 전 코드

// BoardService

@Transactional(readOnly = true)
    public List<Board> getPosts(HttpServletRequest request) {
        User user = userService.isLogin(request);
        List<Board> posts = boardRepository.findAllByUserOrderByModifiedAtDesc(user);
        for (Board post : posts) {
            List<Comment> comments = commentRepository.findAllByPostOrderByModifiedAtDesc(post);
            post.addComments(comments);
        }

        return posts;
    }

수정 후 코드

    @Transactional(readOnly = true)
    public List<BoardResponseDto> getPosts(User user){
//        User user = userService.isLogin(request);
        // 해당 유저에 모든 글 불러옴
        List<Board> posts = boardRepository.findAllByUserOrderByModifiedAtDesc(user);
        List<BoardResponseDto> boardResponseDtoList =new ArrayList<>();
        // posts들 돌면서 해당 posts의 postid에 해당하는 댓글들 모두 찾아오기
        for(Board post: posts){
            List<CommentResponseDto> commentResponseDtos = getCommentResponseList(post);
            boardResponseDtoList.add(new BoardResponseDto(post, commentResponseDtos));
        }
        return boardResponseDtoList;
    }

✔️ 알게 된 점

  • 서비스와 컨트롤러 단에서 엔티티를 그대로 들고와서 사용해버리면 순환참조가 발생하기 쉽다.
  • 뿐만 아니라, MVC 패턴에도 위배되므로 엔티티를 직접적으로 사용하는게 아니라 DTO를 요청받고 응답하는 방식으로 구현하자 !
@thals0
Copy link
Owner Author

thals0 commented Mar 22, 2023

JPA에 관한 논쟁

  • JPA ORM을 너무 맹신하지는 말자 아직까지도 ORM을 쓰는게 좋다 아니다로 찬반이 많이 나오고있고, 실무에 들어가자마자 쿼리문 작성하는 걸 제일 먼저 배울 정도로 JPA로 작성하는걸 지양하는 회사들이 많음 오알엠은 양날의 검 ..

    • JPA ORM 장점

      • JPA는 쿼리를 자바처럼 짤 수 있음
      • DB를 교체해도 코드를 바꿀 필요가 없음 (DB 추적해서 그에맞는 쿼리문으로 바꿔줌)
    • JPA ORM 단점

      • 에러지옥 .. 뭐 조금만 잘못해도 에러를 내보낸다
      • jpa의 쿼리 성능이 괜찮은지 체크하고 리팩토링해야하는 경우가 있다 → 그럼 결론적으로 그냥 내가 쿼리를 짜는게 나을지도 ? 라고 생각하는 사람들이 있음
      • 또 뭐 실행계획 ? 이라는 것 때문ㅇ ㅔ ORM을 기피하는 경우도 있다고 한다.
    • 암튼 그래서 아직까지도 찬반논쟁에 있다고 함 ~

  • 그리고 연관관계 맵핑할 때 양방향 vs 단방향 논쟁도 있음

    • 양방향을 썻을 때는 비지니스로직 부분에서 쿼리를 1번만 요청해도 되지만, 단방향을 썻을 때는 2번을 보내야 한다 .. 등 코드가 조금 길어질 수 있음
    • 하지만 양방향은 !! 가끔 무시무시한 상황을 초례할 수 있다 .. 예를 들어 순환 참조 즉 무한 재귀를 돌면서 스택에 쌓이면 스택오버플로우 에러가 난다 .. 아웃오브메모리 .. 그럼 메모리가 터지면서 서버가 종료되는데 실무에서는 이게 너무 위험한 에러라 양방향을 피하는 회사들이 많다고 한다.

@thals0 thals0 closed this as completed May 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant