2023-02-02 05:17:05
@Repository
@RequiredArgsConstructor
public class UserServiceRepository {
private final JPAQueryFactory queryFactory;
public Long idCheck(String genieId){
return queryFactory
.select(account.genieId.count())
.from(account)
.where(account.genieId.eq(genieId))
.fetchOne();
}
public Account loginOk(Account accountEntity) {
return queryFactory
.selectFrom(account)
.where(
account.genieId.eq(accountEntity.getGenieId()),
account.withdrawal.eq(1)
)
.fetchOne();
}
public void delDelivery(int addressNum) {
queryFactory
.delete(address)
.where(address.addressNum.eq(addressNum))
.execute();
}
}
코드를 보면 알겠지만(며칠 지난 뒤 보니 이 부분도 SpringDATA JPA로 바꿀 수 있지 않을까 싶다)
기본적으로 반환값이 Entity값이다.
문제 상황: 모든 계층에서 전부 entity로 값을 주고 받았었다.
하지만 그러면 DTO, VO가 있을 필요가 없지 않은가?
그렇게 여러 자료를 찾아보았다.
결론 : 서비스 계층에서 DTO와 Entity를 변환해주기로 했다.
그렇게 ModelMapper를 통해 DTO로 변환을 하였고, 이를 가능하게 해주는 코드는 아래와 같다.
package com.genie.myapp.dto;
import com.genie.myapp.Config.CustomerModelMapper;
import com.genie.myapp.entity.Address;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.modelmapper.ModelMapper;
import org.modelmapper.PropertyMap;
import java.util.List;
import java.util.stream.Collectors;
import static org.modelmapper.convention.MatchingStrategies.*;
@Data
@NoArgsConstructor
public class AddressDTO {
private int addressNum;
private String genieId; // 이부분이 중요
private String userName;
private String userTel;
private String userPhoneNum1;
private String userPhoneNum2;
private String userPhoneNum3;
private String zipCode;
private String addr;
private String detailAddr;
public String getUserTel() {
return userPhoneNum1 + "-"+userPhoneNum2+"-"+userPhoneNum3;
}
public void setUserTel(String userTel) {
this.userTel = userTel;
String[] telSplit = userTel.split("-");
userPhoneNum1 = telSplit[0];
userPhoneNum2 = telSplit[1];
userPhoneNum3 = telSplit[2];
}
// Entity -> DTO (정적 팩토리 메서드)
public static AddressDTO convertEntityToDTO(Address address){
ModelMapper modelMapper = new CustomerModelMapper();
// 매핑 전략 설정
modelMapper.getConfiguration().setMatchingStrategy(STRICT);
return modelMapper.map(address,AddressDTO.class);
}
// DTO -> Entyty
public static Address convertDTOtoEntity(AddressDTO addressDTO) {
ModelMapper modelMapper = new CustomerModelMapper();
// 매핑 전략 설정
modelMapper.getConfiguration().setMatchingStrategy(STRICT);
//외래키 해결
modelMapper.addMappings(new PropertyMap<AddressDTO, Address>() {
@Override
protected void configure() {
map().getGenieId().setGenieId(source.getGenieId());
}
});
return modelMapper.map(addressDTO, Address.class);
}
// Entity -> DTO (List의 경우)
public static List<AddressDTO> convertEntityToDTO(List<Address> addressList) {
return addressList.stream().map(AddressDTO::convertEntityToDTO).collect(Collectors.toList());
}
}
추가 문제 발생
//이 부분이 중요 <- 사실 이 부분과 밑에 ModelMapper에서 며칠 밤을 새면서 오류를 못찾고 고생을 했었다.
오류의 문제를 먼저 말하자면 주소를 저장 할 때, 위에 DTO에서 정보를 받으면 genieId필드에서
Null값이 들어가는 오류가 발생했었고 처음에는 DTO에서 private String genieId가 아닌,
private User genieId로 그냥 작성을 했었다.
이때는 그냥 Null값이 아닌 정상적인 아이디 값이 들어가서 넘어갔었다.
해결과정
public static ~ 이 부분으로 구성된 코드는 modelmapper라이브러리를 통해 DTO <->Entity를 구성하는 코드고 제일 밑의 코드는 다들 알고 있듯이 한 계정에 여러개의 주소정보가 있어 리스트로 파일을 받아와야하는 경우가 있다.
이런 경우 람다를 이용해서 정보를 가져오도록 했고 중간에 protected ~ 이부분이 정말 고생해서 만들어낸
코드다. DTO도 하나의 정보를 받아가는 곳일 뿐인데 저렇게 컴포지션으로 구성해버리면
더이상 DTO가 아닌 것 같다 생각하게 됐고 해결방안을 찾기로 결정했다.
처음에는 저게 연관관계 메서드로 구성을 해야하는 즉, 복합키 문제인줄 알고 있었다.
사실 이건 엔티티 간의 문제가 아니라 DTO로 변환하는 과정에서 생긴 문제이기에
복합키 문제가 아니었는데 착각하고 있었던 것이었다.
그렇게 며칠 밤을 고생하다 결국 찾다보니 ModelMapper 참조문서를 발견하게 되었고(외래키 해결 주석 참조)
이를 통해 DTO와 Entity간의 문제를 해결하였다.
//객체를 지속적으로 생성해야할 때, 팩토리 메소드로 해결한 경우
public static UserDTO createUserDTO(String genieId) {
UserDTO userDTO = new UserDTO();
userDTO.setGenieId(genieId);
return userDTO;
}
그리고 계속해서 엔티티와 DTO를 변환하던 도중 생각보다 객체를 생성해야하는 일이 많아졌다.
그러다 보니 객체를 계속 생성하는 것보다 하나를 정해놓고 쓰는게 좋지 않을까 싶어서 찾던 도중
정적팩토리 메서드를 알게 되었고 이를 바탕으로 빈번하게 객체생성이 발생할만한 DTO에는
전부 위와같이 정적 팩토리 메서드를 생성해놓았다.
참고자료
https://velog.io/@aidenshin/DTO%EC%97%90-%EA%B4%80%ED%95%9C-%EA%B3%A0%EC%B0%B0
[DTO에 관한 생각
과거 회사에서 DTO를 사용하지 않고 Entity로 통신을 주고 받는 경우가 있었다.. 결국 대참사가 벌어졌고 DTO를 도입하게 되었던적이 있다.대부분의 Entity 클래스들은 대부분 DB 테이블 스키마와 1:1
velog.io](https://velog.io/@aidenshin/DTO%EC%97%90-%EA%B4%80%ED%95%9C-%EA%B3%A0%EC%B0%B0)
https://dbbymoon.tistory.com/4
[Entity to DTO, DTO to Entity 그리고 ModelMapper
Entity 클래스란 JPA에서 실제 데이터베이스의 테이블과 매칭되는 클래스입니다. JPA를 사용하면서 Entity 클래스를 작성하였고, 프로젝트 초기에는 Entity로 Repository 뿐만 아니라 Service, Controller 영역
dbbymoon.tistory.com](https://dbbymoon.tistory.com/4)
https://modelmapper.org/user-manual/property-mapping
[ModelMapper - Property Mapping
Property Mapping For most object models, ModelMapper does a good job of intelligently mapping source and destination properties. But for certain models where property and class names are very dissimilar, a PropertyMap can be created to define explicit mapp
modelmapper.org](https://modelmapper.org/user-manual/property-mapping)