ProductEntity와 Spring Data JPA를 이용하여 API를 설계해 보았다.
Controller, Service, Repository 3가지 계층으로 나누어 기능별로 구분하였다.
ProductEntity
@Entity
@Getter
public class ProductEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Long price;
protected ProductEntity() {
}
public ProductEntityDto toDto() {
return ProductEntityDto.builder()
.id(id)
.name(name)
.price(price)
.build();
}
@Builder
public ProductEntity(Long id, String name, Long price) {
this.id = id;
this.name = name;
this.price = price;
}
}
ProductEntityDto
@Getter
public class ProductEntityDto {
private Long id;
private String name;
private Long price;
public ProductEntity toEntity() {
return ProductEntity.builder()
.id(id)
.name(name)
.price(price)
.build();
}
@Builder
public ProductEntityDto(Long id, String name, Long price) {
this.id = id;
this.name = name;
this.price = price;
}
}
Controller
@GetMapping(params = "id")
public ProductEntityDto findById(@RequestParam Long id) {
return productService.findById(id);
}
@GetMapping(params = "name")
public ProductEntityDto findByName(@RequestParam String name) {
return productService.findByName(name);
}
@GetMapping(params = "price")
public List<ProductEntityDto> findAllByPrice(@RequestParam Long price) {
return productService.findAllByPrice(price);
}
@GetMapping(params = {"name", "price"})
public List<ProductEntityDto> findAllByNameAndPrice(@RequestParam String name,
@RequestParam Long price) {
return productService.findAllByNameAndPrice(name, price);
}
@PostMapping
public Long save(@RequestBody ProductEntityDto productEntityDto) {
return productService.save(productEntityDto);
}
컨트롤러를 구현할 때 고민했던 것은 동일한 url에 Get으로 요청이 올 때 파라미터 값으로 요청들을 구분하는 방법이었다.
처음에는 @RequestParam(required = false)로 하여 들어있는 파라미터 값에 따라 나눠서 기능하는 방식으로 시도해 봤다.
@GetMapping
public ProductEntityDto findById(@RequestParam(required=false) Long id,
@RequestParam(required=false) String name) {
if(id != null){
return productService.findById(id);
}
if(name != null){
return productService.findByName(name);
}
throw new NoParamException("파라미터 값이 없습니다.");
}
하지만 파라미터의 변수가 늘어날수록 생각해야 할 경우의 수가 너무 많아 효율적이지 못하였다.
그래서 @GetMapping의 인자로 param을 넘겨서 해당 파라미터 값이 있는 요청에 각각 대응시키도록 설계했다.
Service
public Long save(ProductEntityDto productEntityDto) {
List<ProductEntity> findProducts = productJpaRepository.findAllByName(productEntityDto.getName());
return productJpaRepository.save(productEntityDto.toEntity()).getId();
}
public ProductEntityDto findById(Long id) {
return productJpaRepository.findById(id).get().toDto();
}
public ProductEntityDto findByName(String name) {
return productJpaRepository.findByName(name).toDto();
}
public List<ProductEntityDto> findAllByPrice(Long price){
return productJpaRepository.findAllByPriceOrderByNameDesc(price).stream()
.map(ProductEntity::toDto)
.collect(Collectors.toList());
}
public List<ProductEntityDto> findAllByNameAndPrice(String name, Long price){
return productJpaRepository.findAllByNameAndPrice(name,price)
.stream()
.map(ProductEntity::toDto)
.collect(Collectors.toList());
}
service에서는 repository에서 데이터를 받아 dto로 변환 후 controller에 리턴해 주었다.
Repository
public interface ProductJpaRepository extends JpaRepository<ProductEntity, Long> {
public List<ProductEntity> findAllBy();
public ProductEntity findByName(String name);
public List<ProductEntity> findAllByName(String name);
public List<ProductEntity> findAllByPriceOrderByNameDesc(Long price);
public List<ProductEntity> findAllByNameAndPrice(String name, Long price);
//추가기능
public List<ProductEntity> findAllByNameLike(String name);
public ProductEntity findTopByOrderByPriceDesc();
public List<ProductEntity> findAllByNameContaining(String name);
@Query("select p.name from ProductEntity p where p.price = (select min(p2.price) from ProductEntity p2)")
public String findNameOfCheapestProduct();
@Query("select avg(p.price) from ProductEntity p")
public double findAvgPrice();
}
Spring Data JPA를 사용해서 repository를 만들었다.
추가적으로 다음 5가지 기능을 하는 메서드를 만들어 추가하였다.
- 전체 상품을 조회하는데, name이 모니터인 상품은 무시
- 가장 가격이 비싼 상품 조회하기
- 이름에 “컴”을 포함하는 상품 조회하기
- 가장 가격이 저렴한 상품의 ‘이름만’ 조회하기
- 상품 가격의 평균 구하기
참고로 @Query로 직접 jpql을 작성할 때 limit을 사용해 보았는데 @Query에서는 지원을 안 하여 다른 방식으로 작성했다.
배운 내용, 깨달은 점
간단한 기능임에도 시간이 많이 소요되었다. 사소한 것 하나하나 직접 찾아가면서 코드를 짜야하다 보니 생각해야 할 것이 많았다. @ExceptionHandler를 통해 예외 처리하는 방법, 엔티티 리스트를 dto로 변환하는 방법, JpaRepository에서 test 실행 방법 등을 찾아가면서 정리하는 과정을 통해 전체적인 흐름에 대해 좀 더 감을 잡을 수 있었다.
어려웠던 점, 반성하고 싶은 점 / 개선할 방법
mysql로 테스트를 진행하는 것이 아닌 h2 embedded db로 테스트를 하고자 했는데 여러 오류로 인해 실패하였다. 좀 더 찾아보고 적용시키는 방법을 알아봐야겠다.
'study' 카테고리의 다른 글
[JSCODE] 스프링 스터디 회고 (0) | 2023.03.31 |
---|---|
[JSCODE] 상점 API 추가 (0) | 2023.03.29 |
[JSCODE] Spring Data JPA 세팅하기 (0) | 2023.03.21 |
[JSCODE] controller, service, repository 구분하기 (0) | 2023.03.18 |
[JSCODE] Spring Boot 프로젝트 생성하기 (0) | 2023.03.14 |