카테고리 없음

[Architecture] setter 대신 Entity 메서드를 사용하는 이유

기억지기 개발자 2026. 5. 15. 11:05

Spring Boot와 JPA로 쇼핑몰 프로젝트를 진행하면서 이런 코드를 작성하게 되었다.

public void cancel() {
    this.orderStatus = OrderStatus.CANCELLED;
}

처음에는 이런 생각이 들었다.

"그냥 setter로 값 바꾸면 되는 거 아닌가?"

예를 들면:

order.setOrderStatus(OrderStatus.CANCELLED);

이렇게 해도 동작은 똑같기 때문이다.

 


setter 방식의 문제

예를 들어 주문 취소를 setter로 처리한다고 해보자.

order.setOrderStatus(OrderStatus.CANCELLED);

현재는 단순히 상태 값만 바꾸기 때문에 큰 문제가 없어 보인다.

하지만 나중에 주문 취소 규칙이 추가되면 이야기가 달라진다.

 

예를 들어:

  • 이미 취소된 주문은 다시 취소 불가
  • 배송 완료 후에는 취소 불가
  • 주문 취소 시 재고 복구 필요
  • 취소 시간 저장 필요

같은 규칙들이 생길 수 있다.

그러면 Service 코드가 점점 복잡해진다.

if (order.getOrderStatus() == OrderStatus.CANCELLED) {
    throw new RuntimeException();
}

order.setOrderStatus(OrderStatus.CANCELLED);

product.restoreStock(order.getQuantity());

즉:

"주문 관련 규칙들이 Service 곳곳에 퍼지게 된다."


Entity 안에 메서드를 두는 방식

그래서 이런 식으로 Entity 내부에 메서드를 만들게 된다.

public void cancel() {

    if (this.orderStatus == OrderStatus.CANCELLED) {
        throw new RuntimeException("이미 취소된 주문");
    }

    this.orderStatus = OrderStatus.CANCELLED;
}

그리고 Service에서는 이렇게 사용한다.

order.cancel();

왜 이 방식이 더 좋을까?

가장 큰 차이는:

"값 변경"이 아니라 "행동"처럼 읽힌다는 점이다.

예를 들어:

order.setOrderStatus(OrderStatus.CANCELLED);

는 단순히 값을 수정하는 느낌이다.

반면:

order.cancel();

는:

"취소를 진행한다" 라는 의미가 바로 드러난다.


관련 로직을 한 곳에 모을 수 있다

이 방식의 진짜 장점은:

주문과 관련된 규칙을 Order 안에 모을 수 있다는 점

예를 들어 주문 취소 규칙이 바뀌더라도:

public void cancel() {
    ...
}

이 메서드만 수정하면 된다.

즉:

주문 취소 책임은 Order가 가진다.

라는 구조가 되는 것이다.


Product도 마찬가지

재고 복구도 비슷하다.

처음에는 이렇게 작성할 수도 있다.

product.setStockQuantity(
    product.getStockQuantity() + quantity
);

하지만 이렇게 하면 재고 관련 계산 로직이 Service에 계속 퍼지게 된다.

그래서 Product 안에:

public void restoreStock(Integer quantity) {
    this.stockQuantity += quantity;
}

를 만들고:

product.restoreStock(quantity);

처럼 사용하는 것이다.


정리

처음에는:

order.cancel();

과:

order.setOrderStatus(...);

의 차이가 크게 느껴지지 않았다.

하지만 나중에 프로젝트가 커진다면 :

  • 관련 로직을 어디에 둘 것인지
  • 상태 변경 책임을 누가 가질 것인지
  • 비즈니스 규칙을 어떻게 관리할 것인지

가 점점 중요해진다는 것을 느끼게 되었다.

결국 Entity 내부 메서드는:

 

"그 객체와 관련된 상태 변경 책임을 객체 스스로 가지게 하기 위한 방식"

 

이라는 것을 이해하게 되었다.


📍JPA 철학 관점에서 보면..

JPA에서는 Entity를 단순히:

DB 테이블과 연결된 데이터 덩어리

 

로만 보지 않는다.

오히려:

상태(state)와 행동(behavior)을 함께 가진 객체

로 보는 철학이 있다.

예를 들어 현재 Order

Order Entity는 단순 데이터만 있는 게 아니다.

private Integer quantity;
private OrderStatus orderStatus;
private Product product;

 


그런데 JPA 철학에서는

이 객체가 단순 저장용이 아니라 "주문이라는 개념 자체" 를 표현이라고 본다.

즉 Order는

 

자신의 상태를 알고 스스로 상태를 변경할 수 있어야 한다


라고 보는 거다.

그래서 이런 메서드가 자연스럽다

public void cancel() {
    this.orderStatus = OrderStatus.CANCELLED;
}

 

왜냐면

주문 취소는
Order가 해야 하는 행동

이기 때문이다. 


🔥 JPA가 싫어하는 구조

❌ Anemic Domain Model

이런 스타일

@Entity
public class Order {

    private Integer quantity;
    private OrderStatus orderStatus;

    // getter/setter만 존재
}

 

그리고 Service가 다 처리

order.setOrderStatus(CANCELLED);
product.setStockQuantity(...);

 

즉 Entity는

데이터만 들고 있음

 

행동과 규칙은 전부 Service에 있음.


⭐️ 느낀점

이번 내용을 학습하면서, 이전에는 단순히 setter를 통해 상태를 변경하는 방식이 자연스럽고 올바른 방법이라고 생각했던 적이 있었다.
하지만 Entity가 자신의 상태와 행동을 함께 관리하도록 설계하는 방식이 JPA와 객체지향 관점에서 더 지향되는 구조라는 점을 이해하게 되었고, 기존에 당연하다고 생각했던 방식들을 다시 돌아보게 되었다.

 

특히 단순히 “동작하는 코드”를 작성하는 것에서 끝나는 것이 아니라,
객체의 책임을 어디에 두어야 하는지, 비즈니스 로직을 어떤 위치에서 관리해야 유지보수성과 확장성이 좋아지는지에 대해 고민하는 과정이 중요하다는 것을 느꼈다.

 

이번 경험을 통해 개발에서는 하나의 방식만이 절대적인 정답이 되는 것이 아니라, 더 나은 설계와 더 의도가 명확한 구조가 무엇인지 계속 고민하고 학습하는 자세가 중요하다는 점을 다시 한 번 깨닫게 되었다.