230307 TIL #26 JPA Repository 메서드 / 순환참조문제 / DTO

김춘복·2023년 3월 6일
0

TIL : Today I Learned

목록 보기
26/571

230307 Today I Learned

Spring 2주차. 심화과정 lv.2를 보완하고 lv.3을 진행했다. 거의 다 진행했는데 순환에러가 터져서 문제 해결한다고 몇시간을 날렸다. 오늘 TIL에는 JPA Repository의 메서드를 정리하고 언급한 순환에러 해결 방법을 정리해보겠다.


아래 처럼 Velog Markdown에서 테이블 표 편하게 만들어주는 사이트
https://tableconvert.com/markdown-generator

JPA Repository 메서드

참고사이트

  • 주 메서드
method기능
save()레코드 저장 (insert, update)
findOne()primary key로 레코드 한건 찾기
findAll()전체 레코드 불러오기. 정렬(sort), 페이징(pageable) 가능
count()레코드 갯수
delete()레코드 삭제
  • 메서드 생성 키워드
메서드 이름 키워드예시설명
AndfindByEmailAndUserId(String email, String userId)여러필드를 and 로 검색
OrfindByEmailOrUserId(String email, String userId)여러필드를 or 로 검색
BetweenfindByCreatedAtBetween(Date fromDate, Date toDate)필드의 두 값 사이에 있는 항목 검색
LessThanfindByAgeGraterThanEqual(int age)작은 항목 검색
GreaterThanEqualfindByAgeGraterThanEqual(int age)크거나 같은 항목 검색
LikefindByNameLike(String name)like 검색
IsNullfindByJobIsNull()null 인 항목 검색
InfindByJob(String … jobs)여러 값중에 하나인 항목 검색
OrderByfindByEmailOrderByNameAsc(String email)검색 결과를 정렬하여 전달
  • SQL 쿼리 직접 입력
public interface UserRepository extends JpaRepository<User, Long> {

  @Query("select u from User u where u.emailAddress = ?1")
  User findByEmailAddress(String emailAddress);
}

순환 참조 문제

  • 문제 : 댓글 달린 게시글을 조회하니
nested exception is com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion 
(StackOverflowError) (through reference chain: 
com.sparta.blogpostspring.entity.User["commentList"]->
org.hibernate.collection.internal.PersistentBag[0]->
com.sparta.blogpostspring.entity.Comment["post"]->
com.sparta.blogpostspring.entity.Post["user"]->
com.sparta.blogpostspring.entity.User["commentList"]-> ...

위와 같은 StackOverFlow 문제가 터졌다.

  • 시도 :
  1. Entity에 private List<Post> postList로만 선언되어있어 new 처리
    private List<Comment> commentList = new ArrayList<>(); 와 같이
    new로 다 처리했다. 실수를 잘 처리했지만 이건 원인이 아니었다.

  2. @OneToMany 정리

	@OneToMany(mappedBy = "user")
    private List<Post> postList = new ArrayList<>();

    @OneToMany(mappedBy = "user")
    private List<Comment> commentList = new ArrayList<>();

이렇게 겹치는 @OneToMany가 원인인가 싶어서 둘 중 하나도 없에보고, 둘 다 없에봤지만 원인이 아니었다.

  1. DTO 사용
    구글링 도중 DTO를 사용하지 않으면 순환 참조되어 무한하게 참조된다고 해서
    중간중간에 이동 값들을 하나씩 DTO로 만들어 봤다.

    그러던 도중 Postman으로 찍어보니 위의 사진처럼 나와서 문제점을 바로 알게 되었다.
    게시글을 response로 반환할 때 PostResponseDto안에 CommentList는
    List<CommentResponseDto>로 반환해야 했는데 List<Comment>로 했던 것이다.
  • 해결 : 바로 PostResponseDto의 멤버변수를 private List<CommentResponseDto> commentList;
    로 바꾸고 생성자도 변경했다.
    public PostResponseDto(Post post) {

        this.username = post.getUser().getUsername();
        this.title = post.getTitle();
        this.content = post.getContent();
        this.id = post.getId();
        this.createdAt = post.getCreatedAt();
        this.modifiedAt = post.getModifiedAt();

        List<Comment> comments = post.getCommentList();
        if(comments != null){
            List<CommentResponseDto> commentResponseDtoList = new ArrayList<>();
            for (Comment comment : comments) {
                commentResponseDtoList.add(new CommentResponseDto(comment));
            }
            // 아래는 댓글작성시간순으로 정렬
            commentResponseDtoList.sort(Comparator
            .comparing(CommentResponseDto::getCreatedAt).reversed());
            this.commentList = commentResponseDtoList;
        }
    }

이렇게 DTO안에도 DTO로 값을 넣어주니 순환참조 문제가 사라졌다.

작업 중간에 생성자의 파라미터로 post와 comment List를 받아서 service쪽에서 정렬을 하기도 했지만 생성자 안에 파라미터로 하나만 받는게 더 깔끔한 것 같아서 이렇게 바꿨다.

  • 알게된 것 : 유저-게시글-댓글 처럼 이어져있는 구조일때는 순환 참조 문제가 발생할 수 있으니
    최종 response가 아니어도 다른 response의 중간단계일때도 DTO로 값을 옮겨주는 것이 좋다.

  • 날짜정렬방법 참고사이트 여기서 정렬방법은 좀 더 공부해보자.

profile
Backend Dev / Data Engineer

0개의 댓글