[Spring] MapsId로 일대일 매핑하기

easyone·2026년 6월 7일

Spring

목록 보기
21/25

배경

User마다 한개씩만 생성되어야 하는 엔티티들이 있다.
기존에는 기본키를 따로 두고 user를 FK로 두는 방식으로 진행했다.

그렇지만 기본키를 많이 안쓰고 findByUserId 식의 메서드를 많이 사용한다면 컬럼 방비라고 생각하여 전략을 변경해보게 되었다.

전략

기존 방식은 다음과 같다.

@OneToOne + id + user_id

  @Entity
  @Table(name = "user_notification_settings")
  class UserNotificationSetting(

	  // 자체 PK
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      @Column(name = "notification_setting_id")
      val id: Long? = null,                           

	  // user FK
      @OneToOne(fetch = FetchType.LAZY, optional = false)
      @JoinColumn(name = "user_id", nullable = false, unique = true)
      @OnDelete(action = OnDeleteAction.CASCADE)
      val user: User,                                

      // ...
  )
  • user_id를 unique 제약 조건으로 두면서도 불필요한 notification_setting_id가 존재하는 방식이다.

@MapsId를 사용해서 개선하고자 한다.

MapsId

  • 부모 엔티티의 PK를 자식 엔티티의 PK로 공유하는 패턴이다.
  • 즉 별도의 id 컬럼을 생성하지 않고 외래 키가 곧 기본키가 된다.

부모 - User

  @Entity
  @Table(name = "users")
  class User(
      @Id
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      @Column(name = "user_id")
      val id: Long? = null,
      // ...
  )

자식 - UserNotificationSetting

  • 신규 저장 시 DB가 user_id를 자동 생성한다.
  • user에서 가져온 PK를 @Id 필드에서 자동으로 채우는 방식이다.
  • DB 레벨에서 User 삭제 시 해당 엔티티도 자동 삭제된다.

  @Entity
  @Table(name = "user_notification_settings")
  class UserNotificationSetting(

	  // 
      @MapsId
      @OneToOne(fetch = FetchType.LAZY, optional = false)
      @JoinColumn(name = "user_id", nullable = false)
      @OnDelete(action = OnDeleteAction.CASCADE)
      val user: User,

      @Id
      @Column(name = "user_id")
      val userId: Long? = null,                        

      // ...
  )

저장 방법은 다음과 같다.

  // UserAccountRegistrar.kt
  // user를 먼저 저장
  val user = userRepository.save(buildUser())   
  // MapsId가 user.id를 읽어서 user_notification_settings.user_id에 자동 세팅 
  notificationSettingRepository.save(
      UserNotificationSetting(user = user)            
  )
  // @MapsId가 user.id를 읽어서 user_notification_settings.user_id에
  자동 세팅
  • user의 자식이기 때문에 user가 null인 상태에서는 절대 저장할 수 없다.

결론적으로 @MapsId 방식에서는 컬럼이 하나다.
user_id가 PK면서 동시에 FK이기 때문이다.

일대일 매핑을 할 때 무지성 중간테이블 기본키 + 외래키 해서 추가 컬럼을 만들기보다는 이런 식으로 하면 컬럼 낭비를 줄일 수 있을 것 같다.

profile
백엔드 개발자 지망 대학생

0개의 댓글