[자바 ORM 표준 JPA 프로그래밍 - 기본편] 값 타입

jimmy·2025년 1월 23일
post-thumbnail

값 타입이란 단순한 값으로 사용하는 자바 기본 타입이나 객체를 말하며, 식별자가 없고 값만 있으므로 변경 시 추적이 불가능하다는 특징이 있다.

값 타입은 크게 세 가지로 나뉜다.

  • 기본값 타입
  • 임베디드 타입
  • 컬렉션 값 타입

기본값 타입

  • 자바 기본 타입: int, double
  • 래퍼 클래스: Integer, Long
  • String

특징:

  • 기본 타입은 공유되지 않고 항상 값을 복사한다.
  • 래퍼 클래스와 String은 참조가 공유될 수 있으나, 불변성을 가진다.

임베디드 타입

임베디드 타입은 새로운 값 타입을 정의하여 복합적인 값을 표현할 때 사용된다. 주로 기본 값 타입을 모아 하나의 객체로 만들어 활용하며, 복합 값 타입이라고도 한다.

@Embeddable
public class Address {
private String city;
private String street;
private String zipcode;

protected Address() {} // 기본 생성자 필수 ⭐

public Address(String city, String street, String zipcode) {
this.city = city;
this.street = street;
this.zipcode = zipcode;
}

// Getter만 제공하여 불변 객체로 설계 ⭐
public String getCity() { return city; }
public String getStreet() { return street; }
public String getZipcode() { return zipcode; }
}

사용법

  • @Embeddable: 값 타입을 정의하는 곳에 표시
  • @Embedded: 값 타입을 사용하는 곳에 표시

(@Embeddable, @Embedded 둘 중 하나 넣으면 하나는 생략해도 된다)

  • 기본 생성자 필수

특징

  • entity를 가지고 있을 수 있다.
  • 임베디드 타입의 값이 null이면 매핑한 컬럼 값은 모두 null이 된다.
  • 재사용성
  • 높은 응집도
  • 해당 값 타입만 사용하는 의미 있는 메소드를 만들 수 있음 (예: Period.isWork())
  • 임베디드 타입을 포함한 모든 값 타입은, 값 타입을 소유한 엔티티에 생명주기를 의존함
  • 중복된 컬럼명 처리 한 엔티티에서 동일한 값 타입을 두 번 이상 사용하면, 컬럼명이 중복될 수 있다. @AttributeOverrides, @AttributeOverride를 사용하여 컬럼명을 재정의해야 한다.
    @Entity
    public class Member {
    
        @Id @GeneratedValue
        private Long id;
    
        @Embedded
        private Address homeAddress; 
    
        @Embedded
        @AttributeOverrides({
            @AttributeOverride(name = "city", column = @Column(name = "work_city")),
            @AttributeOverride(name = "street", column = @Column(name = "work_street")),
            @AttributeOverride(name = "zipcode", column = @Column(name = "work_zipcode"))
        })
        private Address workAddress;
    }
    
    잘 설계한 ORM 애플리케이션은 매핑한 테이블의 수보다 클래
    스의 수가 더 많음 → 즉, 값타입을 잘 활용하자!

값 타입과 불변 객체

임베디드 타입 같은 값 타입을 여러 엔티티에서 공유하면 어떤 일이 벌어질까?

→ 부작용이 발생한다!

한 엔티티의 값 타입만 수정하려고 해도, 값을 공유하는 모든 엔티티에서 해당 값이 바뀌어버릴 수 있다.

복사해서 사용하면 되는 거 아닌가?

→ 문제는 임베디드 타입이 객체 타입이라는 점이다. 객체 타입은 참조 값을 공유하기 때문에 복사해서 사용하는 것이 의미가 없다.

그렇다면 어떻게 해결할까?

불변 객체로 만들자! 불변 객체란 생성된 이후 값이 변경되지 않는 객체를 말한다.

💡불변 객체를 만드는 방법

  1. 생성자로만 값을 설정하고,
  2. Setter를 아예 만들지 않는다.

그럼 값을 어떻게 변경하지?

→ 값을 변경하려면 기존 객체를 수정하지 말고, 새로운 객체를 만들어 교체하는 방식으로 처리해야 한다.


값 타입의 비교

값 타입은 인스턴스가 달라도 그 안에 값이 같으면 동일한 것으로 간주해야 한다.

  • 동일성 비교 (==): 인스턴스의 참조 값을 비교한다.
  • 동등성 비교 (equals()): 인스턴스 내부의 값을 비교한다.

→ 값 타입은 동등성 비교를 해야한다!


값 타입 컬렉션

값 타입을 하나 이상 저장해야 하는 경우에는 값 타입 컬렉션을 사용할 수 있다.

  • @ElementCollection, @CollectionTable 사용
  • 값 타입 컬렉션은 데이터베이스에 별도의 테이블로 저장된다.

주의사항

  • 컬렉션 데이터는 데이터베이스에 모든 컬럼을 묶어 기본키로 구성해야 한다.
  • 값 타입 컬렉션은 식별자가 없으므로 영속성 컨텍스트에서 추적이 어렵다.(따라서 컬렉션 변경 시 모든 데이터를 삭제 후 재삽입한다.)

실무에서는?

값 타입 컬렉션 대신 일대다 관계를 고려하라!

→ 엔티티를 사용하며, 영속성 전이(CASCADE)와 고아 객체 제거(orphanRemoval)를 활용해 값 타입 컬렉션과 비슷한 방식으로 처리할 수 있다.


언제 값 타입을 사용할까?

값 타입은 정말 값으로만 판단될 때 사용해야 한다.

엔티티(Entity)와 혼동하지 말고, 식별자 개념이 필요하거나 생명주기를 독립적으로 관리해야 한다면 반드시 엔티티를 사용하자.


📌 본 포스팅은 '인프런 - 김영한님 강의(자바 ORM 표준 JPA 프로그래밍 - 기본편)'를 듣고 정리한 내용입니다.

profile
백문이 불여일기

0개의 댓글