DAO? DTO? VO? (feat.JPA 공식문서 같이 읽기)

도준혁·2022년 4월 25일
0
post-thumbnail

자바 스프링 계열 웹개발을 하다보면 필연적으로 보게되는 단어이다. 과연 이 셋이 뭐길래 항상 등장하는 것일까? 이를 논하기 위해서는 Database(이하 DB) 와 Framework(이하 FW) 의 존재 목적과 패러다임부터 논해야 할 것이다. 이는 다른 포스팅에서 좀 더 깊게 다루도록 하고 오늘은 Mysql(DB) 와 Spring(FW) 의 차이를 느꼈다고 가정하고 이 둘 사이의 컨버팅을 더 자유롭게 해주는 DAO / DTO / VO 의 개념에 대해서 이야기하고자 한다.

DAO / DTO / VO 의 공통점

많은 글을 읽었지만 이 셋의 차이에 대해서는 설명을 하는 반면 셋의 공통점에 대해서는 이야기하지 않았다. 내가 생각하는 이 세 개념의 공통점은 DB와 FW의 차이점을 보완해주는 컨버터라고 느껴졌다. DB는 정보를 저장하기 위해 존재한다. FW는 개발의 생산성을 증진시키기 위해 많은 내장 기능을 내재한다. 두 개념의 목적 자체가 다르기 때문에 저장되는 데이터의 형태가 다르다.

Spring data JPA 공식문서의 기본 구조

https://spring.io/guides/gs/accessing-data-jpa

  1. Customer라는 @Entity를 정의한다. 이는 DB를 직접 이루는 테이블 구조에 대한 정보이며 생성자를 통해 정의를 해주어야 하며 엔티티 내용을 지정하고 접근할 수 있는 getter와 setter를 정의할 수 있다. 보통 @getter, @setter를 이용하여 간단하게 구성해주는 편이다.

#공식문서 본문

package com.example.accessingdatajpa;

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

@Entity
public class Customer {

  @Id
  @GeneratedValue(strategy=GenerationType.AUTO)
  private Long id;
  private String firstName;
  private String lastName;

  protected Customer() {}

  public Customer(String firstName, String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  @Override
  public String toString() {
    return String.format(
        "Customer[id=%d, firstName='%s', lastName='%s']",
        id, firstName, lastName);
  }

  public Long getId() {
    return id;
  }

  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }
}
  1. CrudRepository를 확장하는 CustomerRepository 인터페이스를 생성한다. CrudRepository는 JPA에서 지원하는 인터페이스이며 ORM작동의 핵심이다. 기본적인 CRUD를 간단하게 등록할 수 있으며 여러가지 필드로 검색할 수 있는 메서드를 등록할 수 있다.

#공식문서 본문

package com.example.accessingdatajpa;

import java.util.List;

import org.springframework.data.repository.CrudRepository;

public interface CustomerRepository extends CrudRepository<Customer, Long> {

  List<Customer> findByLastName(String lastName);

  Customer findById(long id);
}
  1. Spring 프레임워크 안에서 사용하고 싶은 곳에서 데이터를 호출하고 가공한다.

#공식문서 본문

package com.example.accessingdatajpa;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class AccessingDataJpaApplication {

  private static final Logger log = LoggerFactory.getLogger(AccessingDataJpaApplication.class);

  public static void main(String[] args) {
    SpringApplication.run(AccessingDataJpaApplication.class);
  }

  @Bean
  public CommandLineRunner demo(CustomerRepository repository) {
    return (args) -> {
      // save a few customers
      repository.save(new Customer("Jack", "Bauer"));
      repository.save(new Customer("Chloe", "O'Brian"));
      repository.save(new Customer("Kim", "Bauer"));
      repository.save(new Customer("David", "Palmer"));
      repository.save(new Customer("Michelle", "Dessler"));

      // fetch all customers
      log.info("Customers found with findAll():");
      log.info("-------------------------------");
      for (Customer customer : repository.findAll()) {
        log.info(customer.toString());
      }
      log.info("");

      // fetch an individual customer by ID
      Customer customer = repository.findById(1L);
      log.info("Customer found with findById(1L):");
      log.info("--------------------------------");
      log.info(customer.toString());
      log.info("");

      // fetch customers by last name
      log.info("Customer found with findByLastName('Bauer'):");
      log.info("--------------------------------------------");
      repository.findByLastName("Bauer").forEach(bauer -> {
        log.info(bauer.toString());
      });
      // for (Customer bauer : repository.findByLastName("Bauer")) {
      //  log.info(bauer.toString());
      // }
      log.info("");
    };
  }

}

DAO?

Data Access Object의 약자이다. 말그대로 FW에서 DB에 쉽게 접근하기 위해 만들어둔 객체이다. 위 예제에서는 Customer 2단계에 나오는 CrudRepository를 확장하는 CustomerRepository를 의미한다. 개인적으로는 데이터 접근에서 가장 핵심적인 부분이라고 생각하지만 그것을 대신해주는 것이 ORM이기 때문에 보통 ORM에서 모두 제공한다. 따라서 우리는 손쉽게 선배님들이 만든 공식문서를 보며 그대로 따라하면 된다.

DTO?

Data Transfer Object의 약자이다. DAO가 데이터를 직접적으로 DB에 넣어주는 역할이었다면, DTO는 좀더 프레임워크에서 손쉽게 쓸 수 있는 형태로 만들어놓은 데이터 원형이라고 이해했다. 위 예제에서는 1단계에서 Customer의 테이블 모습을 꾸며주는 단계이며 이것을 자바에서 바로 접근하기 위해 @getter, @setter를 활용해준다.

VO?

Value Object의 약자라고 한다. 정확하게는 이해하지 못했지만 DTO에서 @Setter 기능만 빠진, get메서드로 값을 불러만 올 수 있는 데이터 형태(Read-Only)라고 이해했다. DB에 무언가 실험을 한다거나 중요한 데이터를 다룰때 사용하면 좋을 것 같다.

3줄 요약

어쩌다보니 'JPA 공식문서 같이 읽기' 코너가 되었는데 요약하자면 다음과 같다.

  1. DAO = Repository
  2. DTO = Table
  3. VO = DTO - @Setter(READ ONLY)
profile
ML Ops 와 백엔드를 개발하고 있는 도준혁입니다

0개의 댓글