본문 바로가기

Spring/웹 애플리케이션 개발

05. 상품 도메인 개발

💡 본 게시글은 김영한님의 인프런(Inflearn) 강의 실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발에 대해 공부하고, 정리한 내용입니다.

1. 상품 도메인 개발

1) 구현 기능

  1. 상품 등록
  2. 상품 목록 조회
  3. 상품 수정

2) 구현 순서

  1. 상품 엔티티 개발 (비즈니스 로직 추가)
  2. 상품 리포지토리 개발
  3. 상품 서비스 개발
  4. 상품 기능 테스트

3) 상품 엔티티 개발 (비즈니스 로직 추가)

(1) 상품 엔티티 코드

package jpabook.jpashop.domain.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;
    }
}

(2) 예외 추가

package jpabook.jpashop.exception;

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);
    }
}

(3) 비즈니스 로직 분석

  • addStock(): 파라미터로 넘어온 수만큼 재고를 늘립니다. 이 메서드는 재고가 증가하거나 상품 주문을 취소해서 재고를 다시 늘려야 할 때 사용됩니다.
  • removeStock(): 파라미터로 넘어온 수만큼 재고를 줄입니다. 만약 재고가 부족하면 NotEnoughStockException 예외가 발생합니다. 주로 상품을 주문할 때 사용됩니다.

4) 상품 리포지토리 개발

(1) 상품 리포지토리 코드

package jpabook.jpashop.repository;

import jpabook.jpashop.domain.item.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);
        }
    }

    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();
    }
}

(2) 기능 설명

  • save(): 상품 저장 메서드입니다.
    • 신규 등록: id가 없으면 새로운 엔티티로 보고 persist()를 호출합니다.
    • 수정: id가 있으면 이미 DB에 저장된 엔티티로 보고 merge()를 호출하여 수정합니다.
  • findOne(): 특정 상품을 ID로 조회합니다.
  • findAll(): 모든 상품 목록을 조회합니다.

5) 상품 서비스 개발

(1) 상품 서비스 코드

package jpabook.jpashop.service;

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);
    }
}

(2) 기능 설명

  • saveItem(): 상품 저장 메서드로, 상품 리포지토리의 save() 메서드를 호출하여 상품을 저장합니다.
  • findItems(): 모든 상품 목록을 조회하는 메서드입니다.
  • findOne(): 특정 상품을 ID로 조회하는 메서드입니다.

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


6. 총 정리

  1. 상품 엔티티 개발
    • Item 엔티티는 상품의 속성(이름, 가격, 재고 수량)을 정의하고, 재고를 관리하는 비즈니스 로직을 포함합니다. addStock() 메서드로 재고를 증가시키고, removeStock() 메서드로 재고를 감소시킵니다. 재고가 부족할 경우 NotEnoughStockException 예외가 발생하도록 합니다.
  2. 상품 리포지토리 개발
    • ItemRepository@RepositoryEntityManager를 사용하여 상품 데이터를 관리합니다. save() 메서드는 상품의 등록과 수정을 모두 처리하며, findOne()findAll() 메서드를 통해 상품을 조회할 수 있습니다. @RequiredArgsConstructor를 사용해 EntityManager를 주입받습니다.
  3. 상품 서비스 개발
    • ItemService@Service@Transactional을 사용하여 상품 비즈니스 로직을 처리하고, 트랜잭션을 관리합니다. saveItem(), findItems(), findOne() 메서드를 통해 리포지토리에 있는 상품을 저장하고 조회하는 기능을 제공합니다. 서비스 계층은 리포지토리에 단순히 위임하는 역할을 합니다.
  4. 상품 기능의 비즈니스 로직 테스트
    • Item 엔티티의 비즈니스 로직(재고 추가 및 감소)은 상품 도메인에서 중요한 부분이므로, 이를 테스트하여 올바르게 동작하는지 확인해야 합니다. 상품의 등록, 수정, 조회 기능이 제대로 동작하는지 검증하는 것이 중요합니다.
  5. 트랜잭션 관리 및 성능 최적화
    • @Transactional(readOnly = true)를 사용하여 읽기 전용 트랜잭션으로 설정하면, 플러시를 하지 않음으로써 약간의 성능 최적화를 할 수 있습니다. 상품 등록이나 수정과 같은 데이터 변경 작업에서는 읽기 전용 트랜잭션을 사용하지 않도록 주의해야 합니다.
  6. 예외 처리와 유효성 검증
    • NotEnoughStockException과 같은 예외를 통해 재고 부족 상황을 처리하며, 이러한 예외 처리가 비즈니스 로직의 중요한 부분임을 이해하고, 이를 적절히 핸들링해야 합니다.

'Spring > 웹 애플리케이션 개발' 카테고리의 다른 글

06. 주문 도메인 개발  (0) 2024.09.08
04. 회원 도메인 개발  (0) 2024.09.08
03. 애플리케이션 구현 준비  (2) 2024.09.08
02. 도메인 분석 설계  (2) 2024.09.08
01. 프로젝트 환경설정  (0) 2024.09.08