작성
·
971
0
안녕하세요, 강의에는 나오지 않았지만 변경감지를 병합보다 추천하셔서 DTO를 사용해서 한 번 짜보았는데 봐주실 수 있나요?
```java
package jpabook.jpashop.service;
import lombok.Getter;
import lombok.Setter;
@Getter @Setter
public class UpdateItemDto {
private Long id;
private String name;
private int price;
private int stockQuantity;
private String author;
private String isbn;
}
```
일단 UpdateItemDto는 간단히 Getter, Setter만으로 구성했습니다.
@PostMapping("items/{itemId}/edit")
public String updateItem(@PathVariable("itemId") Long itemId, @ModelAttribute("form") BookForm form) {
UpdateItemDto updateItemDto = new UpdateItemDto();
updateItemDto.setId(form.getId());
updateItemDto.setName(form.getName());
updateItemDto.setPrice(form.getPrice());
updateItemDto.setStockQuantity(form.getStockQuantity());
updateItemDto.setAuthor(form.getAuthor());
updateItemDto.setIsbn(form.getIsbn());
itemService.updateItem(itemId, updateItemDto);
return "redirect:/items";
}
위 코드는 ItemController 안의 updateItem 메소드입니다.
@Transactional
public void updateItem(Long itemId, UpdateItemDto updateItemDto) {
Item findItem = itemRepository.findOne(itemId); //findItem으로 찾아온 값은 영속상태
findItem.change(updateItemDto);
}
위 코드는 ItemService 클래스의 updateItem 메소드입니다.
public void change(UpdateItemDto updateItemDto) {
// if (updateItemDto.getId() != null) {
// this.id = updateItemDto.getId();
// }
if (updateItemDto.getName().length() != 0) {
this.name = updateItemDto.getName();
}
// if (updateItemDto.getPrice() != null) {
// this.price = updateItemDto.getPrice();
// }
// if (updateItemDto.getStockQuantity() != null) {
// this.stockQuantity = updateItemDto.getStockQuantity();
// }
this.price = updateItemDto.getPrice();
this.stockQuantity = updateItemDto.getStockQuantity();
// if (updateItemDto.getAuthor() != null) {
// this.author = updateItemDto.getAuthor();
// }
// if (updateItemDto.getIsbn() != null) {
// this.isbn = updateItemDto.getIsbn();
// }
}
마지막으로 Item 클래스의 change 메소드입니다.
Book의 상위 클래스이기 때문에 author과 isbn 필드가 없어서 이 change 메소드를 Book 클래스 내로 옮겨야 하는지 고민을 했습니다.
그러나 그렇게 진행하면 부수적인 문제가 너무 많이 생겨 그냥 Item 클래스 내에 남기고 author와 isbn을 수정하는 기능은 없앴습니다.
id는 바꿀 필요가 없다고 생각하여 기능을 뺐습니다. Form에도 id를 수정하는 란은 없는데
기존에 영한 님이 강의하실 때 ItemController 내에서 book.setId(form.getId)); 하셨는데 잘못된 부분인 것 같습니다.
name같은 경우 String이라 웹 상에서 수정을 할 때 빈 칸으로 두면 null보다 empty string으로 받는 것 같아서
updateItemDto.getName() != null 조건에 안 걸리는 것 같더라구요. 그래서 빈 칸을 empty string을 찾는 식으로
짰습니다. 마지막으로 price와 stockQuantity는 int라서 != null 조건을 걸수가없는데
웹 상에서 빈 칸일 때 이전 price와 stockQuantity를 유지하는 방법을 여쭤보고 싶습니다.
결론적으로 제 질문은:
1. UpdateItemDto의 경우 controller 패키지의 BookForm과 이름 외에 똑같은데 그래도 DTO 클래스를 따로 만드는데 의의가 있는건가요?
2. ItemController 내에서 어설프게 Entity를 생성하지 말라고 하셨는데 DTO 객체를 생성하는 것은 괜찮은가요?
3. 전반적으로 이런 방식으로 변경감지를 하는건지가 궁금합니다.
4. change 메소드를 Item 밑에 두는게 맞는지 Book 밑에 두는게 맞는지?
답변 주시면 정말 큰 도움이 될 것 같습니다.
감사합니다!
답변 2
0
안녕하세요. ye k님
Q: price와 stockQuantity는 int라서 != null 조건을 걸수가없는데
A: Integer 타입을 사용하시면 됩니다.
1. UpdateItemDto의 경우 controller 패키지의 BookForm과 이름 외에 똑같은데 그래도 DTO 클래스를 따로 만드는데 의의가 있는건가요?
-> BookForm은 사용자 UI에서 전달하는 데이터를 컨트롤러(웹 계층)까지 전달받는 데이터입니다. 지금은 예제가 단순하지만 실제는 이것보다 훨씬 복잡하고 여러 데이터들을 받게 됩니다. 여기서 만든 DTO는 엔티티와 밀접하게 사용되는 DTO인데요. 따라서 외부 영향을 최대한 받으면 안되도록 주의해야 합니다. 그리고 의존관계 관점에서도 고민해야 하는데요. BookForm을 그대로 사용해버리면 엔티티가 BookForm을 의존하게 됩니다. 그러면 엔티티 계층이 웹 계층을 의존하는 의존관계 역전 문제가 발생합니다. 새로만든 DTO는 엔티티와 같은 계층에서 사용하도록 설계되어야 합니다.
2. ItemController 내에서 어설프게 Entity를 생성하지 말라고 하셨는데 DTO 객체를 생성하는 것은 괜찮은가요?
네 괜찮습니다.
3. 전반적으로 이런 방식으로 변경감지를 하는건지가 궁금합니다.
-> 네 맞습니다. 그런데 꼭 엔티티안에서 하지 않아도 되고, 상황에 따라서는 서비스 계층에서 getter, setter를 사용해서 데이터를 변경하셔도 됩니다.
4. change 메소드를 Item 밑에 두는게 맞는지 Book 밑에 두는게 맞는지?
-> 이 부분은 정답이 있는 것이 아니어서 상황에 따라서 조금씩 달라질 수 있습니다. Book과 관련된 부분의 변경이 많다면 BookUpdateItemDto가 필요할 수도 있습니다.
감사합니다.
0
추가적인 질문을 드리자면 현재 ItemRepository의 save 메소드가 아래와 같습니다.
하지만 merge 사용을 방지하고자 상품 등록때만 save 메소드를 사용을 할건데
그러면 save 메소드를 getId == null 체크를 하지 않고 그냥 em.persist(item)만 남겨두고 상품 등록때만 사용을 하면 merge 사용 방지가 될까요?
public void save(Item item) {
if (item.getId() == null) {
em.persist(item);
} else {
em.merge(item);
}
}