Skip to content

[DB] VER2 설계에 대한 고찰 , 오류 인정, 개선 방향

이은비 edited this page Oct 29, 2023 · 1 revision

배경

명식이 버전2 업데이를 하며 "학교 인근 가게 스크랩" 기능을 추가 개발하였습니다. 이 과정에서 테이블 설계를 다음과 같이 하였습니다.

CREATE TABLE Scrap (
    id INT PRIMARY KEY,
    user_id INT,
    store_id INT,
    FOREIGN KEY (user_id) REFERENCES User(id),
    FOREIGN KEY (store_id) REFERENCES Store(id)
);

사용자와 가게를 링크하는 다대다 관계 테이블이며, 당시에는 비식별 관계로 정의함으로써 스크랩 등록/해제를 고유 ID로 관리하는 것이 편리할 것이라 생각했습니다.

변경 이유

스크랩을 하는 행위는 한 명의 유저가 한 개의 가게를 리스트에 저장하고 나중에 쉽게 찾고 타인과 공유하기 위한 목적의 기능입니다.

본 서비스에서도 스크랩은 동일한 목적을 가진 기능입니다.

동일한 가게를 여러 번 스크랩하는 행위는 발생할 수 없으며 ( 논리적으로 ), 스크랩과 스크랩 해제만을 수행합니다.

따라서 유저와 가게 정보를 PK로 지정하여 다대다의 링크 테이블의 기능만을 하는 것이 스크랩의 본 목적과도 일치한다고 생각했습니다.

변경 과정

다대다 링크 테이블을 식별 관계로 정의하기 위해서는 다음과 같이 IdClass가 필요합니다.

public class MarkId implements Serializable { // Serializable를 반드시 구현해야 합니다.

  private Long user;
  private Long store;

  public MarkId(){}
  public MarkId(Long user, Long store) {
    super();
    this.user = user;
    this.store = store;
  }
}

이후 실제 StoreMark 테이블에서 다음과 같이 정의합니다.

@Entity
@Table(name = "mark")
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
@IdClass(MarkId.class) // IdClass 지정
public class StoreMark implements Serializable { // Serializable 구현.

  @Id
  @ManyToOne
  @JoinColumn(name = "user_id")
  private User user;

  @Id
  @ManyToOne
  @JoinColumn(name = "store_id")
  private Store store;

  public StoreMark StoreMark(
      User user,
      Store store
  ) {
    this.user = user;
    this.store = store;
    return this;
  }

이렇게 정의하면 테이블에는 다음과 같이 데이터가 적재됩니다.

image

자세한 변경 이력을 확인하고 싶으시면, 여기를 참고해주세요.

회고

초기 개발 당시, 식별/비식별 관계를 충분히 고려하지 않고 개발 편리성을 생각하며 비식별관계 위주의 설계를 했습니다.

이번 리펙토링 과정을 겪으며, 기능이 잘 돌아간다고 하더라도 안주하지 않고 설계를 되돌아보는 시간이 필요함을 경험했습니다.

모든 설계가 처음부터 완벽할 수 없지만, 요구사항에 적합한 설계인지 끊임없이 의심하고 고민하는 자세가 필요하다고 생각합니다.!

설계 변경에 관해 함께 고민해주신 @규범님, @성식님 감사합니다 😊