Back-end/Spring

[SpringBoot] 9주차 스터디 (페이징, JPA 연관관계)

poppy 2021. 10. 17. 19:38
반응형

1. 페이징(Paging)

- 페이징은 모든 데이터를 무한 스크롤하여 보는 것이 아니라 페이지로 나눠서 데이터를 나눠서 볼 수 있는 것이다. 다음 사진처럼 데이터가 페이지마다 나눠져서 볼 수 있는 방식이다. 

 

 

나만의 셀렉샵에서 페이징 처리하는 부분을 추가해보겠다. ProcductController 에서  "페이지 번호, 각 페이지의 상품 수, 정렬 방식, 오름차순 여부, 회원 정보" 를 입력받아 요쳥에 맞는 상품들을 ProductService 를 통해 조회한다. Pageable 은 쉽게 페이징 처리를 할 수 있도록 도와준다.

@RestController // JSON으로 데이터를 주고받음을 선언합니다.
public class ProductController {
    // 멤버 변수 선언
    private final ProductService productService;

    // 생성자: ProductController() 가 생성될 때 호출됨
    @Autowired
    public ProductController(ProductService productService) {
        // 멤버 변수 생성
        this.productService = productService;
    }

    // 로그인한 회원이 등록한 상품들 조회
    @GetMapping("/api/products")
    public Page<Product> getProducts(
            @RequestParam("page") int page,
            @RequestParam("size") int size,
            @RequestParam("sortBy") String sortBy,
            @RequestParam("isAsc") boolean isAsc,
            @AuthenticationPrincipal UserDetailsImpl userDetails
    ) {
        Long userId = userDetails.getUser().getId();
        page = page - 1; // 서버에서는 페이지가 0부터 시작
        return productService.getProducts(userId, page , size, sortBy, isAsc);
    }
}
@Service
public class ProductService {
    // 멤버 변수 선언
    private final ProductRepository productRepository;
    private static final int MIN_PRICE = 100;

    // 생성자: ProductService() 가 생성될 때 호출됨
    @Autowired
    public ProductService(ProductRepository productRepository) {
        // 멤버 변수 생성
        this.productRepository = productRepository;
    }

    public Page<Product> getProducts(Long userId, int page, int size, String sortBy, boolean isAsc) {
        Sort.Direction direction = isAsc ? Sort.Direction.ASC : Sort.Direction.DESC;
        Sort sort = Sort.by(direction, sortBy);
        Pageable pageable = PageRequest.of(page, size, sort);

        return productRepository.findAllByUserId(userId, pageable);
    }
}
public interface ProductRepository extends JpaRepository<Product, Long> {
    Page<Product> findAllByUserId(Long userId, Pageable pageable);
}

 

2. JPA 연관관계

- DB의 연관관계는 JPA 연관관계로 표현할 수 있다. 연관관계는 다음과 같은 예시처럼 만들어줄 수 있다.

 

관계 코드 연관관계 예시
1 : N (일대다) @OneToMany Order(1) : Food(N) 하나의 주문에 여러 음식 선택 가능
N : 1 (다대일) @ManyToOne Owner(N) : Restaurant(1) 하나의 음식점을 여러 주인이 소유 가능
1 : 1 (일대일) @OneToOne Order(1) : Coupon(1) 하나의 주문에 하나의 쿠폰만 사용 가능
N : M (다대다) @ManyToMany User(N) : Restaurant(M) 고객은 여러 음식점 찜 가능, 음식점은 여러 고객에게 찜 가능

 

JPA 연관관계를 사용하여 나만의 셀렉샵에 회원별 폴더 기능을 추가해보겠다. 회원(User)과 폴더(Folder)는 1:N 관계이므로 Folder에 @ManyToOne을 사용하여 관계를 만들어준다.

@Setter
@Getter // get 함수를 일괄적으로 만들어줍니다.
@NoArgsConstructor // 기본 생성자를 만들어줍니다.
@Entity // DB 테이블 역할을 합니다.
public class Folder extends Timestamped {

    public Folder(String name, User user) {
        this.name = name;
        this.user = user;
    }

    // ID가 자동으로 생성 및 증가합니다.
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Id
    private Long id;

    // 반드시 값을 가지도록 합니다.
    @Column(nullable = false)
    private String name;

    @ManyToOne
    @JoinColumn(nullable = false)
    private User user;
}

 

회원이 폴더를 조회하고 폴더를 생성할 수 있도록 FolderControlller, FolderService, FolderRepository를 다음과 같이 생성한다.

@RestController
public class FolderController {
    // 멤버 변수 선언
    private final FolderService folderService;

    @Autowired
    public FolderController(FolderService folderService) {
        // 멤버 변수 생성
        this.folderService = folderService;
    }

    // 회원이 등록한 모든 폴더 조회
    @GetMapping("/api/folders")
    public List<Folder> getFolders(@AuthenticationPrincipal UserDetailsImpl userDetails) {
        return folderService.getFolders(userDetails.getUser());
    }

    // 회원이 폴더 추가
    @PostMapping("/api/folders")
    public List<Folder> addFolders(@RequestBody FolderCreateRequestDto folderCreateRequestDto, @AuthenticationPrincipal UserDetailsImpl userDetails) {
        List<String> folderNames = folderCreateRequestDto.getFolderNames();
        return folderService.createFolders(folderNames, userDetails.getUser());
    }
}
@Service
public class FolderService {
    // 멤버 변수 선언
    private final FolderRepository folderRepository;

    // 생성자: ProductFolderService() 가 생성될 때 호출됨
    @Autowired
    public FolderService(FolderRepository folderRepository) {
        // 멤버 변수 생성
        this.folderRepository = folderRepository;
    }

    // 회원 ID 로 등록된 모든 폴더 조회
    public List<Folder> getFolders(User user) {
        return folderRepository.findAllByUser(user);
    }

    public List<Folder> createFolders(List<String> folderNameList, User user) {
        List<Folder> folderList = new ArrayList<>();
        for (String folderName : folderNameList) {
            Folder folder = new Folder(folderName, user);
            folderList.add(folder);
        }
        folderList = folderRepository.saveAll(folderList);
        return folderList;
    }
}
public interface FolderRepository extends JpaRepository<Folder, Long> {
    List<Folder> findAllByUser(User user);
}
반응형