[리팩토링 보고서] API와 Repository 의존성 제거하기 (DTO-Repository 의존성 제거)
리팩토링 진행마다 계속 업데이트 됩니다.
* 버전 표기는 프로젝트 실제 버전이 아닌 의존성의 변화과정을 담기 위해 편의상 표기하였습니다.
* v1.0 은 배포된 버전 입니다. (PocketMark)
API 변경 시 DTO 만 수정해도 되도록 리팩토링 완료했습니다.
API -> DTO -> Repository 에서 API -> DTO 로의 리팩토링 여행기입니다.
> v1.0 바로가기
v1.0 의 장점
1.DTO->Repository 의존성이 제거되어 API 스펙변경에도 끄떡없다. (코드 재사용성 매우 우수)
2. Spring Data JPA가 제공하는Query Method를 마음껏 편리하게 사용할 수 있어 신속한 쿼리작성이 가능하다.
3. Projection 이기 때문에성능적으로도 우수하다.
4. Class-Based 는 개발자가 마음만 먹으면 내부에 비지니스 로직을 넣을 수 있지만 Interface-Based는 넣을 수 없게 강제하기 때문에 협업시코드안정성(불변성)에도 큰 기여를 할 수 있다.
* API 스펙변경으로 인해 엔티티에 새로운필드를 추가해야하는 상황은 설명하고자 하는 부분과 관련이 없음.
PocketMark v0.1
API 스펙변경으로 인해 엔티티에 새로운필드를 추가해야하는 상황은 제외한다.
특징
엔티티 객체에서 Dto로 변환이 가능했었음 (ex_Entity.toDto()) (v0.2에서 삭제)
Summary. 개판 5분전

Spring Data JPA Repository
// Query Method (반환형은 User 이지만 Entity.toDto()가 있으므로 수정할 필요가 없음)
List<User> findByName(String name);
// Native Query
@Query(value = "SELECT u.name, u.email FROM user u",
nativeQuery=true)
List<UserDto> findUserDto();
문제점
1. Entity-DTO 양방향 의존성
2. DB의 특수한 기능을 사용하는 것도 아닌데 쓸모없는 NativeQuery
API 스펙변경 시
Native Query
- SQL 문자열 수정필요
PocketMark v0.2
API 스펙변경으로 인해 엔티티에 새로운필드를 추가해야하는 상황은 제외한다.
특징
1. ClassBased-DTO-Proejction 적용으로 Entity-DTO 양방향 의존성 삭제
2. 단순조회에서 필요없어진 NativeQuery
Summary. 엔티티(도메인) 변경 없음

Spring Data JPA Repository & QueryDSL
// Class-Based Projection
List<UserDto> findAllUserDto(){
em.createQeury("select new xx.xx.xx.ClassBasedUserDTO(u.name, u.email) "
+ "from User u", User.class)
.getResultList();
}
//Query-DSL
public List<UserDto> findAllUserDto(){
return queryFactory
.select(new QUserDTO(qUser.name, qUser.email))
.from(qUser)
.fetch()
.stream().......collect(Collectors.toList());
}
문제점
DTO-Repository 의존성으로 인한 API-Repository 의존성 문제
API 스펙변경 시
JPQL & QueryDSL
- 수정필요 (ex_ x.name-> x.userName)
PocketMark v1.0
API 스펙변경으로 인해 엔티티에 새로운필드를 추가해야하는 상황은 제외한다.
Summary. DTO만 수정하면 끝

Spring Data JPA Repository
// Interface-Based DTO Projection
List<UserDtoNameAndItemsOnly> findByNameIn(Collection<String> nameList);
DTO Side
class UserDto{
public interface UserDtoNameOnly{
String getName();
}
public interface UserDtoNameAndItemsOnly{
String getName();
List<Item> getItems();
}
}
장점
1. DTO->Repository 의존성 이 제거되어 API 스펙변경에도 끄떡없다. (코드 재사용성 매우 우수)
2. Spring Data JPA가 제공하는 Query Method 를 마음껏 편리하게 사용할 수 있다.
3. Projection 이기 때문에 성능적으로도 우수하다.
4. Class-Based 는 개발자가 마음만 먹으면 내부에 비지니스 로직을 넣을 수 있지만 Interface-Based는 넣을 수 없게 강제하기 때문에 협업시 코드안정성(불변성) 에도 큰 기여를 할 수 있다.