쿼리dsl을 구성할 때 아래와 같이 어느때와 다름없이 return 값을 Entity로 하게 되었다.
public List<Character> findAll(
OrderBy orderBy,
Pageable pageable) {
return jpaQueryFactory
.selectFrom(character)
.where(character.isDeleted.eq(false))
.orderBy(getOrderSpecifier(orderBy))
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch()
}
근데 뭔가 엔티티는 아무도 몰라야 하는데 이렇게 유출된다고? 라고 생각해서 찾아보았다.
그랬더니 문제점이
1) API 응답 크기 증가 → 성능 저하
-> Entity에는 실제로 클라이언트가 필요하지 않은 필드들이 있을 수 있음
-> 이들이 포함되면 네트워크 사용량 증가, 특히 리스트 응답일 경우 데이터 전송량이 많아져 느려질 수 있음2) 보안 및 정보 노출 이슈
예: isDeleted, createdAt, updatedAt, internalCode 같은 내부 관리용 필드가 클라이언트에 노출되면 안 되는 정보일 수 있음.
Entity에 이런 필드가 있을 경우, 실수로 클라이언트에 노출되면 보안상 취약점이 될 수 있음.3) N+1문제 발생가능성이 있음
-> 만약 엔티티 속에 OneToMany나 ManyToOne으로 관계가 매핑되어 있으면 하나를 조회할 때마다 거기에 포함된 데이터들까지 조회가 될 수밖에 없기에 불필요한 sql이 많이 실행된다는 문제가 있음
이런 다양한 문제가 발생될 가능성이 있다. 이러한 문제점을 해결해주기 위해 엔티티가 아닌 DTO로 수정하였다.
public List<CharacterResponse> findAll(
OrderBy orderBy,
Pageable pageable) {
return jpaQueryFactory
.selectFrom(character)
.where(character.isDeleted.eq(false))
.orderBy(getOrderSpecifier(orderBy))
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch()
.stream()
.map(c -> new CharacterResponse(c.getId(), c.getName(), c.getImageUrl(), c.getFavoriteCount()))
.toList();
}

잘 안보이지만 첫번째 실행이 entity고 두번째 실행이 dto이다. 시간부터 이미 차이가 난다. 응답 데이터의 양이 dto는 훨씬 양이 적기 때문이다.