QueryDSL 이란?
정적 타입을 이용하여, SQL과 같은 쿼리를 코드 형태로 생성할 수 있도록 해주는 오픈소스 빌더 API
즉, SQL문을 직접 하드코딩 하는 것이 아닌, 코드 형태로 작성하는 것이 특징이다.
이 컨트롤러는 다양한 REST API 엔드포인트를 정의하고, 상품 관련 요청을 처리하여 클라이언트에게 응답합니다.
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v2/products")
public class ProductController {
private final ProductService productService;
// 새 상품 생성 엔드포인트
@PostMapping
public ResponseEntity<ProductCreateRes> createProduct(
@AuthenticationPrincipal UserDetailsImpl userDetails, // 현재 사용자 정보
@Valid @RequestPart("req") ProductCreateReq req, // 새 상품 생성 요청
@RequestParam(value = "upload", required = false) List<MultipartFile> files // 선택적 파일 업로드
) throws IOException {
// HTTP 상태코드와 생성된 상품 정보 응답
return ResponseEntity.status(HttpStatus.OK)
.body(productService.createProduct(userDetails.getUser().getId(), req, files));
}
// 상품 업데이트 엔드포인트
@PutMapping("/{productId}")
public ResponseEntity<ProductUpdateRes> updateProduct(
@AuthenticationPrincipal UserDetailsImpl userDetails, // 현재 사용자 정보
@PathVariable Long productId, // 업데이트할 상품 ID
@Valid @RequestPart("req") ProductUpdateReq req, // 업데이트 요청
@RequestParam(value = "upload", required = false) List<MultipartFile> files // 선택적 파일 업로드
) throws IOException {
// HTTP 상태코드와 업데이트된 상품 정보 응답
return ResponseEntity.status(HttpStatus.OK)
.body(productService.updateProduct(userDetails.getUser().getId(), productId, req, files));
}
// 상품 삭제 엔드포인트
@DeleteMapping("/{productId}")
public ResponseEntity<ProductDeleteRes> deleteProduct(
@AuthenticationPrincipal UserDetailsImpl userDetails, // 현재 사용자 정보
@PathVariable Long productId // 삭제할 상품 ID
) {
// HTTP 상태코드와 삭제된 상품 정보 응답
return ResponseEntity.status(HttpStatus.OK)
.body(productService.deleteProduct(userDetails.getUser().getId(), productId));
}
// 단일 상품 조회 엔드포인트
@GetMapping("/{productId}")
public ResponseEntity<ProductGetRes> getProduct(
@PathVariable Long productId // 조회할 상품 ID
) {
// HTTP 상태코드와 조회된 상품 정보 응답
return ResponseEntity.status(HttpStatus.OK)
.body(productService.getProduct(productId));
}
// 상품 리스트 조회 엔드포인트
@GetMapping
public ResponseEntity<Page<ProductListRes>> getProductList(
@RequestParam("page") int page, // 페이지 번호
@RequestParam("size") int size, // 페이지 크기
@RequestParam(value = "sortBy", defaultValue = "createdAt") String sortBy, // 정렬 기준
@RequestParam(value = "isAsc", defaultValue = "false") boolean isAsc // 오름차순 여부
) {
// HTTP 상태코드와 페이지네이션된 상품 리스트 응답
return ResponseEntity.status(HttpStatus.OK)
.body(productService.getProductList(page - 1, size, sortBy, isAsc));
}
// 메인 화면 상품 리스트 조회 엔드포인트
@GetMapping("/main")
public ResponseEntity<List<ProductTopLikesListRes>> getProductListTopThree() {
// HTTP 상태코드와 메인 화면에 출력할 Top 3 상품 리스트 응답
return ResponseEntity.status(HttpStatus.OK)
.body(productService.getProductListByLikesTopThree());
}
// 상품 검색 리스트 조회 엔드포인트
@GetMapping("/search")
public ResponseEntity<Page<ProductSerachListRes>> getProductSearchList(
@RequestParam("page") int page, // 페이지 번호
@RequestParam("size") int size, // 페이지 크기
@RequestParam(value = "sortBy", defaultValue = "createdAt") String sortBy, // 정렬 기준
@RequestParam(value = "isAsc", defaultValue = "false") boolean isAsc, // 오름차순 여부
@RequestParam(value = "query") String search // 검색어
) {
// HTTP 상태코드와 검색 결과를 페이지네이션하여 응답
return ResponseEntity.status(HttpStatus.OK)
.body(productService.getProductSearchList(page - 1, size, sortBy, isAsc, search));
}
// 태그별 상품 리스트 조회 엔드포인트
@GetMapping("/tag")
public ResponseEntity<Page<ProductTagListRes>> getProductListByTag(
@RequestParam("page") int page, // 페이지 번호
@RequestParam("size") int size, // 페이지 크기
@RequestParam(value = "sortBy", defaultValue = "createdAt") String sortBy, // 정렬 기준
@RequestParam(value = "isAsc", defaultValue = "false") boolean isAsc, // 오름차순 여부
@RequestParam(value = "tag") String search // 태그
) {
// HTTP 상태코드와 태그별 검색 결과를 페이지네이션하여 응답
return ResponseEntity.status(HttpStatus.OK)
.body(productService.getProductSearchTagList(page - 1, size, sortBy, isAsc, search));
}
// 사용자별 상품 리스트 조회 엔드포인트
@GetMapping ("/user/{userId}")
public ResponseEntity<Page<UserProductListRes>> getUserProductList(
@PathVariable Long userId, // 사용자 ID
@RequestParam("page") int page, // 페이지 번호
@RequestParam("size") int size, // 페이지 크기
@RequestParam(value = "sortBy", defaultValue = "createdAt") String sortBy, // 정렬 기준
@RequestParam(value = "isAsc", defaultValue = "false") boolean isAsc // 오름차순 여부
) {
// HTTP 상태코드와 사용자별 상품 리스트를 페이지네이션하여 응답
return ResponseEntity.status(HttpStatus.OK)
.body(productService.getUserProductList(userId,page - 1, size, sortBy, isAsc));
}
}
product레퍼지토리에 ProductRepositoryCustom을 상속한다.이렇게 함으로써 ProductRepository는 ProductRepositoryCustom의 메서드도 사용할 수 있게 됩니다. 필요에 따라 ProductRepositoryCustom에 추가한 사용자 지정 메서드를 ProductRepository에서 사용할 수 있습니다.
// 이 인터페이스는 데이터베이스에서 Product 엔티티와 상호작용하는 메서드를 정의합니다.
public interface ProductRepository extends JpaRepository<Product, Long>, ProductRepositoryCustom {
// 삭제되지 않은 모든 제품을 페이지네이션하여 찾습니다.
Page<Product> findAllByDeletedFalse(Pageable pageable);
// 삭제되지 않은 상위 3개 제품을 좋아요 수를 기준으로 내림차순으로 찾습니다.
List<Product> findTop3ByDeletedFalseOrderByProductLikeCntDesc();
// 특정 사용자가 생성한 제품을 찾습니다.
List<Product> findProductsByUserId(Long userId);
// 제품의 ID로 제품을 찾는 기본 메서드로, 찾지 못하면 NotFoundProductException을 던집니다.
default Product findProductByIdWithThrow(Long id) {
return findById(id).orElseThrow(() ->
new NotFoundProductException(ProductErrorCode.NOT_FOUND_PRODUCT));
}
}
이 인터페이스는 사용자 지정 쿼리 메서드를 정의하며, 각 메서드는 페이지네이션과 추가적인 매개변수를 사용하여 제품 목록을 검색하거나 필터링합니다.
public interface ProductRepositoryCustom {
// 페이지네이션과 검색어를 이용하여 제품 목록을 이름으로 검색하여 가져옵니다.
Page<Product> getPostListByName(Pageable pageable, String search);
// 페이지네이션과 태그를 이용하여 제품 목록을 태그로 필터링하여 가져옵니다.
Page<Product> getProductListByTag(Pageable pageable, String tag);
}
이 클래스는 ProductRepositoryCustom 인터페이스의 메서드를 구현합니다. getPostListByName 메서드는 이름으로 제품 목록을 검색하고, getProductListByTag 메서드는 태그로 제품 목록을 필터링합니다. 필요에 따라 쿼리 생성과 페이지네이션을 수행하며, 결과를 반환합니다.
@Repository
@RequiredArgsConstructor
public class ProductRepositoryCustomImpl implements ProductRepositoryCustom {
private final JPAQueryFactory jpaQueryFactory;
QProduct product = QProduct.product;
// 이름으로 제품 목록을 검색하여 페이지네이션하여 반환합니다.
@Override
public Page<Product> getPostListByName(Pageable pageable, String search) {
// JPAQueryFactory를 사용하여 쿼리 생성
JPAQuery<Product> query = jpaQueryFactory
.selectFrom(product)
.where(product.deleted.eq(false)) // deleted가 false인 제품만 검색
.where(product.name.contains(search)); // 제품 이름에 검색어가 포함된 경우 검색
// 정렬 적용
if (pageable.getSort().isSorted()) {
for (Sort.Order order : pageable.getSort()) {
PathBuilder<Product> pathBuilder = new PathBuilder<>(Product.class,
product.getMetadata());
// 정렬 방향에 따라 제품 이름으로 정렬
query.orderBy(new OrderSpecifier<>(order.isAscending() ? Order.ASC : Order.DESC,
pathBuilder.get(order.getProperty(), Comparable.class)));
}
}
// 쿼리 실행하여 페이지네이션된 결과를 가져옴
List<Product> productList = query
.offset(pageable.getOffset()) // 페이지네이션 시작 오프셋 설정
.limit(pageable.getPageSize()) // 페이지 크기 설정
.fetch();
// 페이지 결과 반환
return PageableExecutionUtils.getPage(productList, pageable,
() -> query.fetchCount()); // 총 결과 수를 반환하는 함수 제공
}
// 태그로 제품 목록을 필터링하여 페이지네이션하여 반환합니다.
@Override
public Page<Product> getProductListByTag(Pageable pageable, String tag) {
QProductCategory productCategory = QProductCategory.productCategory;
// JPAQueryFactory를 사용하여 쿼리 생성
JPAQuery<Product> query = jpaQueryFactory
.selectFrom(product)
.leftJoin(product.productCategoryList, productCategory).fetchJoin() // 제품 카테고리 정보를 조인
.where(product.deleted.eq(false)) // deleted가 false인 제품만 검색
.where(hasTag(tag)); // 특정 태그를 가진 제품 검색
// 정렬 적용
if (pageable.getSort().isSorted()) {
for (Sort.Order order : pageable.getSort()) {
PathBuilder<Product> pathBuilder = new PathBuilder<>(Product.class,
product.getMetadata());
// 정렬 방향에 따라 제품 이름으로 정렬
query.orderBy(new OrderSpecifier<>(order.isAscending() ? Order.ASC : Order.DESC,
pathBuilder.get(order.getProperty(), Comparable.class)));
}
}
// 쿼리 실행하여 페이지네이션된 결과를 가져옴
List<Product> productList = query
.offset(pageable.getOffset()) // 페이지네이션 시작 오프셋 설정
.limit(pageable.getPageSize()) // 페이지 크기 설정
.fetch();
// distinct()를 통해 중복 제거된 결과를 반환
return PageableExecutionUtils.getPage(productList, pageable,
() -> query.distinct().fetchCount()); // 총 결과 수를 반환하는 함수 제공
}
// 태그가 포함된 제품을 검색하는 조건을 반환합니다.
private BooleanExpression hasTag(String tagName) {
return product.productCategoryList.any()
.category.name.eq(tagName); // 제품의 카테고리 중 태그 이름이 일치하는 경우
}
}
@Service
@RequiredArgsConstructor
@Transactional
public class ProductService {
// 필드 선언
private final ProductRepository productRepository; // 제품 리포지토리
private final UserRepository userRepository; // 사용자 리포지토리
private final CategoryRepository categoryRepository; // 카테고리 리포지토리
private final ProductCategoryRepository productCategoryRepository; // 제품 카테고리 매핑 리포지토리
private final ImageFileService imageFileService; // 이미지 파일 서비스
// 제품 생성 메서드
public ProductCreateRes createProduct(Long userId, ProductCreateReq req,
List<MultipartFile> files) throws IOException {
// 사용자 조회
User user = userRepository.findUserByIdWithThrow(userId);
// 제품 생성
Product product = Product.builder()
.name(req.name())
.price(req.price())
.quantity(req.quantity())
.region(req.region())
.content(req.content())
.user(user)
.build();
product = productRepository.save(product);
// 카테고리 추가
addCategory(req.categoryList(), product);
List<ImageCreateRes> imageCreateResList;
UserRes userRes = new UserRes(user.getId(), user.getNickname());
// 파일이 존재하는 경우 이미지 생성 서비스 호출
if (files != null) {
imageCreateResList = imageFileService.createImage(userId,
product.getId(), files);
} else {
// 파일이 없는 경우 예외 처리
throw new NotFoundImageFileException(UserErrorCode.NOT_IMAGE_FILE);
}
// 제품 생성 응답 반환
return new ProductCreateRes(product.getId(), product.getName(), product.getQuantity(),
product.getPrice(), product.getRegion(), product.getFinished(), userRes,
product.getContent(), req.categoryList(), imageCreateResList);
}
// 제품 업데이트 메서드
public ProductUpdateRes updateProduct(Long userId, Long productId, ProductUpdateReq req,
List<MultipartFile> files) throws IOException {
// 사용자 및 제품 조회
User user = userRepository.findUserByIdWithThrow(userId);
Product product = productRepository.findProductByIdWithThrow(productId);
// 사용자 권한 검증
validateProductUser(user, product);
List<ImageCreateRes> imageCreateResList;
// 파일이 존재하고 비어 있지 않은 경우 이미지 업데이트 서비스 호출
if(files != null && !files.isEmpty()) {
imageCreateResList = imageFileService.updateImage(userId, productId, files);
} else {
// 파일이 없는 경우 이미지 리스트 조회 후 업데이트
List<ImageListRes> imageList = imageFileService.getImages(productId);
imageCreateResList = imageList.stream()
.map(image -> new ImageCreateRes(image.imagePathUrl()))
.collect(Collectors.toList());
}
// 제품 업데이트
product.update(req);
updateCategory(req.categoryList(), product);
UserRes userRes = new UserRes(user.getId(), user.getNickname());
// 제품 업데이트 응답 반환
return new ProductUpdateRes(product.getId(), product.getName(), product.getQuantity(),
product.getPrice(), product.getRegion(), product.getFinished(), userRes,
product.getProductLikeCnt(), product.getContent(), req.categoryList(),
imageCreateResList);
}
@param productId 조회할 상품의 ID
@return 조회된 상품 정보를 담은 ProductGetRes 객체
@throws ResourceNotFoundException 상품이 존재하지 않는 경우 발생
public ProductGetRes getProduct(Long productId) {
// 상품 정보 조회
Product product = productRepository.findProductByIdWithThrow(productId); // 지정한 ID에 해당하는 상품을 찾고, 없으면 예외 발생
// 판매자 정보 추출
User user = product.getUser();
UserRes userRes = new UserRes(user.getId(), user.getNickname()); // 판매자의 ID와 닉네임으로 판매자 정보 생성
// 상품 카테고리 이름 추출
List<String> categories = product.getProductCategoryList().stream()
.map(productCategory -> productCategory.getCategory().getName()) // 각 카테고리의 이름만 추출
.toList();
// 상품 이미지 조회
List<ImageListRes> imageListRes = imageFileService.getImages(productId); // 상품 ID에 해당하는 이미지 목록 조회
// 조회된 상품 정보를 담은 ProductGetRes 객체 생성 및 반환
return new ProductGetRes(
product.getId(),product.getName(),product.getPrice(),product.getQuantity(),
userRes,product.getRegion(),categories,product.getProductLikeCnt(),product.getContent(),
product.getFinished(),imageListRes
);
}
상품 목록 조회 함수
@param page 조회할 페이지 번호
@param size 페이지당 조회할 상품 개수
@param sortBy 정렬 기준 필드
@param isAsc 오름차순 여부 (true: 오름차순, false: 내림차순)
@return 조회된 상품 목록을 담은 Page 객체
@Transactional(readOnly = true) // 읽기 전용 트랜잭션으로 처리
public Page<ProductListRes> getProductList(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); // 페이지 정보와 정렬 객체를 페이징 객체에 담음
// 상품 목록 조회 (삭제되지 않은 상품만 조회)
Page<Product> productList = productRepository.findAllByDeletedFalse(pageable);
// 조회된 상품 정보를 ProductListRes 객체로 변환하여 반환
return productList.map(product -> {
List<ImageListRes> imageListRes = imageFileService.getImages(product.getId()); // 상품 이미지 조회
List<String> categories = product.getProductCategoryList().stream()
.map(productCategory -> productCategory.getCategory().getName()) // 카테고리 이름 추출
.toList();
ImageListRes imageGetRes = imageListRes.isEmpty() ? null : imageListRes.get(0); // 첫 번째 이미지만 사용
return new ProductListRes(product.getId(),product.getName(),product.getPrice(),
product.getQuantity(),product.getProductLikeCnt(),categories,product.getRegion(),
imageGetRes);
});
}
@Transactional(readOnly = true)
public List<ProductTopLikesListRes> getProductListByLikesTopThree() {
// 상품 좋아요 수 기준 상위 3개 상품 목록을 가져옵니다.
List<Product> productList = productRepository.findTop3ByDeletedFalseOrderByProductLikeCntDesc();
// 상품 목록을 이용하여 응답용 객체로 매핑합니다.
return productList.stream()
.map(product -> {
// 상품 이미지 목록을 가져옵니다.
List<ImageListRes> imageListRes = imageFileService.getImages(product.getId());
// 상품 카테고리 목록을 가져와서 카테고리 이름으로 매핑합니다.
List<String> categories = product.getProductCategoryList().stream()
.map(productCategory -> productCategory.getCategory().getName())
.toList();
// 상품 이미지가 없을 경우에 대비하여 처리합니다.
ImageListRes imageGetRes = imageListRes.isEmpty() ? new ImageListRes(null) : imageListRes.get(0);
// 응답용 객체를 생성하여 반환합니다.
return new ProductTopLikesListRes(product.getId(), product.getName(),
product.getPrice(), product.getQuantity(), product.getProductLikeCnt(), categories,
product.getRegion(), imageGetRes);
})
.toList();
}
@Transactional(readOnly = true)
public Page<ProductSerachListRes> getProductSearchList(int page, int size, String sortBy,
Boolean isAsc, String search) {
// 페이지네이션 처리를 위한 정보를 설정합니다.
Sort.Direction direction = isAsc ? Sort.Direction.ASC : Sort.Direction.DESC;
Sort sort = Sort.by(direction, sortBy);
Pageable pageable = PageRequest.of(page, size, sort);
// 검색어를 이용하여 상품 목록을 가져옵니다.
Page<Product> productList = productRepository.getPostListByName(pageable, search);
// 상품 목록을 이용하여 응답용 객체로 매핑합니다.
return productList.map(product -> {
// 상품 이미지 목록을 가져옵니다.
List<ImageListRes> imageListRes = imageFileService.getImages(product.getId());
// 상품 카테고리 목록을 가져와서 카테고리 이름으로 매핑합니다.
List<String> categories = product.getProductCategoryList().stream()
.map(productCategory -> productCategory.getCategory().getName())
.toList();
// 상품 이미지가 없을 경우에 대비하여 처리합니다.
ImageListRes imageGetRes = imageListRes.isEmpty() ? new ImageListRes(null) : imageListRes.get(0);
// 응답용 객체를 생성하여 반환합니다.
return new ProductSerachListRes(product.getId(), product.getName(),
product.getPrice(), product.getQuantity(), product.getProductLikeCnt(), categories,
product.getRegion(), imageGetRes);
});
}
@Transactional(readOnly = true)
public Page<ProductTagListRes> getProductSearchTagList(int page, int size, String sortBy,
Boolean isAsc, String tag) {
// 페이지네이션 처리를 위한 정보를 설정합니다.
Sort.Direction direction = isAsc ? Sort.Direction.ASC : Sort.Direction.DESC;
Sort sort = Sort.by(direction, sortBy);
Pageable pageable = PageRequest.of(page, size, sort);
// 태그를 이용하여 상품 목록을 가져옵니다.
Page<Product> productList = productRepository.getProductListByTag(pageable, tag);
// 상품 목록을 이용하여 응답용 객체로 매핑합니다.
return productList.map(product -> {
// 상품 이미지 목록을 가져옵니다.
List<ImageListRes> imageListRes = imageFileService.getImages(product.getId());
// 상품 카테고리 목록을 가져와서 카테고리 이름으로 매핑합니다.
List<String> categories = product.getProductCategoryList().stream()
.map(productCategory -> productCategory.getCategory().getName())
.toList();
// 상품 이미지가 없을 경우에 대비하여 처리합니다.
ImageListRes imageGetRes = imageListRes.isEmpty() ? new ImageListRes(null) : imageListRes.get(0);
// 응답용 객체를 생성하여 반환합니다.
return new ProductTagListRes(product.getId(), product.getName(), product.getPrice(),
product.getQuantity(), product.getProductLikeCnt(), categories,
product.getRegion(), imageGetRes);
});
}
@Transactional(readOnly = true)
public Page<UserProductListRes> getUserProductList(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);
// 사용자의 상품 목록을 가져옵니다.
Page<Product> productList = productRepository.findProductsByUserIdAndDeletedFalse(userId, pageable);
// 상품 목록을 이용하여 응답용 객체로 매핑합니다.
return productList.map(product -> {
// 상품 이미지 목록을 가져옵니다.
List<ImageListRes> imageListRes = imageFileService.getImages(product.getId());
// 상품 카테고리 목록을 가져와서 카테고리 이름으로 매핑합니다.
List<String> categories = product.getProductCategoryList().stream()
.map(productCategory -> productCategory.getCategory().getName())
.toList();
// 응답용 객체를 생성하여 반환합니다.
return new UserProductListRes(product.getId(), product.getName(), product.getPrice(),
product.getQuantity(), product.getProductLikeCnt(), categories,
product.getRegion(), imageListRes.isEmpty() ? null : imageListRes.get(0));
});
}
public ProductDeleteRes deleteProduct(Long userId, Long productId) {
// 사용자와 상품을 가져옵니다.
User user = userRepository.findUserByIdWithThrow(userId);
Product product = productRepository.findProductByIdWithThrow(productId);
// 상품 삭제 권한을 검증합니다.
validateProductUser(user, product);
// 상품 이미지를 삭제합니다.
imageFileService.deleteImage(userId, productId);
// 상품을 삭제 처리합니다.
product.deleted();
return new ProductDeleteRes("삭제가 완료되었습니다.");
}
// 사용자와 상품의 권한을 검증합니다.
private void validateProductUser(User user, Product product) {
if (!user.getId().equals(product.getUser().getId())
&& !user.getRole().equals(UserRoleType.ADMIN)) {
throw new NotPermissionAuthority(ProductErrorCode.NOT_PERMISSION_AUTHORITHY);
}
}
// 카테고리 목록을 조회하여 반환합니다.
private List<Category> findCategoryList(List<String> categoryList) {
List<Category> categories = categoryRepository.findByNameIn(categoryList);
// 카테고리가 존재하지 않으면 예외를 발생시킵니다.
if (categoryList.size() != categories.size()) {
throw new NotFoundCategoryException(CategoryErrorCode.NOT_FOUND_CATEGORY);
}
return categories;
}
// 상품에 카테고리를 추가합니다.
private void addCategory(List<String> categoryList, Product product) {
List<Category> categories = findCategoryList(categoryList);
// 상품에 카테고리를 추가합니다.
categories.forEach(category -> {
ProductCategory productCategory = ProductCategory.builder()
.category(category)
.product(product)
.build();
productCategoryRepository.save(productCategory);
product.addProductCategory(productCategory);
});
}
// 상품에서 카테고리를 제거합니다.
private void removeCategory(List<String> categoryList, Product product) {
List<ProductCategory> productCategoryList = productCategoryRepository.findAllByProductAndCategory_NameIn(
product, categoryList);
// 상품에서 카테고리를 제거합니다.
productCategoryList.forEach(productCategory -> productCategoryRepository.delete(productCategory));
}
// 상품의 카테고리를 업데이트합니다.
private void updateCategory(List<String> reqCategoryList, Product product) {
List<String> currentCategories = product.getProductCategoryList()
.stream()
.map(ProductCategory::getCategory)
.map(Category::getName).toList();
// 제거 가능한 카테고리를 찾습니다.
List<String> removeableCategoryList = currentCategories.stream()
.filter(category -> !reqCategoryList.contains(category))
.toList();
// 추가 가능한 카테고리를 찾습니다.
List<String> addableCategoryList = reqCategoryList.stream()
.filter(category -> !currentCategories.contains(category))
.toList();
// 카테고리를 제거합니다.
removeCategory(removeableCategoryList, product);
// 카테고리를 추가합니다.
addCategory(addableCategoryList, product);
}
}
'프로젝트 > booktalk(책 중고 거래 서비스)' 카테고리의 다른 글
1/16 (0) | 2024.02.26 |
---|---|
jwt 토큰 인증 인가 (0) | 2024.02.02 |
product (1) | 2024.01.25 |
image파일 업로드 할때 리사이징 적용하기 (0) | 2024.01.24 |
[SpringBoot] AWS S3로 이미지 업로드하기 (1) | 2024.01.23 |