본문 바로가기
Spring/- [시리즈] 간단한 CRUD 구현하기

CRUD 구현 9 - 간단한 페이지네이션(Pagination) 구현

by 정구정구 2026. 5. 18.

페이지네이션(Pagination)이란?

대용량 데이터를 여러 화면으로 나누어 표시하고 탐색하는 기법

 

 

프로젝트 구현

JpaRepository에서 제공하는 페이지네이션을 사용함

참고 : https://jundyu.tistory.com/19

 

[Spring Data] JPA를 활용해 pagination 구현하기

들어가며웹 애플리케이션에서 무한 스크롤이나 페이징을 통해 데이터를 나눠 불러오는 방식은 서버 성능과 사용자 경험을 모두 고려한 방식입니다. 한 번에 모든 데이터를 불러오면 페이지 로

jundyu.tistory.com

 

import 추가

모든 service에 추가 :

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;

 

모든 Controller에 추가 :

import org.springframework.data.domain.Page;

 

 

OrderRepository 수정

기존 :

@Query("""
    select o
    from Order o
    join fetch o.product
""")
List<Order> findAllWithProduct();

 

수정 후 :

@Query(
    value = """
        select o
        from Order o
        join fetch o.product
    """,
    countQuery = """
        select count(o)
        from Order o
    """
)
Page<Order> findAllWithProduct(Pageable pageable);

 

* countQuery를 추가한 이유 : 전체 데이터 개수를 알아야한다. 특히 fetch join에서는 countQuery를 따로 지정하는 것이 안전하다.

 

 

OrderService  수정

기존 :

public List<OrderResponse> findAll() {

        return orderRepository.findAllWithProduct()
                .stream()
                .map(OrderResponse::from)
                .toList();
}

 

수정 후 :

public Page<OrderResponse> getOrders(int page) {

    Pageable pageable = PageRequest.of(page, 5);

    return orderRepository.findAllWithProduct(pageable)
            .map(OrderResponse::from);
}

 

 

OrderController 수정

기존 :

@GetMapping
public List<OrderResponse> getOrders() {
    return orderService.getOrders();
}

 

수정 후 :

@GetMapping
public Page<OrderResponse> getOrders(
        @RequestParam(defaultValue = "0") int page
) {
    return orderService.getOrders(page);
}

 

 

ProductService 수정

기존 : 

    public List<ProductResponse> findAll() {
        return productRepository.findAll()
                .stream()
                .map(ProductResponse::from)
                .toList();
    }

 

수정 후 :

    public Page<ProductResponse> getProducts(int page) {

        Pageable pageable = PageRequest.of(page, 5);

        return productRepository.findAll(pageable)
                .map(ProductResponse::from);
    }

 

 

ProductController 수정

기존 : 

    @GetMapping
    public List<ProductResponse> findAll() {
        return productService.findAll();
    }

 

수정 후 :

    @GetMapping
    public Page<ProductResponse> getProducts(
            @RequestParam(defaultValue = "0") int page
    ) {
        return productService.getProducts(page);
    }

 

 

테스트

Product 테이블에 아래와 같이 데이터 추가

 

 

모든 상품 조회 (1 페이지)

method : GET

url : http://localhost:8080/products?page=0

 

response :

{
    "content": [
        {
            "id": 1,
            "name": "맥북 프로",
            "price": 2500000,
            "stock": 3
        },
        {
            "id": 2,
            "name": "폴드7",
            "price": 20000000,
            "stock": 3
        },
        {
            "id": 3,
            "name": "폴드1",
            "price": 100000,
            "stock": 3
        },
        {
            "id": 4,
            "name": "폴드2",
            "price": 200000,
            "stock": 3
        },
        {
            "id": 5,
            "name": "폴드3",
            "price": 300000,
            "stock": 4
        }
    ],
    "empty": false,
    "first": true,
    "last": false,
    "number": 0,
    "numberOfElements": 5,
    "pageable": {
        "offset": 0,
        "pageNumber": 0,
        "pageSize": 5,
        "paged": true,
        "sort": {
            "empty": true,
            "sorted": false,
            "unsorted": true
        },
        "unpaged": false
    },
    "size": 5,
    "sort": {
        "empty": true,
        "sorted": false,
        "unsorted": true
    },
    "totalElements": 12,
    "totalPages": 3
}

 

모든 상품 조회 (3 페이지)

method : GET

url : http://localhost:8080/products?page=2

 

response :

{
    "content": [
        {
            "id": 11,
            "name": "PS4 PRO",
            "price": 500000,
            "stock": 1
        },
        {
            "id": 12,
            "name": "PS5",
            "price": 550000,
            "stock": 1
        }
    ],
    "empty": false,
    "first": false,
    "last": true,
    "number": 2,
    "numberOfElements": 2,
    "pageable": {
        "offset": 10,
        "pageNumber": 2,
        "pageSize": 5,
        "paged": true,
        "sort": {
            "empty": true,
            "sorted": false,
            "unsorted": true
        },
        "unpaged": false
    },
    "size": 5,
    "sort": {
        "empty": true,
        "sorted": false,
        "unsorted": true
    },
    "totalElements": 12,
    "totalPages": 3
}

 

모든 상품 조회 (4 페이지) (빈 페이지)

method : GET

url : http://localhost:8080/products?page=3

 

response :

{
    "content": [],
    "empty": true,
    "first": false,
    "last": true,
    "number": 3,
    "numberOfElements": 0,
    "pageable": {
        "offset": 15,
        "pageNumber": 3,
        "pageSize": 5,
        "paged": true,
        "sort": {
            "empty": true,
            "sorted": false,
            "unsorted": true
        },
        "unpaged": false
    },
    "size": 5,
    "sort": {
        "empty": true,
        "sorted": false,
        "unsorted": true
    },
    "totalElements": 12,
    "totalPages": 3
}