DEV ℧ Developer Diary

[JPA] 값 타입(2)

해당 포스트는 인프런 김영한님의 자바 ORM 표준 JPA 프로그래밍 - 기본편 을 듣고 정리한 글입니다.

값 타입(2)

값 타입과 불변 객체

값 타입은 복잡한 객체 세상을 조금이라도 단순화 하려고 만든 개념이다. 따라서 값 타입은 단순하고 안전하게 다룰 수 있어야 한다.

값 타입 공유 참조

  • 기본 String, int같은 기본형 타입이 아닌 임베디드 타입 같은 값 타입을 여러 엔티티에서 공유하면 위험함
  • 부작용(side effect) 발생

임베디드 타입1

해당 주의 할 점에 대해 예시를 들어보자.

Address address = new Address("city", "street", "zipcode");

Member member1 = new Member();
member1.setHomeAddress(address);
member1.setUsername("mho1");
entityManager.persist(member1);

Member member2 = new Member();
member2.setHomeAddress(address);
member2.setUsername("mho2");
entityManager.persist(member2);

전 포스팅에 만든 객체를 다음과 같이 값을 넣어 두개의 rowdata를 넣어보자.

임베디드 타입1

mho1, mho2의 각각의 id를 가지는 데이터가 두줄 생긴것을 확인 할 수 있다.

여기서 아래에 member의 address 데이터를 변경하는 코드를 추가해보자.

member1.getHomeAddress().setCity("newCity");
/* update
    com.dhaudgkr.jpa06.embeded.domain.Member */ update
        Member
    set
        COMPANY_CITY=?,
        COMPANY_STREET=?,
        COMPANY_ZIPCODE=?,
        city=?,
        street=?,
        zipcode=?,
        USERNAME=?,
        endDate=?,
        startDate=?
    where
        MEMBER_ID=?

/* update
    com.dhaudgkr.jpa06.embeded.domain.Member */ update
        Member
    set
        COMPANY_CITY=?,
        COMPANY_STREET=?,
        COMPANY_ZIPCODE=?,
        city=?,
        street=?,
        zipcode=?,
        USERNAME=?,
        endDate=?,
        startDate=?
    where
        MEMBER_ID=?

업데이트가 두번 나가는 것을 볼 수 있다. 또한 결과 값 또한 의도와는 다른 결과를 보인다.

임베디드 타입1

같은 주소 값을 공유하기에 생긴 일이다.

그렇다면 어떻게 해야할까? 방법은 아래에서 확인해 보도록 하자

값 타입 복사

  • 값 타입의 실제 인스턴스인 값을 공유하는 것은 위험
  • 대신 값(인스턴스)를 복사해서 사용

임베디드 타입1

주소값을 복사하므로, 새로운 인스턴스를 만들어서 넣어주면 된다, 한번 예시를 살펴보도록 하자.

Address address = new Address("city", "street", "zipcode");

Member member1 = new Member();
member1.setHomeAddress(address);
member1.setUsername("mho1");
entityManager.persist(member1);

Address copyAddress = new Address(address.getCity(), address.getStreet(), address.getZipcode());

Member member2 = new Member();
member2.setHomeAddress(copyAddress);
member2.setUsername("mho2");
entityManager.persist(member2);

member1.getHomeAddress().setCity("newCity");

address를 copyAddress라는 새로운 객체로 복사해서 member2 Entity에 넣어줬다.

임베디드 타입1

이렇게 mho1에만 데이터가 변경되는 의도한 결과를 확인할 수 있다.

객체 타입의 한계

  • 항상 값을 복사해서 사용하면 공유 참조로 인해 발생하는 부작용을 피할 수 있다.
  • 문제는 임베디드 타입처럼 직접 정의한 값 타입은 자바의 기본 타입이 아니라 객체 타입이다.
  • 자바 기본 타입에 값을 대입하면 값을 복사한다.
  • 객체 타입은 참조 값을 직접 대입하는 것을 막을 방법이 없다.
  • 객체의 공유 참조는 피할 수 없다.

간단한 예시를 들어본다면,

  • 기본 타입 (primitive type)
int a = 10;
int b - a; /* 기본 타입은 값을 복사한다. */
b = 4;
  • 객체 타입 (object type)
Address a = new Address(Old);
Address b = a; /* 객체 타입은 참조를 전달 결국 인스턴스는 하나라는 뜻*/
b. setCity(New)

불변 객체

이러한 한계를 극복하기 위해 객체타입은 불변객체로 제약을 주고 사용 해야한다.

불변객체의 특징

  • 객체 타입을 수정할 수 없게 만들면 부작용을 원천 차단
  • 값 타입은 불변 객체(immutable object)로 설계해야 함
  • 불변 객체: 생성 시점 이후 절대 값을 변경할 수 없는 객체
  • 생성자로만 값을 설정하고 수정자(Setter)를 만들지 않으면 됨
  • 참고: Integer, String은 자바가 제공하는 대표적인 불변 객체

값을 변경할 시?

여러 방법이 있지만, 새로운 인스턴스객체를 만들어 넣어 줘야한다.