호돌맨의 요절복통 개발쇼를 보면서 Post 객체를 수정하는 API를 작성했다.
수정시 받아드리는 Request전용 클래스인 PostEdit을 만들고, 서비스 레이어에서 PostEdit 객체를 Post객체로 변환하는 과정이 있었는데, 여기서 좀 특이? 한 방식이 있어 그냥 생성자나, Builder패턴으로 변환하는것과 어떤차이가 있을지 무작정 따라하지만은 말고, 한번쯤 생각해보고 넘어가면 좋을것 같아서 포스팅을 작성한다.
이 글을 통해서 얻고자 하는 것들
- DTO -> Entity객체로 변환하는 방법
- toEdit() 설명
- 생성자,Builder VS toEdit() 의 장단점
DTO -> Entitiy 객체로 변환하는 방법들
1. 생성자, 혹은 전용 메소드
@Transactional
public void edit(Long id, PostEdit postEdit){
Post post = postRepositroy.findById(id)
.orElseThrow(() -> new PostNotFound());
post.chagePost(postEdit.getTitle(), postEdit.getContent());
}
Post 클래스의 메소드
/**
* toEditor로 바꿔야한다!
* @param editedTitle
* @param editedContent
*/
public void chagePost(String editedTitle, String editedContent){
this.title = editedTitle;
this.content = editedContent;
}
현재 내가 작성한 메소드는 changePost() 메소드로, 사실상 생성자 패턴에 가깝다. Builder패턴을 정하기 귀찮?.. 아서 일단 이렇게 마무리 한것 같은데, 역시 아무래도 Builder 패턴으로 정하는게 좀 더 유연할 것 같긴 하다.
2. ModelMapper
검색을 해본 결과, 그리고 키워드를 보자 fream 프로젝트에서 했던 방법이 떠올랐다. 바로 ModelMapper다. 굉장히 편하게 했던 기억이 있는데, 좀 검색해보면 성능?... 인지 어떤 이유때문에 선호하지 않는 사람들도 있던 것 같다.
ModelMapper는 위에서 언급한 대로 엔티티와 DTO 간에 변환할 때 자동으로 매핑시켜주는 라이브러리입니다.
매핑해줄 클래스에는 setter가 있어야 하고 매핑이 되는 클래스에서 getter가 있어야 사용 가능합니다.
기본적으로 ModelMapper에서 제공해주는 map메서드를 이용하여 변환할 수 있고 클래스 내부에 있는 변수들의 이름을 분석하여 자동 매핑시켜주는 방식입니다
그럼 간단한 예제 코드를 보겠다.
public class MapperTest {
@Autowired
TeamJpaRepository teamJpaRepository;
@Autowired
PersonJpaRepository personJpaRepository;
@Autowired
ModelMapper modelMapper;
@Test
public void modelMapper1() { // 객체 하나
Team team = teamJpaRepository.findById(1).get();
TeamDto teamDto = modelMapper.map(team, TeamDto.class);
log.info(teamDto.toString());
Team nextTeam = modelMapper.map(teamDto, Team.class);
log.info(nextTeam.toString());
}
@Test
public void modelMapper2() { // 리스트
List<Team> teamList = teamJpaRepository.findAll();
List<TeamDto> teamDtoList = teamList.stream().map(team -> modelMapper.map(team, TeamDto.class)).collect(Collectors.toList());
log.info(teamDtoList.toString());
List<Team> nextTeamList = teamDtoList.stream().map(teamDto -> modelMapper.map(teamDto, Team.class)).collect(Collectors.toList());
log.info(nextTeamList.toString());
}
}
코드 자체는 굉장히 간단하다. ModelMapper에서 제공하는 map()메소드만 쓰면 된다.
단, 내가 생각하는 치명적인 단점이 있는데, 바로 Setter가 필요하다는 것이다... 애초에 Builder패턴을 사용하는 이유에는 객체의 불변성을 보장하는것도 있는데, ModellMapper를 사용함으로써 Setter를 쓰게되면.. 정말 Setter를 일일히 하지 않는다는 편리함? 외에는 얻는게 없는것 같다는 생각이다.
toEdit()이란?
그럼 이제 호돌맨이 사용하는 toEdit()에 대해서 알아보자.
우선 호돌맨은 위와 같이 chage() 메소드를 직접 사용할 경우 파라미터가 많아지면 실수가 일어날 확률이 많다고 한다.
그래서 이런 패턴? 방법을 사용한다고 한다. 설명 자체도 많지 않았으므로, 바로 코드로 보자.
Post클래스
package com.example.hodolclone.entity;
@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Data
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
@Lob
private String content;
@Builder
public Post(String title, String content){
this.title = title;
this.content = content;
}
/**
* Builder를 넘기기 때문에 build로 데이터를 픽스하지 않는다.
* @return
*/
public PostEditor.PostEditorBuilder toEditor(){
return PostEditor.builder()
.title(title)
.content(content);
}
/**
* PostEditor라는 새로운 클래스를 받아 Post를 변경한다.
* PostEditor는 변경되는 속성들만 가지고 있는 클래스다.
* 다른 개발자가 봤을때 PostEditor만 변경이 가능하구나 를 알수있다.
* @param postEditor
*/
public void edit(PostEditor postEditor){
this.title = postEditor.getTitle();
this.content = postEditor.getContent();
}
}
toEdit(), edit() 메소드가 추가되었다.
PostEditor 클래스
package com.example.hodolclone.entity;
@Getter
@Builder
@RequiredArgsConstructor
public class PostEditor {
private final String title;
private final String content;
}
PostEditor클래스에는 변경이 가능한 것들만 모아둔다. 신기하게 DTO나 VO로 분류하지 않고, Entity로 분류를 해두었다.
PostService 클래스
- 편의상 변경된 edit()메소드만 첨부한다.
@Transactional
public void edit(Long id, PostEdit postEdit){
Post post = postRepositroy.findById(id)
.orElseThrow(() -> new PostNotFound());
PostEditor postEditor = post.toEditor()
.title(postEdit.getTitle())
.content(postEdit.getContent())
.build();
post.edit(postEditor);
}
post.toEdit() 메소드로 builder를 반환하고, 바로 postEdit의 속성으로 바꿔서 PostEditor로 build를 해준다.
이후 PostEdit을 인자로 받아 Post 객체를 수정한다.
호돌맨이 말하는 이 방법의 장점
- Post로 바꿀때 Post는 PostEditor 인자를 하나만 받는다. 즉, 파라미터가 많아져 실수할 일이 줄어든다.
- PostEditor에 변경이 가능한 속성을 따로 모아둠으로써,변경하지 말아야 할 속성들을 변경하는 실수를 막아준다. (다른 개발자가 보고 Post.builder()가 아닌, edit() 기능을 이용함으로써 실수가 줄어든다.)
생성자,Builder VS toEdit() 의 장단점
사실상 toEdit()은 Builder() 패턴을 이용하면서, 변경되지 말아야할 속성들을 강제하는 기능을? 추가한것으로 보인다.
즉, 생성자,Builder패턴이나 ModelMapper를 이용하는 방법들보다 좀 더 발전한? 방법인것 같다.
다만, 내가 느끼는 단점은 처음? 이 코드를 보면 왜 그냥 Builder를 안쓰고 귀찮게 저렇게 하지?.. 라는 생각을 할수도... 주석을 잘 적어놔야 할것 같다.
refs
'강의 > 호돌맨의 요절복통 개발쇼' 카테고리의 다른 글
Springboot의 정적리소스 접근경로 (0) | 2022.08.30 |
---|