엔티티의 상속? JPA Inheritence

Kim DongKyun·2023년 1월 16일
4

Today I learned

목록 보기
33/43
post-thumbnail

개요

어떤 프로젝트를 만들 때 유저의 권한이 여러 개로 분리된다면, Enum 클래스를 활용하여 Role을 체크하는 식으로 만들면 편합니다. 스프링 시큐리티의 preAuthorize(hasRole = " ~ " ) 기능을 사용해서 인가 처리를 하기도 쉽죠.

그래서 우리는 User라는 하나의 테이블에서 RoleEnum을 사용해서 모든것을 처리했습니다.

그런데, 데이터 테이블의 입장에서 보면 조금 애매해집니다. 예를 들어봅시다.

구조 살펴보기


오늘 프로젝트를 시작하면서 우리는 권한을 세 개로 분리해야 된다는 명세를 전달 받았습니다.

  1. 일반 유저(Buyer)

  2. 판매자 유저(Seller)

  3. 관리자 유저(Admin)

    바로 이전 프로젝트에 사용했던 것처럼 Enum을 사용해서 그 권한을 분리했습니다.

  • 여기서 문제는 user 테이블에 storeName이라는 불필요한 칼럼이 들어간다는 것입니다.

    이 storeName 은 회원가입 할 때는 값을 기입받지 않습니다. 구매자가 판매자로의 권한 상승을 요청하면, 이 storeName 과 함께 별도의 Request를 Body형태로 보내고, 그 Request를 받아서 유저 테이블에있는 "실제 요청을 보낸 유저"를 찾아서 값을 insert 해줘야 합니다.


    무엇이 문제일까?

    다음은 팀 노션에도 공유한 제가 정리한 문제들입니다.

  • User를 자바의 "상속" 으로 처리하는 것이 나은 이유

    1. User테이블 하나로 모든 걸 처리하기엔, null값이 너무 많이 들어갑니다. 이는 DB 공간의 낭비이며, 성능 최적화의 걸림돌이 될 수 있습니다.
    2. 명세와 같이 user의 Role에 따라 기능의 구현이 완전히 달라지게 된다면, 부모 객체(SQL 슈퍼타입)인 User 테이블을 불러오는 것이 아닌 자식 객체(SQL 서브타입)을 불러와서 사용하고, CRUD 하는 것이 더 유리합니다.
  • 그렇다면 어떤 테이블이 바람직할까요?

    1. 참고 해 볼 만한 자료 링크를 남깁니다.
      [SQL] 슈퍼-서브타입 모델의 물리모델 결정기준 (feat. JPA)
      [Spring JPA] 슈퍼-서브타입 관계 모델링 (상속관계 매핑)
    2. 참고 링크를 보시면, 우리가 원하는 구조는 다음과 같습니다.
      슈퍼타입과 각각의 서브타입을 테이블로 생성https://blog.kakaocdn.net/dn/k68Mc/btrqNLbE4U6/vcdEIHnB6Qw8TcUUJ2iZK1/img.png

위와 같이 데이터 테이블의 구조를 만들게 되면 더 좋겠네요! 근데 Spring, JPA로 사용하는 방법은?


JPA로 위와 같은 패턴을 적용하는 방법

위 참조자료에도 나왔는데, JPA의 부모 테이블에 @Inheritence(strategy = InheritenceType."") 와 같이 사용합니다. 직접 볼까요?


위와 같이 어노테이션을 달아줬습니다.

	   User           <부모>
Buyer/Seller/Admin    <자식>

위와 같은 형태가 자연스러우므로, JOINED 를 사용했습니다.

실험적으로 사용한 모습입니다. Entity 에 꼭 필요한 @Id 만드세요~ 라는 컴파일 에러가 뜨지 않는 모습입니다. @ID는 부모객체의 User에서 사용하고, 또한 필드값 또한 부모의 것을 따릅니다.

이 클래스 안에 새로운 필드를 정의하면, 서브타입 테이블로 Seller라는 테이블은 유저의 필드값 + Seller 만의 not null한 필드값을 가지게 될 것입니다.

뭔가 허전하신가요?


@DiscriminatorColumn?

@DiscriminatorColumn 은 구조상 부모가 되는 것이 자명한 엔티티에게 붙여주는 어노테이션 입니다.

@DiscriminatorColumn(name="dtype")이 디폴트로 되어 있지요.

이 친구의 역할은 다음과 같습니다.

어떤 컬럼을 가지고 어떤 자식 엔터티를 판별할 것인가에 대한 힌트를 주는 어노테이션!

즉, 우리가 실제로 사용 할 예제는 User 와 Sellers...등의 테이블이며

이들은 Role에 의해서 구별됩니다. 우리는 이 어노테이션을 다음과 같이 사용 할 수 있을 것입니다.

위처럼 실제 구분의 기준이 되는 role로써 Discrimination 하도록 만들었습니다. 자식 엔티티에도 이에 상응하는 어노테이션을 붙여봅시다.

위와같이 사용했습니다.

수정

-> Dtype을 이미 필드에 정의된 role로 하면 에러가 발생합니다. 디폴트로 변경했습니다.


결론

  1. 슈퍼타입 서브타입의 형태가 자바의 클래스간 상속과 그나마~ 비슷합니다.
  2. 위와 같은 형태를 구현하기 위해서 JPA의 @Inheritance를 사용합니다.
  3. 이와 같은 구현은 "상속"의 구현으로 객체지향적 코드를 만들 수 있습니다.
  4. 더불어 DB에 불필요한 값을 넣지 않아도 되므로(null값들) 경제적입니다.

2개의 댓글

comment-user-thumbnail
2023년 1월 16일

제가 필요로 했던 정보입니다. 정말 정리를 열심히 하셨네요b

1개의 답글