스프링과 JPA 기반 웹 애플리케이션 개발 #35 ModelMapper 적용
해당 내용은 인프런, 스프링과 JPA 기반 웹 애플리케이션 개발의 강의 내용을 바탕으로 작성된 내용입니다.
강의를 학습하며 요약한 내용을 출처를 표기하고 블로깅 또는 문서로 공개하는 것을 허용합니다 라는 원칙 하에 요약 내용을 공개합니다. 출처는 위에 언급되어있듯, 인프런, 스프링과 JPA 기반 웹 애플리케이션 개발입니다.
제가 학습한 소스코드는 https://github.com/n00nietzsche/jakestudy_webapp 에 지속적으로 업로드 됩니다. 매 커밋 메세지에 강의의 어디 부분까지 진행됐는지 기록해놓겠습니다.
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.3.6</version>
</dependency>
modelMapper.getConfiguration()
.setSourceNameTokenizer(NameTokenizer.UNDERSCORE)
.setDestinationNameTokenizer(NameTokenizer.UNDERSCORE)
UNDERSCORE
(_)를 사용했을 때만 nested 객체를 참조하는 것으로 간주하고, 그렇지 않은 경우에는 해당 객체의 직속 프로퍼티에 바인딩한다.nested 된 경우, 어떻게 잘라서 읽어야 하는지 등에 대해 주체적으로 정할 수 없다.
기본 설정은 NamingConventions.NONE
으로 네이밍 컨벤션 없이 모든 프로퍼티 이름에 적용시키는 것이다.
이를테면 우리는 NotificationsForm
내부에 isStudyCreatedByEmail
이라는 필드가 있다. 이 때, ModelMapper
는 Study
라는 내부적인 클래스에 있는 CreatedByEmail
이라는 필드를 찾는지 알 수 없다는 뜻이다. 그래서 뜬금없게 account.setEmail()
에 넣을 데이터를 선택하는데 여러 개의 후보지가 발생했다는 에러가 뜬다.
@Bean
public ModelMapper modelMapper() {
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration()
.setDestinationNameTokenizer(NameTokenizers.UNDERSCORE)
.setSourceNameTokenizer(NameTokenizers.UNDERSCORE);
return modelMapper;
}
위와 같이 작성하면, UNDERSCORE
(_
)가 아니라면 이름이 어떻게 작성되어 있었든 상관없이 그 이름 전체를 하나의 프로퍼티 이름으로 생각한다는 것이다. 만일, nested 한 표현을 하고 싶다면 configuration에 설정해둔대로 UNDERSCORE
를 이용하면 된다.
<!-- 모델 맵퍼 -->
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.3.6</version>
</dependency>
@Bean
public ModelMapper modelMapper() {
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration()
.setDestinationNameTokenizer(NameTokenizers.UNDERSCORE)
.setSourceNameTokenizer(NameTokenizers.UNDERSCORE);
return modelMapper;
}
어차피 계속 생성자로 생성할 것이 아니기 때문에, 빈을 등록해서 쓰면 편리하다.
.setDestinationNameTokenizer()
메소드를 통해, UNDERSCORE
의 경우에만 토크나이징을 하도록 토크나이저를 설정하면 nested 에 대한 프로퍼티를 지정하고 싶을 때는 오직 _
를 이용해서 하면 된다. CamelCase가 nested 프로퍼티를 가리킨다는 오해에서 해방된다.
public void updateProfile(Account account, Profile profile) {
account.setUrl(profile.getUrl());
account.setBio(profile.getBio());
account.setLocation(profile.getLocation());
account.setOccupation(profile.getOccupation());
// TODO: 프로필 이미지
account.setProfileImage(profile.getProfileImage());
// TODO: 중요한 문제가 하나 더 남았다.
}
public void updateProfile(Account account, ProfileForm profileForm) {
// from Source to Destination 로 데이터를 전달해줌
// 프로퍼티의 이름이 일치한다는 가정 하에서 쓰는 것임
// 대량의 Setter 를 발생시킨다.
modelMapper.map(profileForm, account);
}
간단하게 첫번째 인자에 source를 주고 두번째 인자에 destination을 주면 된다. 내부적으로 대량의 setter를 발생시켜 프로퍼티 이름이 일치시키는 부분을 그대로 옮겨준다.
model.addAttribute(new ProfileForm(loginAccount));
public ProfileForm(Account account) {
this.bio = account.getBio();
this.url = account.getUrl();
this.occupation = account.getOccupation();
this.location = account.getLocation();
this.profileImage = account.getProfileImage();
}
model.addAttribute(modelMapper.map(loginAccount, ProfileForm.class));
클래스명.class
를 이용하여 해당 클래스 정보 자체를 넣어주면, 반환 값이 해당 클래스의 객체가 된다.