JPA를 곁들인 DAO, DTO 그리고 Entity

June·2024년 11월 5일
0

애플리케이션 개발에서 데이터의 효율적인 관리와 계층 간의 명확한 역할 분리는 매우 중요합니다. 특히 DAO(Data Access Object), DTO(Data Transfer Object), 그리고 Entity는 이러한 역할 분리를 실현하는 핵심 요소입니다. 이 글에서는 이 세 가지 개념의 정의와 역할, 그리고 Spring JPA를 사용한 구현 방법에 대해 자세히 알아보겠습니다.


1. DAO (Data Access Object)

DAO는 데이터베이스와 같은 영구 저장소와의 상호작용을 담당하는 객체입니다. 데이터베이스 연산을 추상화하여 애플리케이션의 비즈니스 로직이 데이터베이스와 독립적으로 작동할 수 있도록 합니다.

  • 주요 역할:
    • 데이터베이스 접근 및 조작
    • CRUD 연산(Create, Read, Update, Delete) 제공
    • 데이터베이스 연결 및 트랜잭션 관리
    • 쿼리 실행 및 결과 반환
  • 장점:
    • 데이터 접근 로직의 재사용성 증가
    • 비즈니스 로직과 데이터 접근 로직의 분리로 유지보수성 향상
    • 데이터베이스 변경 시 비즈니스 로직에 최소한의 영향

DAO 구현 예시 (Spring JPA 사용)

Spring Data JPA를 사용하면 별도의 DAO 클래스를 작성하지 않고도 Repository 인터페이스를 통해 DAO 패턴을 구현할 수 있습니다.

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> {
    List<Customer> findByName(String name);
}

위의 CustomerRepository 인터페이스는 JpaRepository를 상속받아 기본적인 CRUD 메서드를 자동으로 제공합니다.


2. DTO (Data Transfer Object)

DTO는 계층 간 데이터 전송을 위한 객체로, 주로 데이터를 캡슐화하여 네트워크를 통해 전송하거나 다른 계층으로 전달할 때 사용됩니다. 비즈니스 로직이나 데이터베이스 연산을 포함하지 않고 순수하게 데이터 저장 용도로만 사용됩니다.

  • 주요 역할:
    • 데이터를 캡슐화하여 다른 계층으로 전송
    • 여러 데이터 필드를 하나의 객체로 그룹화
    • 데이터 전송 시 효율성 증대
  • 장점:
    • 네트워크 오버헤드 감소 (필요한 데이터만 전송)
    • 데이터 전송 시 일관성 유지
    • 클라이언트와 서버 간의 명확한 데이터 계약 정의

DTO 클래스 예시

public record CustomerDTO(
    Long id,
    String name,
    String address,
    String phoneNumber
) {
}

Record를 이용하여 DTO 생성 시 모든 필드 값이 final로 생성 이후 값 변경이 불가능하여 불변성을 보장할 수 있습니다.


3. Entity

Entity는 데이터베이스의 테이블과 매핑되는 클래스입니다. 데이터의 구조를 정의하며, 데이터베이스 테이블의 각 필드에 해당하는 속성을 갖습니다. Entity는 비즈니스 로직을 포함하지 않고 순수한 데이터 상태를 표현합니다.

  • 특징:
    • @Entity 어노테이션을 통해 데이터베이스 테이블과 매핑
    • 불변성을 유지하기 위해 Setter 메서드를 제공하지 않음
    • 필요한 경우 특정 필드를 변경하는 메서드를 별도로 제공

Entity 클래스 예시

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
@Table(name = "customers")
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
		
		@Column(length = 100)
    private String name;
    
    @Column
    private String address;
    
    @Column(name = "phone_number")
    private String phoneNumber;

    // 기본 생성자 (JPA 요구 사항)
    protected Customer() {}

    // 생성자
    public Customer(String name, String address, String phoneNumber) {
        this.name = name;
        this.address = address;
        this.phoneNumber = phoneNumber;
    }
    
    public Long getId() { return id; }
    
    public String getName() { return name; }
    
    public String getAddress() { return address; }
    
    public String getPhoneNumber() { return phoneNumber; }
}

Entity 클래스는 불변성을 유지하기 위해 setter 메서드를 제공하지 않으며, 데이터의 일관성을 보장합니다.


4. DAO, DTO, 그리고 Entity의 차이점과 관계

구분DAO (Repository)DTOEntity
역할데이터베이스와의 상호작용 로직 캡슐화계층 간 데이터 전송데이터 구조와 상태 표현
포함 내용CRUD 메서드 및 데이터 접근 로직전송할 데이터의 필드데이터베이스 테이블과 매핑되는 필드
특징JpaRepository 상속으로 기본 기능 제공불변 객체로 설계setter를 제공하지 않음
사용 위치서비스 계층에서 사용컨트롤러와 서비스 계층 간JPA를 통해 자동으로 관리

관계:

  • DAOEntity 객체를 활용하여 데이터베이스 연산을 수행합니다.
  • DTOEntity나 다른 계층에서 필요한 데이터만 추출하여 전송합니다.
  • Entity는 데이터베이스와의 매핑을 통해 애플리케이션에서 데이터의 상태를 관리합니다.

5. Spring JPA를 사용한 구현과 불변성 유지

Spring JPA를 사용하면 DAO와 Entity를 명확하게 분리하여 구현할 수 있습니다. 특히 Entity 객체는 불변성을 유지하기 위해 Setter 메서드를 제공하지 않고, 필요할 경우 업데이트 메서드를 별도로 정의합니다.

왜 Entity는 Setter 메서드를 제공하지 않을까?

  • 불변성 유지: 데이터의 예기치 않은 변경을 방지하여 일관성을 보장합니다.
  • 캡슐화 강화: 내부 구현을 숨기고 객체의 무결성을 유지합니다.
  • 동시성 이슈 방지: 멀티스레드 환경에서 데이터 변경으로 인한 문제를 최소화합니다.

서비스 계층에서의 사용 예시:

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class CustomerService {
    private final CustomerRepository customerRepository;

    public CustomerService(CustomerRepository customerRepository) {
        this.customerRepository = customerRepository;
    }

    // 고객 등록
    @Transactional
    public CustomerDTO createCustomer(CustomerDTO customerDTO) {
        Customer customer = new Customer(
            customerDTO.getName(),
            customerDTO.getAddress(),
            customerDTO.getPhoneNumber()
        );
        customer = customerRepository.save(customer);
        return new CustomerDTO(
            customer.getId(),
            customer.getName(),
            customer.getAddress(),
            customer.getPhoneNumber()
        );
    }

    // 고객 조회
    @Transactional(readOnly = true)
    public CustomerDTO getCustomerById(Long id) {
        Customer customer = customerRepository.findById(id)
            .orElseThrow(() -> new IllegalArgumentException("Customer not found"));
        return new CustomerDTO(
            customer.getId(),
            customer.getName(),
            customer.getAddress(),
            customer.getPhoneNumber()
        );
    }
}

서비스 계층에서는 Repository를 통해 데이터 접근을 수행하고, Entity와 DTO 간의 변환을 담당합니다.


6. 결론

DAO, DTO, 그리고 Entity는 애플리케이션 개발에서 각각의 역할이 명확하게 분리되어야 합니다.

  • DAO는 데이터 접근 로직을 캡슐화하여 비즈니스 로직과 데이터베이스의 의존성을 줄입니다.
  • DTO는 계층 간 데이터 전송을 효율적으로 수행하며, 데이터의 일관성과 무결성을 유지합니다.
  • Entity는 데이터베이스의 구조를 애플리케이션 내에서 표현하며, 불변성을 유지하여 데이터의 안정성을 보장합니다.

Spring JPA를 활용하면 이러한 패턴들을 더욱 효율적으로 구현할 수 있으며, 코드의 재사용성과 유지보수성을 높일 수 있습니다. 각 객체의 역할을 명확히 분리하여 설계하면 애플리케이션의 확장성과 안정성을 향상시킬 수 있습니다.

profile
😊

0개의 댓글