결론부터 두괄식으로 작성해보자면,
"ModelMapper는 해당 클래스의 기본 생성자를 이용해 객체를 생성하고 setter를 이용해 매핑을 한다."
해당 블로그의 내용을 일부 인용하자면 이렇다.
Modelmapper는 일반적으로 setter를 사용한다. (개인적으로 Setter를 통해 모든 필드를 열어놓는 행위를 싫어한다.)
본인도 이 생각에 동의하는 바
@Entity
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Builder
@Getter
@Table(name = "USER", indexes = @Index(name = "i_email", columnList = "email"))
public class User extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "user_id")
private Long id;
//password
@Column(nullable = false)
private String password;
@Column(unique = true,nullable = false)
private String email;
private String name;
private String phone;
private String address;
private String level;
@OneToOne(fetch = FetchType.LAZY)
private Order order;
}
엔티티는 DB정보와 직결되는 객체라고 생각하는 바, 프로젝트 시작부터 생성자와 Setter를 전부 틀어막아서 불변 객체로 만들고싶어했다.
public void signUp(SignupRequest request) {
if (isExistsEmail(request.getEmail())) {
throw new DuplicatedIdException("Same id exists email: " + request.getEmail());
}
UserDTO encryptedUser = encryptUser(request);
User userTemp = modelMapper.map(encryptedUser, User.class);
userRepository.save(userTemp);
}

결과를 보자, 분명 ModelMapper으로 매칭을 시도했건만 전부 null이 뜨는 모습이다.
@Configuration
@RequiredArgsConstructor
public class ModelMapperConfig {
@Bean
public ModelMapper modelMapper() {
ModelMapper modelMapper = new ModelMapper();
/*연결 전략 : 같은 타입의 필드명이 같은 경우만 동작
*
* */
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.LOOSE).setSkipNullEnabled(true).setFieldMatchingEnabled(true)
.setFieldAccessLevel(AccessLevel.PRIVATE);;
return modelMapper;
}
}
해당 방법으로 해결하자 딱히 @Setter 같은 어노테이션이나 setter를 추가하지 않고도 잘 매핑되는 결과를 볼 수 있었다.
+추가 "modelmapper Unable to make field private final java.time.LocalDate java.time.LocalDateTime.date accessible: module java.base does not "opens java.time" to unnamed module" 의 경우 LocalDateTime 타입과 Date 타입을 갖는 필드간 매칭할때 벌어지는 오류였다.