어설픈 객체지향은 안티 패턴을 만들어낸다.
스터디 프로젝트를 진행하면서 받았던 리뷰를 한번 정리하고자 합니다.
필자는 프로젝트를 진행하면 최대한 객체지향 적 코드를 짜려고 시도를 해봤다.
그중 공부하면서 보았던 Tell, don't ask 원칙(TDA 원칙) 즉 객체에게 물어보지 말고 시켜라는 의미가 있다.
해당 글을 읽고 내가 평소에 짜던 코드 방식을 다시 생각하는 시간을 가졌다.
따라서 해당 원칙을 사용하면서 안티패턴을 만들어낸 상황과 해결방법을 공유하고자 한다.
책임을 확실하게 구분하자.
TDA 원칙을 따르면서 내가 간과한 부분은 객체에 대한 책임을 확실하게 구분하지 않았다.
우선 예제를 보자.(컨트롤러는 중요하지 않으니 생략하겠습니다.)
//.. 롬복 생략
public class UsedItemRequest {
@NotNull
@ApiModelProperty(value = "글 작성자(ID)", required = true, example = "dkansk924@naver.com")
private String writer;
//... 필드 생략
@ApiModelProperty(value = "판매위치 주소", example = "서울시중구")
private String address;
}
위 예제의 클래스는 사용자의 요청을 받는 DTO입니다.
해당 DTO가 오면 저장을 하기 위해 서비스 로직을 타게 됩니다.
저는 초기에 이러한 코드를 작성했습니다.
@Service
@RequiredArgsConstructor
@Transactional
public class UsedItemService {
private final UsedItemRepository usedItemRepository;
public UsedItem save(UsedItemRequest request) {
UesdItem item = UsedItem.builder()
.writer(writer)
.title(title)
.price(price)
.description(description)
.tradeType(tradeType)
.category(category)
.negotiation(negotiation)
.address(address)
.build();
return usedItemRepository.save(item);
}
}
해당 코드를 보면 엄청 마음에 안 듭니다.
save 메서드의 목적은 UsedItem 도메인을 저장하는 것이 목적입니다.
해당 메서드의 핵심 로직은 usedItemRepository.save(item)
repository에 저장하는 이 부분입니다.
따라서 save라는 메서드에서 DTO -> Entity을 직접 변환하는 과정을 굳이 서비스 로직에 노출할 필요가 없다고 생각을 했습니다.
실수
저는 DTO -> entity을 변환하는 과정에 있어 변환을 담당하는 책임은 entity에 있다고 생각했습니다.
왜냐면 객체에게 시켜라 라는 의미에서 entity 한태 자기 자신을 만들어라는 책임을 부여하고 싶어
UsedItemRequest(DTO)를 UsedItem(entity) 에게 넘겨주어 자기 자신을 만들라고 책임을 부여해서 다음과 같은 코드를 작성했습니다.
//각종 설정 생략
public class UsedItem {
//필드 생략
public static UsedItem toEntity(UsedItemRequest item) {
return UsedItem.builder()
.writer(item.getWriter)
.title(item.getTitle)
.price(item.getPrice)
// 생략
.address(address)
.build();
}
}
위 예제처럼 코드를 작성하면 아래 예제처럼 save 메서드는 깔끔한 로직으로 변하게 됩니다.
public UsedItem save(UsedItemRequest request) {
UsedItem item = UsedItem.toEntity(request);
return usedItemRepository.save(item);
}
그럼 문제가 다 해결된 건가요??
아닙니다.
여기서 한 가지 간과한 부분이 있습니다.
Entity 객체가 DTO를 의존하고 있는 코드를 만들게 되었습니다.
해당 부분이 뭐가 문제야? 라고 생각할 수 있지만
Entity(UsedItem) 은 DB Layer와 소통하는 객체이고 DTO(UsedItemRequest) 은 View layer 와 소통하는 객체입니다.
여기서 문제가 발생합니다.
앞단 즉 클라이언트랑 소통하는 객체의 경우 데이터 변경이 자주 일어나게 됩니다.
데이터 변경이 자주 일어나는 객체를 Entity 가 의존하고 있으니 앞단에서 변경이 일어날 때마다 Entity까지 영향이 갑니다.
이러한 코드는 매우 안좋고 위험합니다.
따라서 책임을 확실하게 생각하지 않고 작성한 코드 때문에 안티 패턴을 만들게 되었습니다.
제가 계속 책임을 강조하는 이유는 해당 안티패턴의 해결은
DTO -> entity 을 변환하는 과정에 있어 변환을 담당하는 책임은 entity에 있다고 생각했습니다.
이 부분을 반대로 생각하면 됩니다.
public class UsedItemRequest {
@NotNull
@ApiModelProperty(value = "글 작성자(ID)", required = true, example = "dkansk924@naver.com")
private String writer;
//.. 필드생략
public UsedItem toEntity() { // 책임을 DTO 에게 !
return UsedItem.builder()
.writer(writer)
.title(title)
.price(price)
.description(description)
.tradeType(tradeType)
.category(category)
.negotiation(negotiation)
.address(address)
.build();
}
}
서비스 코드
public UsedItem save(UsedItemRequest request) {
return usedItemRepository.save(request.toEntity());
}
결론
어설픈 객체지향은 안티 패턴을 만들고 매우 안 좋은 코드를 생성합니다. ㅜㅜ
코드를 작성할 때 객체의 대한 책임을 명확하게 구분하여 코드를 작성합시다!
또한 제가 느낀 점은 올바른 객체지향 설계는 지름길이 없음으로 코드를 많이 작성해보고 리뷰를 많이 받아야 생각하는 시야가 넓어집니다.
긴 글 읽어 주셔서 감사합니다 :)
- 끝 -
'나만의공부(이슈정리)' 카테고리의 다른 글
Entity 설계 다시하기 (0) | 2021.06.23 |
---|---|
토이프로젝트 다시한번 살펴보기! (1) | 2021.06.16 |
어느 패턴을 사용해야 정답일까요?? (0) | 2021.03.22 |
[Spring-boot] CORS , SOP 개념 및 설정 (0) | 2021.01.24 |
[JAVA] live -study 대시보드 만들어보기 (gitHub 라이브러리 사용) (0) | 2020.12.06 |