프레임워크(Framework)/JPA

[JPA] 값 타입과 불변 객체 - 값 타입 (2)

잇트루 2022. 12. 9. 02:33
반응형
본 내용은 온라인 강의 사이트 인프런의 김영한 님의 강의 내용이 포함되어 있습니다.
'자바 ORM 표준 JPA 프로그래밍 - 기본편'
 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., - 강의 소개 | 인프런

www.inflearn.com

 

Intro

JPA의 데이터 타입을 분류하면 엔티티 타입과 값 타입으로 구분할 수 있다.

 

엔티티 타입

  • @Entity로 정의하는 객체
  • 데이터가 변해도 식별자를 통해 지속해서 추적할 수 있다.
  • 예) 회원 엔티티의 키나 나이 등의 값을 변경해도 식별자로 인식 가능

 

 

값 타입

  • int, Integer, String처럼 단순히 값으로 사용하는 자바 기본 타입이나 객체
  • 식별자가 없고 숫자나 문자 같은 속성만 있어 변경 시 추적이 불가능하다.
  • 예) 숫자 100을 200으로 변경하면 완전히 다른 값으로 대체

 

 

값 타입은 다음 3가지로 나눌 수 있다.

  • 기본값 타입 : 자바 기본 타입, 래퍼 클래스, String
    • 자바에서 제공하는 기본 데이터 타입

 

  • 임베디드 타입(Embedded type, 복합 값 타입) : 좌표, 포지션 등
    • JPA에서 사용자가 직접 정의한 값 타입

 

  • 컬렉션 값 타입(Collection value type) : 기본값 타입이나 임베디드 타입을 저장할 수 있는 것
    • 하나 이상의 값 타입을 저장할 때 사용

 

값 타입과 불변 객체

값 타입은 복잡한 객체를 조금이라도 단순화하려고 만든 개념이다.

따라서, 값 타입은 단순하고 안전하게 다룰 수 있어야 한다.

 

값 타입 공유 참조

  • 임베디드 타입 같은 값 타입을 여러 엔티티에서 공유하면 위험하다.
  • 예상치 못한 곳에서 문제가 발생하는 부작용(side effect) 발생

Member member1 = new Member();
member1.setName("member1");
member1.setHomeAddress(address);
em.persist(member1);

Member member2 = new Member();
member2.setName("member2");
member2.setHomeAddress(address);
em.persist(member2);

// member2의 City를 newCity로
member2.getHomeAddress().setCity("newCity");

이 경우 member2의 주소를 “NewCity”로 변경하길 원했지만, member1의 주소도 같이 변경된다.

  • 영속성 컨텍스트에서는 회원1과 회원2 둘 다 city 속성이 변경된 것으로 판단하여 각각 UPDATE SQL을 실행

 

이처럼 공유 참조로 인해 발생하는 문제는 찾아내기가 어렵다. 이 경우 값을 복사해서 사용해야 한다.

 

값 타입 복사

값 타입의 실제 인스턴스 값을 공유하는 것은 예상치 못한 곳에서 문제가 발생할 수 있다.

따라서, 값(인스턴스)을 복사해서 사용해야 한다.

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

Member member1 = new Member();
member1.setName("member1");
member1.setHomeAddress(address);
em.persist(member1);

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

Member member2 = new Member();
member2.setName("member2");
member2.setHomeAddress(copyAddress);
em.persist(member2);

// member2의 City를 newCity로
member2.getHomeAddress().setCity("newCity");
  • 공유 참조를 하지 않기 위해 해당 참조 객체를 복사해서 사용한다.
    • 공유 참조로 인해 발생하는 부작용을 피할 수 있다.

 

객체 타입의 한계

임베디드 타입처럼 직접 정의한 값 타입은 자바의 기본 타입이 아닌 객체 타입이다.

  • 기본 타입은 값을 대입하면 해당 값을 복사한다.
  • 하지만, 객체 타입은 참조 값을 직접 대입하는 것을 막을 방법이 없다.
  • 객체의 공유 참조는 피할 수 없는 한계가 있다.
// 기본 타입
int a = 10;
int b = a; // 기본 타입은 값을 복사
b = 4; // a = 10, b = 4

// 객체 타입
Address a = new Address("Old");
Address b = a; // 객체 타입은 참조를 전달
b.setCity("New"); // a와 b 모두 New로 변경

 

불변 객체

불변 객체는 생성 시점 이후 절대 값을 변경할 수 없는 객체를 의미한다.

값 타입은 부작용 걱정 없이 사용할 수 있어야 한다. 따라서 객체를 불변하게 만들어 애초에 수정할 수 없도록 할 필요가 있다.

  • 값 타입은 불변 객체(immutable object)로 설계해야 한다.
  • 생성자로만 값을 수정하고 수정자(Setter)를 만들지 않으면 된다.

 

즉, 값 타입을 생성자로만 값을 설정하고, Setter를 선언하지 않는 것으로 불변 객체로 만들 수 있다.

  • 부작용 문제 해결

 

불변 객체의 값 수정

불변 객체의 값을 수정해야 할 경우에는 새로운 객체를 생성하여 필요한 부분만 수정해야 한다.

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

Member member1 = new Member();
member1.setName("member1");
member1.setHomeAddress(address);
em.persist(member1);

// 수정
Address newAddress = new Address("NewCity", address.getStreet(), address.getZipcode());
member1.setHomeAddress(newAddress);
  • 불변이라는 작은 제약으로 부작용이라는 큰 재앙을 막을 수 있다.
반응형