ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Spring Boot - JPA를 사용한 데이터 조작
    Framework & Library 2022. 2. 4. 00:00

    JPA를 사용한 데이터 조작

    ✔️ Entity 생성

    @Entity
    @Getter
    @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    @Table(name = "product")
    public class ProductEntity {
    
        @Id
        private String productId;
    
        private String productName;
    
        private int productPrice;
    
        private int productStock;
    }

    ㆍ DB의 테이블과 직접적으로 매핑되는 클래스이다.

     

    애너테이션 설명
    @Entity 해당 애너테이션을 클래스에 붙이면 JPA가 해당 클래스를 관리하게 된다.
    @Builder 해당 애너테이션을 사용하게 되면 객체를 생성할 때 생성자를 통해서 객체를 생성하는 것이 아니라 Entity이름.builder( ).필드(값).필드(값).build( );를 통해 필드에 대한 값을 좀 더 명시적으로 지정할 수 있다.
    @Table 매핑할 테이블을 지정하기 위한 애너테이션이다.
    @Id Primary Key를 지정하기 위한 애너테이션이다.

    ㆍ JPA 사용 시 위와 같은 여러 가지 애너테이션을 사용하여 테이블, 필드, 필드 옵션 등을 설정할 수 있다.

     

    ✔️ DTO 생성

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @ToString
    @Builder
    public class ProductDTO {
    
        private String productId;
    
        private String productName;
    
        private int productPrice;
    
        private int productStock;
    }

    ㆍ Service나 Controller에서 DB에 접근할 때 사용하는 클래스이다.

    ㆍ 위에서 설명한 Entity와 다른 점은 Entity는 DB 테이블에 대한 정보를 가지고 있는 클래스이고, DTO는 해당 테이블에서 실제로 CRUD를 할 필드를 정의해둔 것이라고 보면 된다.

    ㆍ 이렇게 Entity와 DTO를 분리해서 사용하는 이유는 소스코드 작성 중 DB에 접근할 필드의 변경이 생겼을 경우 Entity를 변경하여 DB에 접근하면 DB 테이블의 내용이 변경되어 큰 문제를 일으킬 수 있기 때문이다. 따라서, 테이블에 대한 정보를 작성하는 Entity 클래스와 DB에 접근하는 필드에 대한 DTO 클래스를 분리해서 사용하는 것이다.

     

    ✔️ Repository 생성

    public interface ProductRepository extends JpaRepository<ProductEntity, String> {
    }

    ㆍ JPA를 사용하게 되면 JpaRepository 인터페이스를 상속받아 제네릭을 통해 관리하고 하는 클래스, Id의 타입을 <ProductEntity, String>와 같이 작성하면 자동으로 DB와 CRUD 연결을 할 수 있는 메서드를 사용할 수 있다.

     

    ✔️ DAO 생성

    public interface ProductDAO {
    
        ProductEntity saveProduct(ProductEntity productEntity);
    
        ProductEntity getProduct(String productId);
    }

     Service가 DB에 연결할 수 있게 도와주는 역할을 수행하는 DAO 인터페이스를 생성한다.

    ㆍ 해당 인터페이스는 상품을 저장하기 위한 saveProduct( ) 메서드와 상품을 조회하는 getProduct( ) 메서드가 정의되어 

     

    @Service
    public class ProductDAOImpl implements ProductDAO {
    
        ProductRepository productRepository;
    
        @Autowired
        public ProductDAOImpl(ProductRepository productRepository) {
            this.productRepository = productRepository;
        }
    
        @Override
        public ProductEntity saveProduct(ProductEntity productEntity) {
            productRepository.save(productEntity);
    
            return productEntity;
        }
    
        @Override
        public ProductEntity getProduct(String productId) {
            ProductEntity productEntity = productRepository.getById(productId);
    
            return productEntity;
        }
    }

    ㆍ ProductDAO 인터페이스의 구현체인 ProductDAOImpl 클래스이다.

    ㆍ ProductRepository 객체에 대한 의존성을 주입받고 해당 객체를 통해 DB에 접근하는 방식으로 동작한다.

     

    ✔️ Handler 생성

    public interface ProductDataHandler {
    
        ProductEntity saveProductEntity(String productId, String productName, int productPrice, int productStock);
    
        ProductEntity getProductEntity(String prodyctId);
    }

    ㆍ Handler는 필수적으로 구현하는 것이 아닌, 필요에 따라 구현을 하는 영역이다. 데이터를 핸들링할 필요가 있을 때 해당 영역을 구현한다.

    ㆍ Handler 영역 역시 인터페이스와 클래스로 분리되어 있다.

    ㆍ 해당 인터페이스는 상품을 저장하기 위한 saveProductEntity( ) 메서드와 상품을 조회하는 getProductEntity( ) 메서드가 정의되어 있다.

     

    @Service
    @Transactional
    public class ProductDataHandlerImpl implements ProductDataHandler {
    
        ProductDAO productDAO;
    
        @Autowired
        public ProductDataHandlerImpl(ProductDAO productDAO) {
            this.productDAO = productDAO;
        }
    
        @Override
        public ProductEntity saveProductEntity(String productId, String productName, int productPrice, int productStock) {
            ProductEntity productEntity = new ProductEntity(productId, productName, productPrice, productStock);
    
            return productDAO.saveProduct(productEntity);
        }
    
        @Override
        public ProductEntity getProductEntity(String productId) {
            return productDAO.getProduct(productId);
        }
    }

    ㆍ ProductDataHandler 인터페이스의 구현체인 ProductDataHandlerImpl 클래스이다.

    ㆍ ProductDAO 객체에 대한 의존성을 주입받고 해당 객체를 통해 DB에 접근하는 방식으로 동작한다.

     

    ✔️ Service 생성

    public interface ProductService {
    
        ProductDTO saveProduct(String productId, String productName, int productPrice, int productStock);
    
        ProductDTO getProduct(String productId);
    }

    ㆍ Service 영역 역시 인터페이스와 클래스로 분리되어 있다.

    ㆍ 해당 인터페이스는 상품을 저장하기 위한 saveProduct( ) 메서드와 상품을 조회하는 getProduct( ) 메서드가 정의되어 있다.

     

    @Service
    public class ProductServiceImpl implements ProductService {
    
        ProductDataHandler productDataHandler;
    
        @Autowired
        public ProductServiceImpl(ProductDataHandler productDataHandler) {
            this.productDataHandler = productDataHandler;
        }
    
        @Override
        public ProductDTO saveProduct(String productId, String productName, int productPrice, int productStock) {
    
            ProductEntity productEntity = productDataHandler.saveProductEntity(productId, productName, productPrice, productStock);
    
            ProductDTO productDTO = new ProductDTO(productEntity.getProductId(), productEntity.getProductName(), productEntity.getProductPrice(), productEntity.getProductStock());
    
            return productDTO;
        }
    
        @Override
        public ProductDTO getProduct(String productId) {
    
            ProductEntity productEntity = productDataHandler.getProductEntity(productId);
    
            ProductDTO productDTO = new ProductDTO(productEntity.getProductId(), productEntity.getProductName(), productEntity.getProductPrice(), productEntity.getProductStock());
    
            return productDTO;
        }
    }

    ㆍ ProductService 인터페이스의 구현체인 ProductServiceImpl 클래스이다.

    ㆍ 인터페이스에서 정의된 두 메서드의 실질적인 로직을 구현한 클래스이다.

    ㆍ ProductDataHandler 객체에 대한 의존성을 주입받고 해당 객체를 통해 데이터를 조작하는 방식으로 동작한다.

     

    ✔️ Controller 생성

    @RestController
    @RequestMapping("/api/v1/product-api")
    public class ProductController {
    
        private ProductService productService;
    
        @Autowired
        ProductController(ProductService productService) {
            this.productService = productService;
        }
    
        @GetMapping(value = "/product/{productId}")
        public ProductDTO getProduct(@PathVariable String productId) {
            return productService.getProduct(productId);
        }
    
        @PostMapping(value = "/product")
        public ProductDTO createProduct(@RequestBody ProductDTO productDTO) {
            String productId = productDTO.getProductId();
            String productName = productDTO.getProductName();
            int productPrice = productDTO.getProductPrice();
            int productStock = productDTO.getProductStock();
    
            return productService.saveProduct(productId, productName, productPrice, productStock);
        }
    
    }

    ㆍ 사용자로부터 요청을 받고 그에 대한 응답을 해주는 클래스이다.

    ㆍ ProductService 객체에 대한 의존성을 주입받게 된다.

    ㆍ Controller 클래스로 요청이 들어오게 되면, 각 요청에 매핑된 메서드들이 호출되고 해당 메서드 내의 Service 객체의 비즈니스 로직들이 동작하는 방식이다.


    테스트

    ✔️ 상품 저장 테스트

    ㆍ 컨트롤러와 매핑된 URL로 상품 정보를 http body에 담아 상품 저장 요청을 수행한다.

    ㆍ 요청에 대한 응답이 정확하게 출력되는 것을 확인할 수 있다.

     

    ㆍ DB 확인 결과 요청한 정보가 정확하게 저장된 것을 확인할 수 있다.

     

    ✔️ 상품 조회 테스트

    ㆍ 기존에 저장된 상품을 조회하기 위해 서버로 요청을 보낸다.

    ㆍ 요청한 상품정보가 정확하게 출력되는 것을 확인할 수 있다.


     

    GitHub - qlsdud0604/spring-boot-study

    Contribute to qlsdud0604/spring-boot-study development by creating an account on GitHub.

    github.com

     

    728x90

    댓글

Designed by Tistory.