Spring

[DDD] 자바에서의 참조 공유와 (Value Object, Entity) 정리

곽코딩루카 2025. 10. 17. 10:28
반응형

들어가며

DDD(Domain-Driven Design)를 공부하다 보면 “밸류(Value Object)는 불변 객체여야 한다”는 말을 자주 듣습니다.
그런데 막상 자바 코드로 보면 “왜 불변이어야 하지?”, “참조 공유가 왜 문제지?” 같은 의문이 생기죠.

이 글에서는

  • 자바의 참조 구조(Stack, Heap)
  • 참조 공유 문제의 원리
  • 불변 객체로 해결하는 이유
  • DDD에서 밸류(Value Object)와 엔티티(Entity)의 차이
    를 실제 예제 코드와 함께 명확히 정리해보겠습니다.

 

// 금액
class Money {
    private int value;

    public Money(int value) {
        if (value < 0) throw new IllegalArgumentException();
        this.value = value;
    }
    public int getValue() { return value; }
    public void setValue(int value) { this.value = value; } // ❗ 가변 포인트
    @Override public String toString() { return "Money(" + value + ")"; }
}

// 주문 목록
class OrderLine {
    private final String productName;
    private final Money price; // 같은 인스턴스를 그대로 참조
    private final int quantity;

    public OrderLine(String productName, Money price, int quantity) {
        this.productName = productName;
        this.price = price; // ❗ 방어적 복사 없음 → 참조 공유 발생
        this.quantity = quantity;
    }
    public Money getPrice() { return price; }
    public int total() { return price.getValue() * quantity; }
   
}

 

자바의 메모리 구조: Stack과 Heap

자바는 데이터를 저장할 때 두 가지 영역을 사용합니다.

구분저장 위치예시저장 내용
Stack 메서드 호출 시 생기는 임시 공간 Money price = new Money(1000); 변수명과 참조값(주소)
Heap new로 만든 객체들이 저장되는 공간 new Money(1000) 객체의 실제 데이터(필드 값)

즉,

 
Money price = new Money(1000);

 

 

이 한 줄의 실행 결과는 다음과 같습니다 (아래참고)

[Stack]           [Heap]
price ───► [ Money 객체 | value = 1000 ]

 

 

 

Stack에는 참조값(reference),
Heap에는 객체의 실제 데이터(value) 가 저장됩니다.

 

 

 

 참조 공유(Reference Sharing) 문제란?

자바의 참조형 변수는 객체를 가리키는 주소값을 저장합니다.
이 때문에 여러 변수가 같은 객체를 동시에 참조할 수 있습니다.

 
Money price = new Money(1000);
OrderLine line = new OrderLine(price);
price.setValue(2000); // 🔥 외부에서 금액 변경

 

이때 메모리 구조는 이렇게 됩니다.

 
[Stack]
price ─┐
       └──► [Heap] Money 객체 { value = 2000 }
line.price ─┘
 

 price와 line.price가 같은 객체(Heap 주소) 를 가리키고 있어서
한쪽을 수정하면 다른 쪽에도 영향을 줍니다.

이걸 참조 공유 문제(reference aliasing) 라고 합니다.

 

 

 불변 객체(Immutable Object)로 해결하기

OrderLine이 외부에서 받은 Money 객체를 그대로 저장하면,
참조 공유 때문에 외부 변경이 내부에도 영향을 줍니다.

해결 방법: OrderLine 내부에 복사본을 만들어 보관합니다.

 

 

class Money {
    private int value;

    public Money(int value) { this.value = value; }
    public int getValue() { return value; }
    public void setValue(int value) { this.value = value; }
}

class OrderLine {
    private final Money price;

    public OrderLine(Money price) {
        // ✅ 불변처럼 동작하도록 복사본 생성
        this.price = new Money(price.getValue());
    }

    public Money getPrice() { return price; }
}

 

 

 

 DDD의 밸류(Value Object)란 무엇인가?

밸류(Value Object) 는 “도메인 안의 개념을 값으로 표현하는 객체”입니다.
즉, ‘누구인지’보다 ‘무엇인지’가 중요한 객체입니다.

구분엔티티(Entity)밸류(Value Object)
구분 기준 고유 ID로 구분 값의 동등성으로 구분
동일성 판단 id가 같으면 동일 모든 필드 값이 같으면 동일
상태 변경 가능 (mutable) 불변 (immutable)
예시 주문(Order), 회원(User) 금액(Money), 주소(Address), 주문번호(OrderNo)

 

 

 

 밸류 타입 예시: OrderNo

주문 번호를 단순히 String으로 저장하면 도메인 의미가 드러나지 않습니다.
그래서 도메인 의미를 담은 밸류 타입을 만듭니다.

 
public record OrderNo(String value) {
    public OrderNo {
        if (!value.matches("^ORD-\\d{8}-\\d{4}$"))
            throw new IllegalArgumentException("잘못된 주문번호 형식입니다.");
    }
}

record는 자바 16부터 추가된 불변 객체 전용 문법으로,
모든 필드가 final이며, equals/hashCode도 자동으로 값 기반으로 생성됩니다.

사용 예:

OrderNo id = new OrderNo("ORD-20251017-0001");
Order order = new Order(id);

이제 코드를 보지 않아도
OrderNo라는 타입 이름만으로 이게 주문번호라는 의미를 바로 알 수 있습니다.

 

 

 

 

 밸류 객체와 엔티티 객체의 차이

구분엔티티(Entity)밸류(Value Object)
존재 이유 시스템 안에서 “고유한 존재” 도메인 개념을 “값으로 표현”
식별자 있음 (id 필드) 없음
동등성 판단 식별자 비교 (id.equals()) 값 비교 (equals() 모든 필드)
상태 변화 허용 불변
사용 예 주문(Order), 사용자(User) 금액(Money), 이메일(Email), 주문번호(OrderNo)

 

 

 

 

실전 예시

 
@Entity
public class Order {
    @Id
    private OrderNo id;

    @Embedded
    private Money totalAmount;

    public Order(OrderNo id, Money totalAmount) {
        this.id = id;
        this.totalAmount = totalAmount;
    }
}
 
 

→ DB에는 VARCHAR 값으로 저장되지만,
코드에서는 OrderNo, Money로 표현되어 의미가 살아 있는 도메인 모델이 됩니다.

 

 

 

 

 마무리 요약

개념핵심 요약
참조 공유 여러 변수가 같은 객체 주소를 가리켜서 한쪽 변경이 다른 쪽에 영향을 주는 문제
불변 객체 내부 상태를 변경할 수 없는 객체. 참조 공유로부터 안전함
밸류(Value Object) 도메인 개념을 값으로 표현하는 불변 객체. ID 없음
엔티티(Entity) 고유 식별자를 갖고, 상태가 변해도 같은 객체로 인식되는 존재
 
 
 정리하면,

자바의 객체는 참조로 다뤄지기 때문에 참조 공유가 필연적으로 발생합니다.
따라서 DDD에서의 밸류 객체는 불변 객체(Immutable Object) 로 만들어
참조 공유의 부작용을 원천 차단하고,
도메인의 의미가 드러나는 안전한 모델링을 구현하는 것입니다.

반응형