[ ORM ]/JPA

[ JPA ] E-commerce 프로젝트 - 상품 도메인 개발

환이s 2025. 1. 6. 16:21
728x90


Intro

 

안녕하세요. 환이s입니다👋

이전 포스팅에서 회원 도메인 개발까지 알아봤습니다.

이어서 상품 도메인을 개발해 보겠습니다. 상품 테스트는 회원 테스트와 비슷하므로 생략하겠습니다🙂


상품 도메인 개발 - 상품 엔티티 개발(비즈니스 로직 추가)

 

먼저 Item 엔티티에서 관리하는 stockQuantity를 변경하는 비즈니스 로직을 엔티티에서 처리하겠습니다.

서비스 단에서 처리할 수 있지만, 엔티티에서 처리하는 것이 응집도가 높은 설계로 볼 수 있습니다.

 

✅Item

import jpabook.jpashop.exception.NotEnoughStockException;
import lombok.Getter;
import lombok.Setter;
import jpabook.jpashop.domain.Category;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "dtype")
@Getter @Setter
public abstract class Item {

     @Id @GeneratedValue
     @Column(name = "item_id")
     private Long id;
     
     private String name;
     
     private int price;
     
     private int stockQuantity;
     
     @ManyToMany(mappedBy = "items")
     private List<Category> categories = new ArrayList<Category>();
     
     //==비즈니스 로직==//
     public void addStock(int quantity) {
     this.stockQuantity += quantity;
     }
     
     public void removeStock(int quantity) {
     int restStock = this.stockQuantity - quantity;
     
     if (restStock < 0) {
     		throw new NotEnoughStockException("need more stock");
    	 }
    	this.stockQuantity = restStock;
     }
}

 

예제 코드로는 setter를 통해 외부에서 수정할 수 있게 되어있지만, setter 없이 지금처럼 추가적인 비즈니스 로직을 만드는 것이 바람직합니다.

 

removeStock 메서드는 파라미터로 넘어온 수만큼 재고를 줄입니다.

따라서  만약 재고가 부족할 시 마이너스가 발생하는 것을 막기 위해 예외 처리를 해주겠습니다.

 

이러한 방식은 주로 상품을 주문할 때 사용합니다.

 

✅NotEnoughStockException

public class NotEnoughStockException extends RuntimeException {

     public NotEnoughStockException() {
     }
     
     public NotEnoughStockException(String message) {
     	super(message);
     }
     
     public NotEnoughStockException(String message, Throwable cause) {
     	super(message, cause);
     }
     
     public NotEnoughStockException(Throwable cause) {
     	super(cause);
     }	
}

 


상품 도메인 개발 -  리포지토리 개발

 

 

리포지토리에서는 상품을 등록하는 save()와 단일 조회 findOne(), 전체 조회 findAll() 메서드를 생성합니다.

 

✅ItemRepository

import jpabook.jpashop.domain.Item;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

import javax.persistence.EntityManager;
import java.util.List;

@Repository
@RequiredArgsConstructor
public class ItemRepository {
    
    private final EntityManager em;

    public void save(Item item) {
        if (item.getId() == null) {
            em.persist(item);
        } else {
            em.merge(item); //강제로 update
        }
    }

    public Item findOne(Long id) {
        return em.find(Item.class, id);
    }

    public List<Item> findAll() {
        return em.createQuery("select i from Item i", Item.class).getResultList();
    }
}

 

예제 코드를 풀어보자면

save() 메서드에 조건문을 보면 만약 id가 없으면 신규로 간주하고 persist() 메서드를 호출하여 새로운 Item 객체를 데이터베이스에 저장하는데, persist() 메서드는 객체를 영속화하며, 이때 데이터베이스에 생성된 ID가 Item객체에 설정됩니다.

 

반대로 id가 있거나 null이 아닌 경우, merge 메서드가 객체의 상태를 데이터베이스에 업데이트합니다. 이 과정에서 merge는 주어진 객체의 상태를 기반으로 새로운 객체를 생성하고, 그 객체를 통해 데이터베이스의 해당 레코드를 업데이트합니다.

 

이러한 방식은 새로운 데이터를 추가하거나 기존 데이터를 업데이트하는 실무에서 일반적인 패턴입니다.


상품 도메인 개발 -  서비스 개발

 

서비스 클래스는 리포지토리에서 모든 구현적인 코드를 다 구현해서 기능들을 불러오는 역할만 합니다.

 

✅ItemService

import jpabook.jpashop.domain.item.Item;
import jpabook.jpashop.repository.ItemRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class ItemService {

     private final ItemRepository itemRepository;
     
     @Transactional
     public void saveItem(Item item) {
     	itemRepository.save(item);
     }
     
     public List<Item> findItems() {
     	return itemRepository.findAll();
     }
     
     public Item findOne(Long itemId) {
     	return itemRepository.findOne(itemId);
     }
}

 

즉, 상품 서비스는 상품 리포지토리에 단순히 위임만 하는 클래스입니다.


마치며

 

상품 도메인은 별도로 어려운 로직이 없지만, 다음으로 소개해 드릴 주문 도메인 개발 쪽이 이 프로젝트에서 가장 핵심 기능을 담당합니다.

 

다음 포스팅에서 뵙겠습니다👋

 

위 포스팅 글은 김영한님의 실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발을 참고했습니다.

728x90