인프런 영문 브랜드 로고
인프런 영문 브랜드 로고

인프런 커뮤니티 질문&답변

bk님의 프로필 이미지
bk

작성한 질문수

실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발

주문, 주문상품 엔티티 개발

생성 메서드 setter 질문

해결된 질문

작성

·

7.7K

38

entity에는 Setter가 존재하면 안된다는 글을 많이 보았습니다.

때문에 생성자를 통해서만 entity가 만들어지도록 샘플프로젝트를 만들어왔었는데요.

물론  교육이라 setter를 사용하신다고 생각하는데요

실무에서

 setter를 entity 내 만들어쓰시는지

생성자를 통해서 해결하시는지

또는 private 처리로 생성 메서드만 노출하는지

궁금합니다.

연관관계 편의 메서드와

생성 메서드 예를 들어주실수 있으실까요?

감사합니다~

답변 8

30

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. bk님^^

객체를 생성할 때는 3가지 방법중 하나를 사용합니다.

- 생성자

- 정적 팩토리 메서드

- Builder 패턴

엔티티에 따라 이 방법중 상황에 따라서 하나를 선택하고, 파라미터에 객체 생성에 필요한 데이터를 다 넘기는 방법을 사용합니다. 그리고 정적 팩토리 메서드나, Builder 패턴을 사용할 때는 생성자를 private 처리합니다. 객체 생성이 간단할 때는 단순히 생성자를 사용하고, 만약 객체 생성이 복잡하고, 의미를 가지는 것이 좋다면 나머지 방법 중 하나를 선택합니다.

그러면 setter가 없는데, 엔티티를 어떻게 수정할까요?

이것은 setter를 만들기 보다는 의미있는 변경 메서드 이름을 사용합니다. 예를들어서 고객의 등급이 오른다면 member.levelUp() 같은 메서드가 있겠지요? 이 메서드는 내부의 필드 값을 변경하고요.

만족하실 만한 답변이 되었는지 모르겠네요^^ 혹시 더 궁금하신 내용이 있으면, 구체적인 예제 코드로 질문을 해주시면 제가 더 자세히 답변을 드릴께요^^

감사합니다.

27

김영한님의 프로필 이미지
김영한
지식공유자

처음 질문의 중요한 요지는 setter를 줄이는 것으로 이해했어요.

자세히 읽어보니 제가 createOrderItem2 부분만 보고, 섣불리 답변을 드렸네요^^ 그래서 정적 팩토리에 추가로 빌더까지 사용하신 것 같습니다.

이미 두번째 질문 주신 앞 부분에 단순 생성자나, 빌더를 통해서 setter 없이 문제를 해결하는 방법을 적어주셨네요^^

마지막에 고민하셨던 것 처럼, 정적 팩토리 메서드 없이 생성자를 통해서 바로 처리하셔도 충분합니다.

핵심은 정적 팩토리, 단순 생성자, 빌더, 어떤것을 사용하든 상관이 없습니다. 중요한 것은 이렇게 생성자에 파라미터를 넘기는 기법을 사용해서, 변경이 필요없는 필드에 추가적인 setter를 외부에 노출하는 것을 줄이는 것이 핵심입니다.(생성 이후에 변경할 필요가 없는데, setter가 외부에 노출되어 있으면 이것을 사용하는 다른 개발자들은 이 setter를 호출해야 하나? 말아야 하나 고민하니까요. 그런데 setter나 변경 가능한 메서드가 없으면, 아 이건 내가 막 변경하면 안되는구나 생각하겠지요.)

외부에 어떤 것을 공개할지 객체 생성을 목적으로 하는 것이기 때문에 외부에 한가지 방식만 제공하면 됩니다^^

추가로 마지막에 질문주신 item.removeStock(count)라는 비즈니스 로직을 new 생성자 안에 직접 넣는 부분은 저도 부담스럽네요. new 생성자는 관례상 객체를 정말 생성하는데 필요한 역할을 담당하기 때문인데요. 그래서 이 부분은 저도 정확한 답변을 드리기는 어렵지만, 저라면 위에 적어주신 것 처럼 정적 펙토리 메서드에 이 로직을 넣거나, 아니면 외부 비즈니스 서비스 계층으로 이 비즈니스 로직 호출을 넘기는 둘중 하나를 선택할 것 같습니다. (역시 new는 객체 생성만 담당하는게 좋겠네요. 아직 메모리 할당도 안되었는데, 비즈니스 메서드가 호출되는 것은 문제는 없겠지만, 역할과 관례싸 부담스럽네요.)

그리고 추가로 setter를 어느정도는 허용하셔도 됩니다^^(단 관리를 잘 하여야 합니다.) 연관관계를 양방향으로 적용하시려면 필연적으로 들어가야 할 때도 있더라구요 ㅎㅎ

감사합니다^^

5

김영한님의 프로필 이미지
김영한
지식공유자

원하는 질문을 잘 풀어서 해주셨네요^^

마지막에 createOrderItem2에서 OrderItem을 생성할 때도 OrderItem에 setter없이, item, orderPrice, count를 OrderItem에 생성자나 정적 펙토리 메서드나 빌더 등을 추가하고 파라미터 값으로 다 넘기도록 변경해보세요^^

그러면 이런 상황에도 setter를 제거할 수 있습니다. 생성 시점에 정말 다 끝낼 수 있는거지요.

그리고 사실 setter에 강박관념을 가질 필요는 없습니다. 쉽게 안풀리면 일부분에 setter를 사용하는 것도 방안이라 보아요^^(양방향 연관관계 일때 억지로 푸는 것 보다 setter를 사용하는게 편한 상황이 있더라구요)

그럼 한번 OrderItem에도 적용해보시고^^! 댓글남겨주세요

4

bk님의 프로필 이미지
bk
질문자

제가 이해하는게 맞는지 확인차 추가 문의드립니다.

예제중에서 

public static OrderItem createOrderItem(Item item, int orderPrice, int count) {
OrderItem orderItem = new OrderItem();
orderItem.setItem(item);
orderItem.setOrderPrice(orderPrice);
orderItem.setCount(count);

item.removeStock(count);

return orderItem;
}

위 예제는 정적 팩토리 메서드이고 Entity를 생성하는 방식이 다양하다면 쓰는방식?

아래는 생성자를 통한 생성

private OrderItem(Item item, int orderPrice, int count) {
this.item = item;
this.orderPrice = orderPrice;
this.count = count;

item.removeStock(count);
}

아래는 생성자 Builder 패턴 추가

@Builder
private OrderItem(Item item, int orderPrice, int count) {
this.item = item;
this.orderPrice = orderPrice;
this.count = count;

item.removeStock(count);
}

라고 이해는게 맞을까요?

또한 Entity에 비지니스 로직이 들어가는게 상당히 이점이 있구나 생각이 들면서

Set자체가 있으면 안된다는 강박관념이 공부하다가 생겨서 김영한님의 예제를 볼때
상당히 혼란스러웠습니다.

답변을 주신것을 기반으로 생각할때 Entity에서는 @Setter는 쓰지않는다
단, 변경메서드를 만들어서 의미를 부여한다.
즉, 아래와 같은식으로 한다.

public static OrderItem createOrderItem2(Item item, int orderPrice, int count) {
OrderItem orderItem = new OrderItem();
orderItem.mappingItem(item);
orderItem.mappingOrderPrice(orderPrice);
orderItem.mappingCount(count);

item.removeStock(count);

return orderItem;
}

하지만 mappingItem 과 같이 의미가 없다면...
@Setter Lombok은 쓰지않지만
Entity 내 setter 메서드를 필요에 따라 만든는것은 실무에서는 사용된다? 
라고 판단해도 좋을까요?

회사내 Boot와 JPA를 도입해보고자 샘플을 만드는데 조금이라도 더 고민하고싶어
과정과 벋어난 질문을 드리는것같아 송구합니다만
인프런에 이런 좋은 강의를 올려주신건 저에겐 행운이네요.

감사합니다.

1

안녕하세요 위 질문과 연관관계는 없지만

질문을 읽어 보고 강사님 의견도 보면서

정적 팩토리 메서드에서 객체를 생성하는

코드 샘플을 만들어 보았습니다.

//==생성 메서드 ==//
public static Ex04Order createOrder(Ex04Member member, Ex04Delivery delivery, Ex04OrderItem... orderItems) {
//생성자를 사용할 경우
// Ex04Order order = new Ex04Order(member, delivery, LocalDateTime.now(), Ex04OrderStatus.ORDER );
// for (Ex04OrderItem orderItem : orderItems) {
// order.addOrderItem(orderItem);
// }
// return order;

//setter를 사용할 경우 ( 이 방법은 피하자! )
// Ex04Order order = new Ex04Order();
// order.setMember(member);
// order.setDelivery(delivery);
// for (Ex04OrderItem orderItem : orderItems) {
// order.addOrderItem(orderItem);
// }
// order.setStatus(Ex04OrderStatus.ORDER);
// order.setOrderDate(LocalDateTime.now());
// return order;

//빌더 객체를 사용할 경우
final Ex04Order order = Ex04Order.builder()
.member(member)
.delivery(delivery)
.status(Ex04OrderStatus.ORDER)
.orderDate(LocalDateTime.now())
.build();

Arrays.stream(orderItems).forEach(order::addOrderItem);

return order;
}

감사합니다.

0

음.. setter없이 update문은 어떻게 수행하나요??

setter를 지양하는 것이 좋다고해서 이번에 static 함수를 이용해서 Create매서드를 만들었습니다.

 

그런데 객체에 find를 써서 찾은 뒤 수정하려고하니깐 어찌해야할지.. 

 

이럴 경우 현업에서는 setter를 안쓰고 어떻게하나요?

 

 

 

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. 김카프리오님

필요한 경우 setter도 사용해야 합니다.

그런데 단순히 필드 하나만 변경해야 한다면 setter도 사용하지만, 그게 아니라면 의미있는 비즈니스 메서드 이름을 사용하는 것도 좋습니다. (예: 등급 업 등등)

감사합니다.

0

프로젝트를 진행하던 중 @Setter를 없애면서 큰 고민이 있었는데 .. 해결이 되는것같네요! 좋은 정보 제공해주셔서 감사합니다~

0

bk님의 프로필 이미지
bk
질문자

추가
정적 팩토리 메서드라는 패턴이 있는것을 알게되어 이해하게 되었습니다.
JPA뿐만 아니라 여러모로 도움이 되는 명강의 입니다.

===================================================================

말씀하신대로 OrderItem에 적용해보았습니다.

public static OrderItem createOrderItem(Item item, int orderPrice, int count) {

OrderItem orderItem = OrderItem.builder()
.item(item)
.orderPrice(orderPrice)
.count(count)
.build();

item.removeStock(count);

return orderItem;
}

@Builder
private OrderItem(Item item, int orderPrice, int count) {
this.item = item;
this.orderPrice = orderPrice;
this.count = count;
}

헌데... 

왜 정적 펙토리 메서드 내 생성자를 넣어서 만드는껄까? 하는 의문이 듭니다.

즉,

@Builder
private OrderItem(Item item, int orderPrice, int count) {
this.item = item;
this.orderPrice = orderPrice;
this.count = count;

item.removeStock(count);
}

이렇게 만들어서 Service에서 바로 사용하면.... 
생성자에 비지니스 로직이 들어가는건 좋지 않을것같은 느낌이네요.

혹시 생성자는 생성자대로 존재하고
다양한 비지니스 로직에 따라서 정적 팩토리 메서드들이 저 생성자를 재사용하겠금 하는게
목적일까요?

귀한 주말에 빠른답변 감사드립니다~

bk님의 프로필 이미지
bk

작성한 질문수

질문하기