코딩스토리

값 타입 본문

Spring/JPA

값 타입

kimtaehyun98 2022. 5. 9. 00:09

# 해당 포스팅은 인프런 김영한 강사님의 "자바 ORM 표준 JPA 프로그래밍 - 기본편" 강의 및 교재를 참고하여 작성한 글입니다. 

Entity와 값 타입

 

Entitiy Type

- Entitiy로 정의하는 객체

- 데이터가 변해도 식별자로 지속해서 추적 가능

- 즉 회원 엔티티(객체) 내부의 값들을 변경하여도 식별자(id 값)를 통해 추적이 가능하다

 

값 타입

- int, String 같이 단순한 값으로 사용하는 자바 기본 타입이나 객체

- 식별자가 없음

- 값 타입의 생명주기 = entity에 의존

- 공유되면 안 됨

 

내 기준 말 그대로 Entity가 아닌 것들을 값 타입이라고 이해하면 편한 것 같다.

 

값 타입들

 

Embedded type - x, y 좌표처럼 묶어서 사용하고 싶을 때 position이란 class 만들고 사용

 

컬렉션 값 : 자바 컬렉션에다가 기본 값들 넣는 것 (ex. ArrayList)

 

 

int, double 같은 java의 primitive type은 알아서 공유 금지가 되어 있음

ex) int a = 10; int b = a; 했을 때 값이 복제만 되어서 넘어가는 것이다

 

Integer 같은 래퍼 클래스나 String 같은 특수 클래스는 공유 가능하지만 변경이 불가능함

ex) Integer a = new Integer(10); Integer b = a; => b와 a는 같은 주소 공간을 가리킨다

 

 

임베디드 타입

 

아래와 같은 멤버 클래스가 있다고 하자. 

그리고 아래의 클래스 코드를 확인해보자.

 

즉 원래라면 위의 사진과 같이 클래스를 구성해야 했었다면 Embedded type을 사용하면 아래와 같이 표현할 수 있다.

 

훨씬 깔끔하고 직관적이다.

 

그리고 가장 중요한 점인 객체지향스럽다.

 

이렇게 구현하게 된다면 임베디드 클래스에 필요한 메서드를 만드는 등 여러 면에서 객체지향스러운 활동을 할 수 있다.

 

여기서 추가적으로 기억해야 될 점은 Embeddable 클래스에는 @Entity를 붙이면 안 된다.

(당연한 거겠죠? 값 타입이니까)

 

사실 나가는 query는 같다. 즉 임베디드 타입을 사용하거나 안 하거나 매핑하는 테이블을 같다는 것이다.

 

만약 homeAddress와 workAddress 두 개를 똑같은 Address 클래스로 사용하고 싶다면 @AttributeOverride를 통해 각 컬럼들을 새롭게 매핑해서 사용하면 되긴 하지만 개인적으로 코드를 봤을 때 좋진 않은 것 같다..

 

값 타입 Side Effect

 

이제 다음 코드를 보자.

 

member1과 member2를 만들고 persist를 통해 DB에 저장했다.

 

이 부분에서 문제는 무엇일까?

 

사실 이 코드만 놓고 보면 문제는 발생하지 않을 것이다.

하지만 만약 아래와 같은 코드가 다른 계층, 즉 Service 쪽 코드에서 호출되었다고 생각해보자.

 

address.setCity("경기도")

 

그리고 persist()를 하는 순간 member1과 member2의 주소가 동시에 바뀌게 된다.

 

(실제 이런 경우 오류를 발견하기 어렵다고 한다.

내가 생각해도 내가 아닌 다른 사람이 짠, 다른 계층에 있는 코드 때문에 문제가 발생하면 진짜 매우 화날 것 같다ㅎㅎ..)

 

따라서 이런 경우에는 각 address를 생성자 호출을 통해 생성해주면 해결할 수 있다.

 

왜 이런 값 타입 side effect를 생각해줘야 되는 걸까?

 

JPA의 특징 때문이니까.

객체 지향, 즉 객체를 사용하니까 참조하니까.

 

그리고. 객체 타입은 복사가 아니라 참조를 전달할 수 있으니까.

 

(물론 위와 같은 경우에는 각 address를 생성자 호출을 통해 생성해주면 해결할 수도 있다.)

 

 

불변 객체

 

위에서 발생했던 Side effect를 해결하는 방법보다는 처음부터 안 만드는 게 가장 좋은 방법일 것이다.

 

이렇게 예상하지 못했던 부분에서 발생하는 Side Effect들을 방지하기 위해 불변 객체를 사용할 수 있다.

 

이름만 불변 객체이고 따로 타입이 존재하는 것이 아니다.

생성하는 방법은 Setter 메서드를 없애주면 된다. (private으로 만들어도 됨)

 

???

 

이론적인 부분 같고 별 것 없어 보이지만 때로는 가장 심플한 것이 베스트일 때가 있다.

이 경우가 그런 것 아닐까..?

 

값 타입 컬렉션

내가 DB를 배울 때 가졌던 의문점 중 하나가 바로 RDB에서 LIST를 저장하고 관리하는 방법이었다.

 

JDBC Template에서는 query로 할 건지 queryForObject로 할 건지에 따라 다르다.

 

JPA에서는 @ElementCollection, @CollectionTable을 사용하면 해결된다!

 

이는 JAVA의 어떠한 컬렉션을 사용해도 된다.

 

아래의 ERD를 보자.

DB에는 컬렉션을 같은 테이블에 저장할 수 있는 방법이 없다.

(List를 그대로 저장하고 있는 DB의 테이블은 없으니까. 우리는 이를 join으로 풀어내는 거고)

 

따라서 컬렉션을 저장하기 위해서는 별도의 테이블이 필요하다.

(풀어서 말하자면 Join 할 테이블이 필요하다는 것이다.)

 

코드는 아래와 같다.

favoriteFoods는 Member 테이블과 FOOD_NAME 테이블이 memberID를 JoinCondition으로 잡고 Join 한 결과를 Set 형태로 저장하고 있는 객체이다.

 

addressHistory는 Member 테이블과 ADDRESS 테이블이 memberID를 JoinCondition으로 잡고 Join 한 결과를 List 형태로 저장하고 있는 객체이다.

 

그럼 이렇게 Entity를 잘 설계했다면 다음과 같은 코딩이 가능하다.

별표 친 부분과 같이 ArrayList에 add 하는 코드로 DB의 테이블에 알아서 저장이 된다.

 

심지어 Cascading을 자체적으로 필수로 가지기 때문에 update시에도 걱정할 필요가 없다.

(물론 update시에 수정이 불가하고 모든 것을 갈아 끼우는 형식으로 개발해야 되지만 이 정도야 애교 아닐까 싶네요..🤣)

 

그럼 단점은 정말 하나도 없는 걸까?

 

아래의 코드와 쿼리를 보자.

 

먼저 해당 코드는 Address에 새로운 데이터 "a"와 "b"를 넣고, 그중 "a"를 삭제하고 "c"를 넣는 코드이다.

 

중요한 점이 있는데 remove시에 저렇게 같은 객체를 생성해서 넣어줘야 된다는 점이다.

또한 저렇게만 해서는 지우고 싶은 객체를 삭제하지 못한다.

 

왜냐하면 컬렉션에서는 remove시 비교를 대부분 == 을 통해서 하는데 ==은 객체끼리는 성립하지 않기 때문이다.

따라서 == override를 해줘야 한다.

 

이해하기 어렵진 않으나 뭔가 Override를 배울 때 힘들었던 기억 때문인지 거부감이 든다 😐

 

이렇게 해서 코드를 실행시켜보면 아래와 같은 쿼리가 나간다.

 

 

먼저 delete query가 한번 나간 뒤 다시 Insert 쿼리를 두 번 보낸다.

 

으잉? 🙄🤔

 

왜 Insert 쿼리가 두 번 나가지??

 

이유는 이런 경우 table에 있는 모든 데이터를 삭제한 후 다시 전부 insert 한다고 한다.

 

그렇기 때문에 insert 쿼리가 두 번 나가게 된 것이다.

 

현재는 두 개라 상관없지만 만약 수 백, 수 천, 수 만개의 데이터가 테이블에 있었다고 하면 정말 끔찍한 상황이 발생할 수 있다.

 

이런 상황이 발생하는 이유는 값 타입 컬렉션의 제약사항 때문이다.

왜냐하면 값 타입은 엔티티와 다르게 식별자 개념이 없기 때문에 값을 변경 시 추적이 어렵다.

 

따라서 전부 삭제 후 다시 전부 삽입이란 비효율적인 방법을 선택할 수밖에 없다.

 

이러한 이유 때문에 실제로 실무에서는 일대다 연관관계로 테이블을 만들어서 풀어나간다고 한다.

 

결론적으론... 정말 단순하거나 식별자를 찾을 필요가 없는 수준이 아니라면 웬만하면 연관 관계로 풀어나가는 게 맞다고 한다.

 

살짝 허무한 감도 없지 않아 있지만 다시 한번 연관관계 쪽을 공부해야겠다고 생각하게 된 좋은 계기가 된 것 같다.

 

또 공부하면 할수록 왜 JPA를 사용해야 되는지, 그리고 JPA가 얼마나 의미 있는 기술인지 알아가고 있는 것 같다.

 

'Spring > JPA' 카테고리의 다른 글

프록시를 사용한 즉시 로딩, 지연 로딩  (0) 2022.04.28
영속성 컨텍스트  (0) 2022.03.14
Comments