[Spring Data JPA] page 사용 & 순환참조 주의

jin·2023년 1월 3일
0

Spring Boot

목록 보기
8/8
post-custom-banner

Repository

Page<Post> findAll(Pageable pageable);

PostSearchResponse

package com.finalproject_sns.domain.dto.post;

import com.finalproject_sns.domain.Post;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import org.springframework.data.domain.Page;

@Builder
@Getter
@AllArgsConstructor
public class PostSearchResponse {

    private Long id;
    private String title;
    private String body;
    private String userName;

    /* Page<Post> -> Page<Dto> 변환처리 */
    public static Page<PostSearchResponse> toDtoList(Page<Post> postList) {
        Page<PostSearchResponse> postDtoList = postList.map( p -> PostSearchResponse.builder()
                .id(p.getId())
                .title(p.getTitle())
                .userName(p.getUser().getUserName())
                .body(p.getBody())
                .build());
        return postDtoList;
    }
}

PostService

	// 생략
 	@Transactional(readOnly = true)
    public Page<PostSearchResponse> findAllPost(Pageable pageable) {
        Page<Post> findAll = postRepository.findAll(pageable);
        Page<PostSearchResponse> postDtoList = PostSearchResponse.toDtoList(findAll);
        return postDtoList;
    }
    // 생략

PostController

package com.finalproject_sns.controller;

import com.finalproject_sns.domain.dto.Response;
import com.finalproject_sns.domain.dto.post.*;
import com.finalproject_sns.service.PostService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;

import org.springframework.web.bind.annotation.*;


@RestController
@RequestMapping("/api/v1/posts")
@RequiredArgsConstructor
public class PostController {

    private final PostService postService;

    @GetMapping
    public Response<Page<PostSearchResponse>> findAllPost(@PageableDefault(size = 20, sort = "userName", direction = Sort.Direction.DESC) Pageable pageable) {
        Page<PostSearchResponse> postDtoList = postService.findAllPost(pageable);
        return Response.success(postDtoList);
    }

🚨 주의 사항

  • 양방향 참조된 Entity를 Controller에서 응답으로 return하게 되면, Entity가 참조하고 있는 객체가 지연 로딩되고, 로딩된 객체는 또 다시 본인을 참조하고 있는 객체를 호출하게 된다. 결국 서로 참조하는 객체를 계속 호출하면서 무한 루프에 빠지게 된다.
    순환 참조가 일어나지 않도록 응답의 return으로 DTO로 두는 것이 안전하다.

Entity -> DTO 변환하기

PostRepositoryTest

PageRequest pageRequest = PageRequest.of(0, 20, Sort.by(Direction.DESC, "username"));
Page<Member> page = postRepository.findAll(userName, pageRequest);
Page<PostSearchResponse> postDtoList = page.map(p -> 
		new PostSearchResponse(p.getId(), p.getTitle(),
        p.getUser().getUserName(), p.getBody()));

PostSearchResponse

public static Page<PostSearchResponse> toDtoList(Page<Post> postList) {
        Page<PostSearchResponse> postDtoList = postList.map( p -> PostSearchResponse.builder()
                .id(p.getId())
                .title(p.getTitle())
                .userName(p.getUser().getUserName())
                .body(p.getBody())
                .build());
        return postDtoList;
    }
  • Controller에서 PostSearchResponse(DTO) 내보낸다.
profile
jin
post-custom-banner

0개의 댓글