본문 바로가기

Development/Spring

[Spring] DAO (Data Access Object), DTO (Data Transfer Object)

DAO(Data Access Object)

  • 데이터베이스에 접근하기 위한 로직을 관리하기 위한 객체
  • 비즈니스 로직의 동작 과정에서 데이터를 조작하는 기능은 DAO 객체가 수행함
  • 단, Spring Data JPA에서 DAO의 개념은 Repository(레포지토리)가 대체
  • 규모가 작은 서비스에서는 DAO를 별도로 설계하지 않고 바로 서비스 레이어에서 데이터베이스에 접근해서 구현하기도 함
  • 하지만 DAO를 서비스 레이어와 리포지토리 중간 계층을 구성하여 사용하면, 비즈니스 로직을 개발할 때 유지보수 측면에서 용이한 경우가 많음

서비스와 비즈니스 레이어

  • 객체지향적인 설계에서는 서비스와 비즈니스 레이어를 분리해서 서비스 레이어에서는 서비스 로직을, 비즈니스 레이어에서는 비즈니스 로직을 수행해야 한다는 의견이 많다.
  • 서비스 레이어는 주로 사용자 인터페이스(UI)나 컨트롤러와 같은 프레젠테이션 계층에서 받은 요청을 처리하는 역할을 하며,
    트랜잭션 관리, 보안 및 권한 확인, 로깅, 예외 처리 등의 '서비스 로직'을 수행한다.
  • 서비스 레이어는 여러 비즈니스 로직을 조합하고 순서를 제어하여 하나의 비즈니스 프로세스를 완성하기도 한다.
  • 비즈니스 레이어는 애플리케이션의 핵심 비즈니스 로직을 담당한다.
  • 비즈니스 레이어에서는 비즈니스 규칙을 적용하고, 데이터를 처리하며, 계산을 수행한다.
  • 비즈니스 레이어는 도메인 모델을 표현하고, 데이터 액세스 레이어와 통신하여 데이터를 저장하거나 가져온다.

DAO 구현

  • DAO 클래스는 일반적으로 '인터페이스 - 구현체' 구성으로 생성함
  • DAO 클래스는 의존성 결합을 낮추기 위한 디자인 패턴으로,서비스 레이어에 DAO 객체를 주입받을 때 인터페이스를 선언하는 방식으로 구성할 수 있음
  • 아래는 Product라는 엔티티의 대한 DAO의 인터페이스와 구현체 클래스 코드이다.

ProductDAO

public interface ProductDAO {

    Product insertProduct(Product product);

    Product selectProduct(Long number);

    Product updateProductName(Long number, String name) throws Exception;

    void deleteProduct(Long number) throws Exception;

}

ProductDAOImpl

@Component
public class ProductDAOImpl implements ProductDAO {

    private ProductRepository productRepository;

    @Autowired
    public ProductDAOImpl(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }

    @Override
    public Product insertProduct(Product product) {
        Product savedProduct = productRepository.save(product);

        return savedProduct;
    }

    @Override
    public Product selectProduct(Long number) {
        Product selectedProduct = productRepository.getById(number);

        return selectedProduct;
    }

    @Override
    public Product updateProductName(Long number, String name) throws Exception {
        Optional<Product> selectedProduct = productRepository.findById(number);

        Product updatedProduct;
        if (selectedProduct.isPresent()) {
            Product product = selectedProduct.get();

            product.setName(name);
            product.setUpdatedAt(LocalDateTime.now());

            updatedProduct = productRepository.save(product);
        } else {
            throw new Exception();
        }

        return updatedProduct;
    }

    @Override
    public void deleteProduct(Long number) throws Exception {
        Optional<Product> selectedProduct = productRepository.findById(number);

        if (selectedProduct.isPresent()) {
            Product product = selectedProduct.get();

            productRepository.delete(product);
        } else {
            throw new Exception();
        }
    }
}

@Service, @Component

  • 두 어노테이션은 스프링 빈을 자동으로 등록하는 `@ComponentScan`의 대상이 되며, 이를 통해 스프링은 이들을 자동으로 빈으로 등록하고 종속성 주입을 수행한다.
  • @Component: 일반적인 컴포넌트를 등록하는 데 사용된다. 예를 들어, DAO, 서비스, 유틸리티 클래스 등을 등록할 때 사용
  • @Component로 등록된 빈은 자동으로 주입되지 않는다. 빈을 주입하려면 @Autowired 어노테이션을 함께 사용해야 한다.
  • @Service: 비즈니스 로직을 담당하는 서비스 클래스를 등록하는 데 사용됩니다. @Component보다 더 구체적인 어노테이션이다.
  • @Service로 등록된 빈은 자동으로 주입된다. @Autowired 어노테이션 없이도 다른 빈에 주입될 수 있다.
  • @Service는 @Component보다 더 명확하고, 자동 주입 기능도 제공하기 때문에 코드를 간소화할 수 있다.

DTO(Data Transfer Object)

  • 일반적으로 데이터베이스에 접근하는 계층에서만 Entity를 사용하고, 그 외 다른 계층으로 데이터를 전달할 때는 DTO 객체를 사용함
  • 아래는 Product라는 엔티티의 DTO 객체 예시 코드이다.
package com.springboot.jpa.data.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Builder
public class ProductDto {

    private String name;

    private int price;

    private int stock;

}

스프링 부트 애플리케이션의 구조

참고 자료

  • 스프링 부트 핵심 가이드 "스프링 부트를 활용한 애플리케이션 개발 실무" , 장정우, 2022