반응형
service(CartService, ProductItemService, ProductSearchService, ProductService)
ProductSearchService
package com.zerobase.cms.order.service;
import com.zerobase.cms.order.domain.model.Product;
import com.zerobase.cms.order.domain.repository.ProductRepository;
import com.zerobase.cms.order.exception.CustomException;
import com.zerobase.cms.order.exception.ErrorCode;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class ProductSearchService {
private final ProductRepository productRepository;
public List<Product> searchByName(String name) {
return productRepository.searchByName(name);
}
public Product getByProductId(Long productId) {
return productRepository.findWithProductItemsById(productId)
.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_PRODUCT));
}
public List<Product> getListByProductIds(List<Long> productIds) {
return productRepository.findAllByIdIn(productIds);
}
}
1. 클래스 선언
- @Service: Spring 프레임워크에게 이 클래스가 서비스(Service) 역할을 한다는 것을 알려줍니다. 서비스는 비즈니스 로직을 처리하는 역할을 합니다.
- @RequiredArgsConstructor: Lombok 어노테이션입니다. 이 클래스의 final 필드인 productRepository를 매개변수로 받는 생성자를 자동으로 생성해줍니다.
- public class ProductSearchService: ProductSearchService라는 이름의 클래스를 정의합니다.
- public: 이 클래스는 어디서든 접근 가능합니다.
2. 필드 선언
- private final ProductRepository productRepository: 상품 데이터를 데이터베이스에 접근하는 ProductRepository 객체를 저장하는 productRepository 필드를 선언합니다.
- private: 이 필드는 ProductSearchService 클래스 내에서만 접근 가능합니다.
- final: 이 필드는 한 번 값이 할당되면 변경할 수 없습니다. 즉, ProductSearchService 객체가 생성된 후에는 productRepository 필드를 변경할 수 없습니다.
- ProductRepository: 상품 데이터를 데이터베이스에 접근하는 인터페이스입니다.
3. 상품 이름으로 검색 메서드
- public List<Product> searchByName(String name): 상품 이름을 받아서 해당 이름을 포함하는 상품 목록을 검색하는 메서드입니다.
- public: 이 메서드는 어디서든 호출 가능합니다.
- List<Product>: 이 메서드는 Product 객체의 목록을 반환합니다.
- String name: 검색할 상품 이름을 나타내는 매개변수입니다.
- return productRepository.searchByName(name): productRepository를 사용하여 데이터베이스에서 상품 이름을 포함하는 상품 목록을 가져와서 반환합니다.
- productRepository.searchByName(name): ProductRepository 인터페이스에 정의된 searchByName 메서드를 호출하여 데이터베이스에서 상품 목록을 가져옵니다.
4. 상품 ID로 검색 메서드
- public Product getByProductId(Long productId): 상품 ID를 받아서 해당 ID에 해당하는 상품 정보를 검색하는 메서드입니다.
- public: 이 메서드는 어디서든 호출 가능합니다.
- Product: 이 메서드는 Product 객체를 반환합니다.
- Long productId: 검색할 상품 ID를 나타내는 매개변수입니다.
- return productRepository.findWithProductItemsById(productId) ...: productRepository를 사용하여 데이터베이스에서 상품 ID에 해당하는 상품 정보를 가져옵니다.
- productRepository.findWithProductItemsById(productId): ProductRepository 인터페이스에 정의된 findWithProductItemsById 메서드를 호출하여 데이터베이스에서 상품 정보를 가져옵니다. 이 메서드는 상품 정보와 함께 관련된 상품 아이템 정보도 함께 가져옵니다.
- .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_PRODUCT)): 상품 정보를 가져오지 못하면 NOT_FOUND_PRODUCT 에러 코드를 사용하여 CustomException 예외를 발생시킵니다.
5. 여러 상품 ID로 검색 메서드
- public List<Product> getListByProductIds(List<Long> productIds): 상품 ID 목록을 받아서 해당 ID 목록에 해당하는 상품 목록을 검색하는 메서드입니다.
- public: 이 메서드는 어디서든 호출 가능합니다.
- List<Product>: 이 메서드는 Product 객체의 목록을 반환합니다.
- List<Long> productIds: 검색할 상품 ID 목록을 나타내는 매개변수입니다.
- return productRepository.findAllByIdIn(productIds): productRepository를 사용하여 데이터베이스에서 상품 ID 목록에 해당하는 상품 목록을 가져와서 반환합니다.
- productRepository.findAllByIdIn(productIds): ProductRepository 인터페이스에 정의된 findAllByIdIn 메서드를 호출하여 데이터베이스에서 상품 목록을 가져옵니다.
요약
이 코드는 상품 정보를 검색하는 ProductSearchService 클래스입니다. 상품 이름으로 검색, 상품 ID로 검색, 여러 상품 ID로 검색하는 기능을 제공하며, 데이터베이스 연동 및 예외 처리를 수행합니다. Spring 프레임워크의 @Service 어노테이션을 사용하여 서비스 클래스로 등록하고, Lombok 라이브러리를 사용하여 코드의 보일러플레이트(반복적인 코드)를 줄이고, 가독성을 높였습니다.
- Repository: Repository는 데이터베이스에 접근하는 인터페이스입니다. Spring Data JPA를 사용하면 Repository 인터페이스만 정의하면 Spring이 자동으로 구현체를 생성해줍니다. Repository를 사용하면 데이터베이스 연동 코드를 간결하게 작성할 수 있고, 데이터베이스를 쉽게 교체할 수 있습니다.
- 예외 처리(Exception Handling): 예외 처리는 프로그램 실행 중에 발생할 수 있는 예외 상황을 처리하는 방법입니다. 예외 처리를 통해 프로그램이 비정상적으로 종료되는 것을 방지하고, 사용자에게 유용한 오류 메시지를 제공할 수 있습니다.
- Optional: Optional은 값이 있을 수도 있고 없을 수도 있는 컨테이너 클래스입니다. Optional을 사용하면 null pointer exception을 방지하고, 코드를 더 안전하게 작성할 수 있습니다.
ProductService
package com.zerobase.cms.order.service;
import com.zerobase.cms.order.domain.model.Product;
import com.zerobase.cms.order.domain.model.ProductItem;
import com.zerobase.cms.order.domain.product.AddProductForm;
import com.zerobase.cms.order.domain.product.UpdateProductForm;
import com.zerobase.cms.order.domain.product.UpdateProductItemForm;
import com.zerobase.cms.order.domain.repository.ProductRepository;
import com.zerobase.cms.order.exception.CustomException;
import com.zerobase.cms.order.exception.ErrorCode;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
public class ProductService {
private final ProductRepository productRepository;
@Transactional
public Product addProduct(Long sellerId, AddProductForm form) {
return productRepository.save(Product.of(sellerId, form));
}
@Transactional
public Product updateProduct(Long sellerId, UpdateProductForm form) {
Product product = productRepository.findBySellerIdAndId(sellerId, form.getId())
.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_PRODUCT));
product.setName(form.getName());
product.setDescription(form.getDescription());
for (UpdateProductItemForm itemForm : form.getItems()) {
ProductItem item = product.getProductItems().stream()
.filter(pi -> pi.getId().equals(itemForm.getId()))
.findFirst().orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_ITEM));
item.setName(itemForm.getName());
item.setPrice(itemForm.getPrice());
item.setCount(itemForm.getCount());
}
return product;
}
@Transactional
public void deleteProduct(Long sellerId, Long productId) {
Product product = productRepository.findBySellerIdAndId(sellerId, productId)
.orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_PRODUCT));
productRepository.delete(product);
}
}
1. 클래스 선언
- @Service: Spring 프레임워크에게 이 클래스가 서비스(Service) 역할을 한다는 것을 알려줍니다. 서비스는 비즈니스 로직을 처리하는 역할을 합니다.
- @RequiredArgsConstructor: Lombok 어노테이션입니다. 이 클래스의 final 필드인 productRepository를 매개변수로 받는 생성자를 자동으로 생성해줍니다.
- public class ProductService: ProductService라는 이름의 클래스를 정의합니다.
- public: 이 클래스는 어디서든 접근 가능합니다.
2. 필드 선언
- private final ProductRepository productRepository: 상품 데이터를 데이터베이스에 접근하는 ProductRepository 객체를 저장하는 productRepository 필드를 선언합니다.
- private: 이 필드는 ProductService 클래스 내에서만 접근 가능합니다.
- final: 이 필드는 한 번 값이 할당되면 변경할 수 없습니다. 즉, ProductService 객체가 생성된 후에는 productRepository 필드를 변경할 수 없습니다.
- ProductRepository: 상품 데이터를 데이터베이스에 접근하는 인터페이스입니다.
3. 상품 추가 메서드
- @Transactional: 이 메서드를 트랜잭션으로 묶어줍니다. 트랜잭션은 데이터베이스의 상태를 변화시키는 작업의 단위입니다. 트랜잭션은 ACID (Atomicity, Consistency, Isolation, Durability) 속성을 보장해야 합니다.
- Atomicity: 트랜잭션 내의 모든 작업은 완전히 성공하거나 완전히 실패해야 합니다.
- Consistency: 트랜잭션이 완료된 후에도 데이터베이스의 무결성이 유지되어야 합니다.
- Isolation: 동시에 실행되는 트랜잭션은 서로 영향을 미치지 않아야 합니다.
- Durability: 트랜잭션이 성공적으로 완료되면 그 결과는 영구적으로 데이터베이스에 저장되어야 합니다.
- public Product addProduct(Long sellerId, AddProductForm form): 판매자 ID와 상품 추가 폼 데이터를 받아서 상품을 추가하는 메서드입니다.
- public: 이 메서드는 어디서든 호출 가능합니다.
- Product: 이 메서드는 Product 객체를 반환합니다.
- Long sellerId: 판매자 ID를 나타내는 매개변수입니다.
- AddProductForm form: 상품 추가 폼 데이터를 나타내는 매개변수입니다.
- return productRepository.save(Product.of(sellerId, form)): productRepository를 사용하여 데이터베이스에 상품 정보를 저장하고, 저장된 상품 정보를 반환합니다.
- Product.of(sellerId, form): Product 클래스에 정의된 of 메서드를 호출하여 판매자 ID와 폼 데이터를 기반으로 새로운 Product 객체를 생성합니다.
- productRepository.save(...): ProductRepository 인터페이스에 정의된 save 메서드를 호출하여 데이터베이스에 상품 정보를 저장합니다. save 메서드는 Spring Data JPA에서 제공하는 기본적인 데이터 저장 메서드입니다.
4. 상품 수정 메서드
- @Transactional: 이 메서드를 트랜잭션으로 묶어줍니다.
- public Product updateProduct(Long sellerId, UpdateProductForm form): 판매자 ID와 상품 수정 폼 데이터를 받아서 상품 정보를 수정하는 메서드입니다.
- public: 이 메서드는 어디서든 호출 가능합니다.
- Product: 이 메서드는 수정된 Product 객체를 반환합니다.
- Long sellerId: 판매자 ID를 나타내는 매개변수입니다.
- UpdateProductForm form: 상품 수정 폼 데이터를 나타내는 매개변수입니다.
- Product product = productRepository.findBySellerIdAndId(sellerId, form.getId()) ...: 판매자 ID와 상품 ID를 사용하여 데이터베이스에서 상품 정보를 가져옵니다.
- productRepository.findBySellerIdAndId(sellerId, form.getId()): ProductRepository 인터페이스에 정의된 findBySellerIdAndId 메서드를 호출하여 데이터베이스에서 상품 정보를 가져옵니다.
- .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_PRODUCT)): 상품 정보를 가져오지 못하면 NOT_FOUND_PRODUCT 에러 코드를 사용하여 CustomException 예외를 발생시킵니다. orElseThrow는 Optional 객체에서 값을 가져오는 방법 중 하나이며, 값이 없을 경우 예외를 발생시킵니다.
- product.setName(form.getName()): 상품 이름을 폼 데이터의 상품 이름으로 변경합니다.
- product.setDescription(form.getDescription()): 상품 설명을 폼 데이터의 상품 설명으로 변경합니다.
- for (UpdateProductItemForm itemForm : form.getItems()) { ... }: 폼 데이터에 포함된 상품 아이템 목록을 순회하면서 각 상품 아이템 정보를 수정합니다.
- for: 반복문의 한 종류로, 특정 조건을 만족하는 동안 코드 블록을 반복해서 실행합니다. 여기서는 form.getItems()에서 가져온 각 itemForm에 대해 코드 블록을 실행합니다.
- UpdateProductItemForm itemForm : form.getItems(): form.getItems()는 UpdateProductForm 객체에서 상품 아이템 목록을 가져오는 메서드입니다. 이 목록에 있는 각 UpdateProductItemForm 객체를 순서대로 itemForm 변수에 할당합니다.
- ProductItem item = product.getProductItems().stream() ...: 현재 상품(product)에 포함된 상품 아이템 중에서 폼 데이터의 상품 아이템 ID와 일치하는 상품 아이템을 찾습니다.
- product.getProductItems().stream(): 상품에 속한 상품 아이템 목록을 스트림으로 변환합니다. 스트림은 데이터를 처리하는 데 유용한 도구이며, filter, map, reduce 등의 다양한 연산을 제공합니다.
- .filter(pi -> pi.getId().equals(itemForm.getId())): 스트림에서 상품 아이템 ID가 폼 데이터의 상품 아이템 ID와 같은 상품 아이템만 필터링합니다. filter는 스트림에서 특정 조건을 만족하는 요소만 선택하는 연산입니다.
- .findFirst(): 필터링된 상품 아이템 중에서 첫 번째 상품 아이템을 Optional 객체로 반환합니다. findFirst는 스트림에서 첫 번째 요소를 찾는 연산입니다.
- .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_ITEM)): 상품 아이템을 찾지 못하면 NOT_FOUND_ITEM 에러 코드를 사용하여 CustomException 예외를 발생시킵니다.
- item.setName(itemForm.getName()): 상품 아이템 이름을 폼 데이터의 상품 아이템 이름으로 변경합니다.
- item.setPrice(itemForm.getPrice()): 상품 아이템 가격을 폼 데이터의 상품 아이템 가격으로 변경합니다.
- item.setCount(itemForm.getCount()): 상품 아이템 수량을 폼 데이터의 상품 아이템 수량으로 변경합니다.
- return product: 변경된 상품 정보를 반환합니다.
5. 상품 삭제 메서드
- @Transactional: 이 메서드를 트랜잭션으로 묶어줍니다.
- public void deleteProduct(Long sellerId, Long productId): 판매자 ID와 상품 ID를 받아서 상품 정보를 삭제하는 메서드입니다.
- public: 이 메서드는 어디서든 호출 가능합니다.
- void: 이 메서드는 반환값이 없습니다.
- Long sellerId: 판매자 ID를 나타내는 매개변수입니다.
- Long productId: 삭제할 상품 ID를 나타내는 매개변수입니다.
- Product product = productRepository.findBySellerIdAndId(sellerId, productId) ...: 판매자 ID와 상품 ID를 사용하여 데이터베이스에서 상품 정보를 가져옵니다.
- productRepository.findBySellerIdAndId(sellerId, productId): ProductRepository 인터페이스에 정의된 findBySellerIdAndId 메서드를 호출하여 데이터베이스에서 상품 정보를 가져옵니다.
- .orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_PRODUCT)): 상품 정보를 가져오지 못하면 NOT_FOUND_PRODUCT 에러 코드를 사용하여 CustomException 예외를 발생시킵니다.
- productRepository.delete(product): productRepository를 사용하여 데이터베이스에서 상품 정보를 삭제합니다.
- productRepository.delete(product): ProductRepository 인터페이스에 정의된 delete 메서드를 호출하여 데이터베이스에서 상품 정보를 삭제합니다.
요약
이 코드는 상품 정보를 관리하는 ProductService 클래스입니다. 상품 추가, 수정, 삭제 기능을 제공하며, 데이터베이스 연동, 예외 처리, 반복문 활용 등 다양한 프로그래밍 기법을 사용합니다. Spring 프레임워크의 @Service 어노테이션을 사용하여 서비스 클래스로 등록하고, @Transactional 어노테이션을 사용하여 트랜잭션을 관리합니다. 또한, Lombok 라이브러리를 사용하여 코드의 보일러플레이트(반복적인 코드)를 줄이고, 가독성을 높였습니다.
- 트랜잭션(Transaction): 트랜잭션은 데이터베이스의 상태를 변화시키는 작업의 단위입니다. 트랜잭션은 ACID (Atomicity, Consistency, Isolation, Durability) 속성을 보장해야 합니다. 트랜잭션을 사용하면 데이터베이스의 무결성을 유지하고, 오류 발생 시 롤백(rollback)하여 데이터베이스의 상태를 이전 상태로 되돌릴 수 있습니다.
- 예외 처리(Exception Handling): 예외 처리는 프로그램 실행 중에 발생할 수 있는 예외 상황을 처리하는 방법입니다. 예외 처리를 통해 프로그램이 비정상적으로 종료되는 것을 방지하고, 사용자에게 유용한 오류 메시지를 제공할 수 있습니다.
- Repository: Repository는 데이터베이스에 접근하는 인터페이스입니다. Spring Data JPA를 사용하면 Repository 인터페이스만 정의하면 Spring이 자동으로 구현체를 생성해줍니다. Repository를 사용하면 데이터베이스 연동 코드를 간결하게 작성할 수 있고, 데이터베이스를 쉽게 교체할 수 있습니다.
- Optional: Optional은 값이 있을 수도 있고 없을 수도 있는 컨테이너 클래스입니다. Optional을 사용하면 null pointer exception을 방지하고, 코드를 더 안전하게 작성할 수 있습니다.
- for 반복문: for 반복문은 코드 블록을 여러 번 반복해서 실행하는 데 사용되는 기본적인 제어문입니다. for 반복문은 초기화, 조건, 증감의 세 부분으로 구성됩니다.
- 초기화: 반복문을 시작하기 전에 실행되는 부분입니다. 주로 반복 횟수를 제어하는 변수를 초기화합니다.
- 조건: 코드 블록을 실행할지 여부를 결정하는 부분입니다. 조건이 참(true)이면 코드 블록이 실행되고, 거짓(false)이면 반복문이 종료됩니다.
- 증감: 코드 블록이 실행된 후에 실행되는 부분입니다. 주로 반복 횟수를 제어하는 변수를 증가시키거나 감소시킵니다.
반응형