[작업로그] Dto to Entity에 대한 고민

APRIL 20, 2021

요즘 FMS 프로젝트에 대한 리펙터링을 열심히 진행하고 있다.
그 중 가장 초점을 맞추고 있는 작업은 Request/Response 객체로 사용되고 있는 Entity를 Dto로 바꾸는 것이다.
그래서 Dto to Entity, Entity to Dto 의 편리성을 위해 Mapstruct 를 도입하였고, 큰 문제가 없이 수월한 작업을 예상했는데
역시나.. 모든 리펙토링은 많은 고민이 필요하구나를 다시한번 느끼게 된다.

오늘 고민한 내용은 다음과 같다.
연관관계(FK)가 많은 Entity를 생성할때 모든 FK 객체는 어떻게 채울 것인가?
DTO가 가지고 있는 각 매핑 ID로 모두 조회를 해야하나?


일단, 간단하게 프로젝트를 구성하여 테스트를 했다.

  • 생성요청 DTO 클래스에는 customerId (FK) 값이 존재한다.
  • DTO 클래스 안에 DTO to Entity 메소드가 존재한다.

    • Case1) customerId 로 repository에서 select 한 Customer 객체를 추입받는다
    • Case2) customerId 필드만 넣어 Customer 객체 생성을 한다.
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ReservationDto {

    @Getter
    @NoArgsConstructor
    public static class CreateReq {
        private Long customerId;
        private PassengerType passengerType;
        private String usages;
        private String luggage;

        //case1. customer id로 repository에서 select 한 값을 주입 받아 entity 에 주입
        public ReservationPassenger toEntity(Customer customer) {
            ReservationPassenger reservationPassenger = ReservationDtoMapper.INSTANCE.toReservationPassengerEntity(this);
            reservationPassenger.setCustomer(customer);
            return reservationPassenger;
        }

        //Case2. id로 customer id 객체 생성 후 entity 에 주입
        public ReservationPassenger toEntity() {
            Customer customer = new Customer(this.customerId);

            ReservationPassenger reservationPassenger = ReservationDtoMapper.INSTANCE.toReservationPassengerEntity(this);
            reservationPassenger.setCustomer(customer);
            return reservationPassenger;
        }

    }
}

case1. customer id로 repository에서 select 한 값을 주입 받아 entity에 주입

public ReservationPassenger toEntity(Customer customer) {
    ReservationPassenger reservationPassenger = ReservationDtoMapper.INSTANCE.toReservationPassengerEntity(this);
    reservationPassenger.setCustomer(customer);
    return reservationPassenger;
}

// 호출 메소드 로직
public void createReservation(ReservationDto.CreateReq req) {
    // customer id로 select
    Customer customer = customerRepository.findById(req.getCustomerId()).orElseThrow(IllegalArgumentException::new);
    ReservationPassenger reservationPassenger = req.toEntity(customer);
    ReservationPassenger saved = reservationRepository.save(reservationPassenger);
}

그래.. 이게 정석이겠지,
그런데 Entity의 연관관계가 많으면? 저렇게 객체를 가져와서 매핑해야 하나?
실질적으로는 ID만 필요로 할 뿐인데?

case2. customerId 필드만 넣어 Customer 객체 생성을 한 후 entity에 주입

public ReservationPassenger toEntity(Customer customer) {
    Customer customer = new Customer(this.customerId);

    ReservationPassenger reservationPassenger = ReservationDtoMapper.INSTANCE.toReservationPassengerEntity(this);
    reservationPassenger.setCustomer(customer);
    return reservationPassenger;
}

// 호출 메소드 로직
public void createReservation(ReservationDto.CreateReq req) {
    ReservationPassenger reservationPassenger = req.toEntity();
    ReservationPassenger saved = reservationRepository.save(reservationPassenger);
}

헉! DB에는 문제없이 잘 매핑이 된다!
그럼 존재하지 않는 ID를 넣으면 어떻게 되지?


오~ 역시
could not execute statement; SQL [n/a]; constraint 오류가 발생한다.
똑똑하구로..

org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: 
Referential integrity constraint violation: "FKT1K88T0M3HKQGVYFI365S5KV8: 
PUBLIC.RESERVATION_PASSENGER FOREIGN KEY(CUSTOMER_ID) REFERENCES PUBLIC.CUSTOMER(ID) (2)"; SQL statement:

물론 매핑 ID의 검증을 위해 첫번째 방법이 당연하다는건 알지만,
매핑 ID의 검증이 필요 없는 경우는 두번째 방법을 써도 되지 않을까?

좀 더 나은 방법이 없는지 더 고민 해봐야 겠다.


작업 기록 블로그