728x90
👜 DTO
- Data Transfer Object / dto package
- 계층 간 데이터 교환을 위해 사용하는 객체
- 로직을 갖지 않는 순수한 데이터 객체이다
- 데이터를 전달하기 위해 사용하는 객체이기 때문에 오직 getter/setter 메서드만을 가지며 다른 로직은 갖지 않는다
- 데이터를 담아서 전달하는 바구니라고 생각하면 된다
- 주로 데이터베이스와 통신하거나 소켓에서 받은 메시지의 구문을 분석할 때 유용하게 사용된다
DTO를 사용하는 이유
- Core 클래스인 Entity 클래스 보호
- View Layer와 DB Layer의 역할 분리
- 다양한 비즈니스 로직과 요구사항에 대한 유연한 대응
- Controller와 Service 사이의 강한 의존 방지
- 트랜잭션으로 처리되어야 하는 DTO 항목이 요청으로 들어온 결과값과 동일하지 않을 수 있다
- 위의 그림과 같이 Controller가 받은 Web DTO와 Service가 받을 DTO가 다른 경우가 존재한다
- 이러한 경우에 Service 레이어가 Controller 레이어 DTO에 의존하고 있으면 문제가 발생할 수 있기 떄문에 Service 레이어에 맞는 DTO가 필요하다
DTO를 불변 객체로?
- setter를 없애고 final 키워드를 통해 오직 데이터를 가져올 수 만 있도록 수정하면 된다
🧧 VO
- 값 표현용
- Value Object
- 값 그 자체를 표현하는 객체
VO 특징
- VO는 값 자체를 표현하기 때문에 불변객체여야 한다
- 따라서 위처럼 setter 성격의 메서드는 포함해서는 안되고, 생성자를 통해서만 값을 초기화해야 한다
- getter, setter 메서드 외의 로직을 포함할 수 없는 DTO와 달리, VO는 이외의 로직을 포함할 수 있다
💡 DTO vs VO
- DTO는 인스턴스 개념이고, VO는 리터럴 값 개념이다
- VO는 값들에 대해 read-only를 보장해줘야 존재의 신뢰성이 확보되지만, DTO의 경우는 단지 데이터를 담는 그릇의 역할일 뿐 값은 그저 전달되어야 할 대상일 뿐이다
- 값 자체에 의미가 있는 VO와 전달될 데이터를 보존해야 하는 DTO의 특성상 개념이 다르다
🎛 Entity
- domain package
- 실제 DB와 매칭될 클래스로 가장 core한 클래스라고도 부른다
- 최대한 외부에서 Entity 클래스의 getter 메소드를 사용하지 않도록 해당 클래스 안에서 필요한 로직 메서드를 구현한다
- 단, Domain Logic만 가지고 있어야 하고 Presentation Logic을 가지고 있어서는 안된다
- Entity는 DB의 테이블과 일대일로 대응되기 때문에 테이블이 갖지 않는 컬럼을 필드로 가져서는 안된다
- 또한 Entity 클래스는 다른 클래스를 상속받거나 인터페이스의 구현체여서는 안된다
💡 DTO Class vs Entity Class
- Entity 클래스는 데이터베이스와 매핑되어 있는 핵심 클래스이기 때문에 요청이나 응답 값을 전달하는 클래스로 사용하면 안된다
- Entity 클래스를 기준으로 테이블이 생성되고 스키마가 변경된다
- 만약 Entity 클래스를 데이터를 전달하는 클래스로 사용할 경우, View의 변경사항이 생길 때마다 맞춰서 변경해야 한다
- 또한 수많은 서비스 클래스나 비즈니스 로직들이 Entity 기반으로 동작하기 때문에 Entity는 변경되면 안된다
- 따라서 요청이나 응답 값을 전달하는 클래스로는 반드시 View에 변경에 따라 다른 클래스들에게 영향을 끼치지 않고 자유롭게 변경할 수 있는 DTO를 사용해야 한다
- 또한 응답값으로 여러 테이블들을 조인한 결과값을 전달해야 할 경우가 빈번하기 때문에 Entity 클래스 만으로는 응답값을 표현하기 어렵다
- 따라서 Entity 클래스와 뷰의 결과값을 전달해주는 DTO는 꼭 분리해서 사용해야 한다!
🧱 DAO
// JPA인 경우
public interface UserRepository extends JpaRepository<User, Long> {
// 구현 로직 작성
// 기본 CRUD method는 JPA가 제공한다
}
- Data Access Object / repository package
- 데이터베이스의 data에 접근하기 위한 객체로 Persistence Layer이다
- Database에 접근하기 위한 로직과 비즈니스 로직을 분리하기 위해 사용한다
- 쉽게 생각하면 Service와 DB를 연결하는 고리의 역할을 한다
- DAO를 사용하는 이유는 효율적인 커넥션 관리와 보안성 때문이다
- DB에 대한 접근을 DAO가 담당하도록 하여 데이터베이스 엑세스를 DAO에서만 하게 되면, 다수의 원격 호출을 통하 오버헤드를 VO나 DTO를 통해 줄일 수 있고 다수의 DB 호출문제를 해결할 수 있다
- ex) 클라이언트가 데이터를 입력하여 DTO를 통해 전송한다 -> DTO를 받은 서버가 DAO를 통해 DB에 데이터를 넣는다
💡 Persistence Layer : DB에 data를 CRUD하는 계층
전체 구조로 보는 사용 위치
- 우선 패키지 구조로 보면 위의 객체들은 위와 같은 위치에 놓으면 된다
- dao - repository package
- entity - domain package
- vo - domain package
- dto - dto package
DTO의 사용 범위
- DB Layer에는 Entity, View Layer에는 DTO를 사용하여 역할을 분리한다
- DB와 매핑되고 데이터의 CRUD에 대해 데이터를 관리하고 보관하는 Persistence Layer까지는 Entity를 사용하고, Service 영역에서부터 클라이언트 영역까지는 DTO를 사용한다
- Service -> Controller 에서 Entity와 DTO 중 어떤 것을 사용하는 것이 더 좋을 지에 관해서는 많은 의견이 존재한다
- 이는 시스템의 규모와 아키텍처 마다 어디까지 DTO와 Entity를 사용하는 것이 효율적인지 다르기 때문에 상황에 맞게 사용하는 것이 중요하다고 생각된다!
Entity <-> DTO 간 객체 변환
- Entity와 DTO 간 객체 변환은 ModelMapper 라이브러리를 통해 변환이 가능하다
- ModelMapper를 사용하여 변환하고, Entity에 없는 필드들은 직접 값을 넣어주는 방식으로 변환한다
사용 예시
/* ModelMapper를 통한 변환 */
ModelMapper modelMapper = new ModelMapper();
// 매핑 전략 설정
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
// 변환
final UserBasicInfoDto userBasicInfoDto = modelMapper.map(userEntity, UserBasicInfoDto.class);
실제 변환 작업은 DTO 내의 static 메서드를 생성하여 변환한다
- Entity -> DTO = of()
- DTO -> Entitty = toEntity()
/*DTO 변환 Example*/
public class SampleDto {
private Long id;
private String title;
private String interviewee;
private String company;
...
// Entity -> DTO
public static SampleDto of(Sample sample) {
return modelMapper.map(sample, SampleDto.class);
}
// Entity -> DTO (Page의 경우)
public static Page<SampleDto> of(Page<Sample> sourcePage) {
return sourcePage.map(SampleDto::of);
}
}
DTO로 변환하는 내용은 DTO 내부의 of() 메서드에 모두 넣어두고 실제 사용은 아래와 같이 한다
Sample sample = sampleRepository.findById(id)
.orElseThrow(() -> new NotExistException("Sample", id));
// DTO로 변환하여 리턴
return SampleDto.of(sample);
Entity로의 변환은 static 메서드가 아닌 일반 메서드로 작성하여, 객체 자신의 값을 가지고 Entity를 생성하도록 한다
주로 요청 DTO에서는 toEntity() 메서드 (DTO->Entity) 를, 응답 메서드에는 of() 메서드 (Entity->DTO)를 갖게 된다
// DTO -> Entity
public Sample toEntity() {
Sample.builder()
.company(this.company)
.description(this.description)
...
.build();
}
public void saveSample(SampleCreateRequest createRequest, Long managerId) {
// DTO -> Entity
Sample sample = createRequest.toEntity();
sampleRepository.save(sample);
}
참고)
https://www.youtube.com/watch?v=z5fUkck_RZM
https://melonicedlatte.com/2021/07/24/231500.html
https://studyandwrite.tistory.com/402
https://dbbymoon.tistory.com/4
https://gmlwjd9405.github.io/2018/12/25/difference-dao-dto-entity.html
https://velog.io/@aidenshin/DTO%EC%97%90-%EA%B4%80%ED%95%9C-%EA%B3%A0%EC%B0%B0
https://tecoble.techcourse.co.kr/post/2021-04-25-dto-layer-scope/
728x90
'야미스터디 > Backend' 카테고리의 다른 글
개발자로 성장하는 꿀팁 5가지🍯 (3) | 2022.11.07 |
---|---|
[Etc] Docker 📌 (0) | 2022.10.11 |
[Etc] Java와 C언어의 차이 📌 (1) | 2022.09.18 |
[Etc] 좋은 개발자란 무엇일까요? 📌 ~ing (0) | 2022.08.28 |
[보안] 암호화, 해시함수 (0) | 2021.12.23 |
댓글