본문 바로가기
Spring/JPA

List 타입을 Page 타입으로 리팩토링

by YoonJong 2023. 1. 28.
728x90

현재 JPA 를 이용해서 페이징 기능을 사용하고 있으나, 불필요한 Response 생성 및 API 를 클라이언트에게 보내줄때 좀 더 편리하게 사용할 수 있도록 리팩토링을 하려고 합니다.

 

먼저 현재 코드를 보면 아래와 같습니다.

이전에 가장 중요한 것은 어떤 반환값이든 엔티티를 반환값으로 사용하지 않습니다.

엔티티가 변경되면 API 를 사용하고 있는 화면에 장애가 발생할 수 있기 때문에 Dto 클래스를 필수로 사용합니다.

 

List<GoodsPageResponse> 타입을 갖고 있는 것을 볼 수 있는데, GoodsPageResponse를 따로 만들어준 이유는 페이징에 필요한 정보만 축약해서 응답해주려고 만들었습니다.

// 상품 전체 조회
@GetMapping("/goods")
@ResponseStatus(HttpStatus.OK)
@ApiOperation(value = "상품 전체 조회")
public List<GoodsPageResponse> goodsFindAll(@PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable) {
    return goodsService.goodsFindAll(pageable);
}


public class GoodsPageResponse {

    private Long memberId;
    private Long goodsId;
    private String goodsName;
    private String categoryName;
    private int price;
    private String description;
    private List<ImageResponse> imageList;
    private List<OptionResponse> options;
    private int totalPage;
    private int totalCount;
    private int pageNumber;
    private int currentPageSize;

필요한 응답 필드만 내려준다는 의미에서는 좋은 방법일 수 있지만, 이렇게 응답을 내리게 되면 문제가 발생할 수 있습니다.

먼저, Response 클래스를 하나 더 생성해야 합니다. 유지보수 및 관리 면에서 번거로워집니다.

GoodsResponse 클래스를 공통으로 사용하고 싶으나, GoodsPageResponse 를 따로 생성해서 상품 페이징이 필요한 곳에 따로 사용해야 합니다.

 

두번째, goodsRepository.findAll(Pageable); 을 이용하면 Page 타입으로 반환되는데, 이것을 다시 List 형태로 변환해야 하는 불필요한 과정을 거쳐야 합니다.

 

가장 큰 문제는 양날의 검처럼 발생됩니다.

필요한 응답코드만 내려줬으나,  페이징정보가 중복되는 것을 볼 수 있습니다.

만약 페이징 size가 10개라면 10개의 페이징 중복값이 나타납니다.

[
    {
        "memberId": 118,
        "goodsId": 217,
        "goodsName": "상품test",
        "categoryName": "신발",
        "price": 3000,
        "description": "나이키정품!",
      ...	
	...
        "totalPage": 25,
        "totalCount": 121,
        "pageNumber": 0,
        "currentPageSize": 5
    },

    {
        "memberId": 118,
        "goodsId": 216,
        "goodsName": "상품2000",
        "categoryName": "가방",
        "price": 3000,
        "description": "나이키정품!",
      ...
	...

        "totalPage": 25,
        "totalCount": 121,
        "pageNumber": 0,
        "currentPageSize": 5
    }
}

 


이제 List<GoodsPageResponse> 에서 Page<GoodsReponse> 로 리팩토링해보겠습니다.

응답에 필요한 필드값을 적어주었습니다.

GoodsResponse(Goods goods) 를 생성한 이유는 Service에서 Goods -> GoodsResponse 로 변환을 할때 간단히 사용하기 위해서 입니다.

public class GoodsResponse implements Serializable {

    private Long memberId;
    private Long goodsId;
    private String goodsName;
    private String categoryName;
    private int price;
    private String description;
    private List<ImageResponse> imageList;
    private List<OptionResponse> options;

    public GoodsResponse(Goods goods) {
        this.memberId = goods.getMemberId();
        this.goodsId = goods.getId();
        this.goodsName = goods.getGoodsName();
        this.categoryName = goods.getCategory().getCategory();
        this.price = goods.getPrice();
        this.imageList = ImageResponse.toResponse(goods);
        this.options = OptionResponse.toResponse(goods);

    }
}

 

컨트롤러 부분을 변경했습니다.

// 상품 전체 조회
@GetMapping("/goods")
@ResponseStatus(HttpStatus.OK)
@ApiOperation(value = "상품 전체 조회")
public Page<GoodsResponse> goodsFindAll(@PageableDefault(sort = "id", direction = Sort.Direction.DESC) Pageable pageable) {
    return goodsService.goodsFindAll(pageable);
}

서비스 부분을 변경해보겠습니다.

goodsRepository.findAll(pageable); 은 Page 타입을 반환하는데, 이 과정을 거친 후 형변환을 해주어야 합니다.

goods.map 을 이용해서 형변환을 간단히 진행할 수 있습니다.

new GoodsResponse(good) 은 GoodsResponse 클래스에서 만든 생성자 타입으로 만들어주었습니다.

// 상품 전체 검색
@Override
@TimerAop
@Transactional(readOnly = true)
@Cacheable(cacheNames = "goods", key = "#pageable")
public Page<GoodsResponse> goodsFindAll(Pageable pageable) {
    Page<Goods> goods = goodsRepository.findAll(pageable);

    Page<GoodsResponse> responses = goods.map(good -> new GoodsResponse(good));
    return responses;

}

 

테스트 코드의 리팩토링은 포스팅에서는 생략하겠습니다.

이제 포스트맨을 통해 테스트해보겠습니다.

이전과 다르게 응답 값 맨 아래에 공통적으로 1개만 응답해주는 것을 볼 수 있습니다.

 

보편적으로 페이징 기능은 이렇게 응답값을 내려주면 클라이언트에서 사용하는 것을 디폴트값으로 사용하고 있다고 합니다. 

728x90

댓글