본문 바로가기
IT 공부/자바와 웹 애플리케이션

[Model Mapper] - DTO와 Entity 변환 시 내부에 객체를 매핑하고 싶을 때

by exdus3156 2024. 1. 29.

 

 

Model Mapper Deep 매핑 방법??

MVC 패턴에서 컨트롤러와 뷰, 모델 사이에는 DTO라는 데이터 박스를 사용해 데이터를 한꺼번에 전달한다. 이는 컨트롤러와 서비스에서도 마찬가지인데, 이렇게 해야 웹 레이어와 서비스 내부(도

linocraft.tistory.com

 

전에 품었던 의문을 해결했다. 결론적으로 Model Mapper는 내부의 객체까지 전부 자동으로 매핑해준다. 그러나 여기에는 두 가지 조건이 있는 것으로 확인했다.

( ※  Entity 클래스에는 절대 setter를 쓰지 않아야 한다. setter를 쓰지 않아도 잘 동작하며, 논리적으로도 엔티티는 DB에서 가져온 시점에서 불변이어야 앞뒤가 맞다. service 단에서 엔티티의 내부 값을 setter를 통해 자유롭게 변동하는 것을 허용한다면 엔티티를 믿을 수 없다. DB와 동기화된 데이터라고 어떻게 믿을 수 있겠는가. )

 

 

1. 변수 이름을 잘 적어야 한다.

먼저 변수 이름을 아래와 같이 설정하면 내부 객체는 매핑되지 않는다.

// Entity
@Entity
public class Reply {
    private Board board;
}

@Entity 
public class Board {
    @Id
    private Long bno;
}

// DTO
public class ReplyDTO {
    private Long bno;
}

public class BoardDTO {
    private Long bno;
}

여기서 관건은 과연 Reply 엔티티의 Board와 ReplyDTO의 bno가 서로 매핑될 수 있는가다.

결론부터 말하자면 매핑되지 않는다. 즉, Reply 엔티티의 Board가 존재해도 ReplyDTO의 bno는 null이 된다. 반대 방향도 마찬가지다. ReplyDTO의 bno가 있어도 Reply 엔티티의 Board는 null이 된다.

왜냐하면 이름이 잘못 설정되었기 때문이다. 

이름은 아래와 같이 Java Bean's Naming convention을 지켜야 비로소 Model Mapper가 매핑을 식별할 수 있다. Model Mapper는 디폴트로 Java Bean의 네이밍 컨벤션을 따른다.

// Entity
@Entity
public class Reply {
    private Board board;
}

@Entity 
public class Board {
    @Id
    private Long bno;
}

// DTO
public class ReplyDTO {
    private Long boardBno; // 달라진 부분!
}

public class BoardDTO {
    private Long bno;
}

 

 

2. Matching Strategy는 STANDARD

@Configuration
public class RootConfig {

    @Bean
    public ModelMapper getModelMapper() {
        ModelMapper modelMapper = new ModelMapper();
        
        modelMapper.getConfiguration()
                .setFieldMatchingEnabled(true)
                .setFieldAccessLevel(org.modelmapper.config.Configuration.AccessLevel.PRIVATE)
                .setMatchingStrategy(MatchingStrategies.STANDARD);
                
        return modelMapper;
    }
    
}

매칭 전략을 이전에 STRICT로 잡았었는데, STRICT는 아주 엄격한 매칭 전략으로서 이름와 타입이 완전히 일치하는 경우에만 매핑을 수행한다.

setFieldMatchingEnabled(true)로는 STRICT 전략을 덮어쓸 수 없다. 따라서 디폴트를 따르거나, 혹은 위처럼 STANDARD로 전략을 수정해야 한다.

 

 


※ Model Mapper는 개인적으로 조금 더 실험해보거나, 혹은 요즘 자주 쓴다는 MapStruct로 갈아타야겠다는 생각이 든다..