Data Transfer Object, 데이터 전송 객체
개념
- 계층간 데이터 교환을 위해 사용하는 객체
- 데이터 교환을 위한
Java Beans
를 의미
- 데이터 교환을 위한
- 요청과 응답을 처리하기 위한 객체
Java Beans
- Java로 작성된 소프트웨어 컴포넌트를 지칭하는 단어로, 비즈니스 로직 부분을 담당하는 java프로그램 단위
- 데이터를 표현하는 것을 목적으로 하는 자바 클래스
특징
- 다른 로직을 갖고 있지 않는 순수한 데이터 객체
- 오직 getter/setter 메서드만 가짐
- 일반적으로 Request용 DTO, Response용 DTO로 구분지어 사용
불변객체 DTO
- DTO가 setter매서드를 가질 경우 해당 DTO는 데이터가 가변적
- setter매서드를 삭제하고, 생성자를 통해 속성값을 초기화하여 불변객체로 만들 수 있음
- 전달과정 중 데이터 불변성을 보장함
가변 객체 DTO
// 가변 객체 DTO
// 기본생성자로 생성 후 값을 유동적으로 변경
public class DtoEx {
private String name;
private int age;
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}
불변 객체 DTO
// 불변 객체 DTO
// 생성시 지정했던 값이 변하지 않고 getter() 메소드만 사용 가능
public class DtoEx {
private final String name;
private final int age;
public DtoEx(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
Data Access Object
개념
- DB에 접근하는 역할을 하는 객체로, 직접 DB에 접근해 데이터 삽입, 삭제, 조회등 CRUD 기능 수행
- 서비스 모델에 해당하는 부분과 DB를 연결하는 역할
- 데이터의 CRUD 작업을 시행하는 클래스
- 데이터에 대한 CRUD 기능을 전담하는 오브젝트
특징
-
DB접근을 위한 로직과 비즈니스 로직의 분리를 위함
- 효율적인 커넥션 관리
-
데이터베이스 독립성을 유지하고 재사용성 높임
-
데이터베이스 접근 로직 캡슐화
- 보안성: 비즈니스 로직 분리로 도메인 로직으로부터 DB와 관련한 메커니즘을 숨김
-
DAO의 형태
- DAO에 DB Connection이 설정되어있는지 여부로 DAO의 형태가 나뉨
MyBatis
- DB Connection정보를 root-context.xml파일에 저장
@Mapper
- MyBatis에서 사용되는 인터페이스로, SQL 매퍼 XML 파일과 연동되어 SQL 쿼리 실행
- 해당 인터페이스를 기반으로 DAO 구현체 생성
JPA
- applicaiton.yml(properties)파일에 설정해 사용
@Repository
- Spring Data JPA에서 DAO 역할을 하는 인터페이스나 클래스에 붙이는 어노테이션
- Spring이 자동으로 DAO 구현체 생성
Value Object, 값 객체
개념
- 값 그 자체를 표현하는 객체
- 도메인에서 한개 또는 그 이상의 속성들을 묶어서 특정 값을 나타내는 객체
특징
- 동등성
- 값 객체의 모든 속성이 같은
값
을 가질 시 동일한 객체로 판단 - 이때, equals(), hashCode() 오버라이딩을 했다는 전제 조건 필요
- 해당 속성들은
primitive 타입
- 값 객체의 모든 속성이 같은
- 불변성
- 수정자(setter)를 포함하지 않고, 생성자를 통해서 값 초기화
- 새로운 값 사용을 위해선 새로운 객체 생성
- 중간에 데이터를 조작할 수 없음
- Read Only
- 자가 유효성 검사(Self-Validation)
- 유효성 검사 진행 후 VO 생성
- VO 사용 시 유효성 검사가 보장되어있으므로 안전하게 사용 가능
- getter 이외의 로직 포함 가능
- 값 타입을 여러 엔티티에서 공유 시
Side effect
발생가능
사용 이유
primitive 타입이 도메인 객체를 모델링하기에 충분하지 않음
-
primitive 타입의 기능들을 객체가 전부 사용하지 않음
public class Ladder { private final int width; private final int height; public void Ladder(int width, int height) { this.width = width; this.height = height; } }
- 위 코드의 경우, '사다리의 높이가 음수가 되는 경우', '사다리 높이의 연산' 등 사다리에서 필요없는
int
기능들이 사용된다.
- 위 코드의 경우, '사다리의 높이가 음수가 되는 경우', '사다리 높이의 연산' 등 사다리에서 필요없는
-
한 곳이 아니라 여러 곳에서 사용될 때 중복 코드 발생
public class Rectangle { private final int width; private final int height; public void Ladder(int width, int height) { validateWidth(width); // 중복 코드 validateHeight(height); // 중복 코드 this.width = width; this.height = height; } private void validateWidth() { ... } private void validateHeight() { ... } }
- 다음과 같이
사다리
와 같은 형태를 가진사각형
에서 또한 유효성 검사코드가 중복되서 사용된다. - 이처럼 유효성 검사, 불변 체크등의 진행을
값
이 있는 모든 객체에서 진행해야하는 상황이 일어남
- 다음과 같이
VO 예시
public class ShapeProperty {
// 불변성 (Immutable)
private final int width;
private final int height;
public Shape(final int width, final int height) {
// 자가 유효성 검사 (Self-Validation)
validateWidth(width);
validateHeight(height);
this.width = width;
this.height = height;
}
// 동등성 (Equality)
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ShapeProperty that = (ShapeProperty) o;
return width == that.width && height == that.height;
}
@Override
public int hashCode() {
return Objects.hash(width, height);
}
}
// 원시 타입 사용
public class Ladder {
private final int width;
private final int height;
public void Ladder(int width, int height) {
validateWidth(width);
validateHeight(height);
this.width = width;
this.height = height;
}
}
// VO로 변경
public class Ladder {
private final ShapeProperty shapeProperty;
public void Ladder(final ShapeProperty shapeProperty) {
this.shapeProperty = shapeProperty;
}
}
- 사다리가
모양 속성
이란 네이밍을 가진 객체를 가질 수 있게 됨- 원시타입 너비, 높이를 묶어 ShapeProperty라는 값 객체를 가짐
- Ladder에서는
핵심 비즈니스 로직
만 가짐- 유효성 검사와 불변체크를 Ladder가 아닌, 값 객체 ShapeProperty에서 수행
- 향후 같은 형태의 Rectangle이 추가되어도 중복으로 유효성 검사, 불변 체크 불필요
- Rectangle이 너비, 높이를 사용하더라도 원시 타입이 아닌 ShapeProperty 사용
개념
- JPA와 같은 ORM프레임워크에서 사용되는 개념으로
@Entity
어노테이션을 붙여 엔티티를 나타냄 - 실제 DB의 테이블과 정확하게 매칭되는 속성들을 갖도록 디자인 된 클래스
특징
- 보틍 식별자를 가짐(Primary key)
- ID로 구분 되며 로직 포함 가능
- 비즈니스 로직을 엔티티로 모음으로 응집도가 높아짐
- 데이터베이스의 테이블과 직접 연관
- ORM 프레임워크에서 데이터베이스 테이블과 매핑되어 CRUD작업 수행
Entity와 DTO의 분리
- Entity는 요청/응답 값을 사용하는 클래스로 사용하면 안됨
- Entity는 DB와 매핑되어있는 핵심 클래스
- 이를 기준으로 테이블이 생성되고 스키마가 변경 됨
- Entity클래스를 요청/응답 값을 전달하는 클래스로 사용할 경우 뷰 변경시 마다 Entity클래스를 변경해야 함
- REST API 설계시 엔티티의 속성이 변화(추가, 삭제)될때마다 API명세가 바뀌게 됨
- API명세가 테이블에 의존하는 현상이 발생
- 요청/응답 값을 사용하는 클래스로 DTO의 사용
- 뷰의 변경에 따라 다른 클래스에 영향을 끼치지 않고 자유롭게 변경 가능
- 여러 테이블들을 Join한 결과값을 응답값으로 표현하기 편리함
-
DTO와 VO
DTO
는 데이터 전송을 위한 객체로 가변적일 수 있음VO
는 불변객체로 값 자체를 표현
-
DAO와 Repository
DAO
는 데이터 접근 로직을 캡슐화Repository
는 좀 더 도메인 중심의 접근 방식을 사용해 데이터 계층 관리
-
Entity와 DTO
Entity
는 데이터베이스 테이블과 직접 매핑되는 객체로 비즈니스 로직 포함 할 수 있음DTO
는 단순히 데이터 전송을 위한 객체
DTO
: 여러 데이터 소스를 결합해 한 번에 전송할 수 있도록 함DAO
: 데이터소스와 상호작용하는 로직 캡슐화VO
: 두 VO객체가 동일한 값을 가지면 동일하다고 간주Repository
: 데이터 계층을 추상화해 데이터 접근로직을 캡슐화하고, 비즈니스 도메인과 데이터 매핑 관리
DTO란 무엇이고 왜 사용해야 할까?
[10분 테코톡] 📍인비의 DTO vs VO
VO(Value Object)는 무엇일까? 왜 사용할까?