FK를 사용하느냐 하지 않느냐에 대한 고민

August 01, 2024


IT 연합 동아리인 DND 11기 프로젝트를 진행하면서, 테이블 스키마를 작성해야 했습니다.

같은 기수중 한 분이 외래키를 실무에서는 잘 사용하지 않는 경우가 있다고 들었습니다.

데이터베이스 강의에서는 RDB를 사용한다면, FK가 중요한 기능이라고 생각했는데, 왜 실무에서는 사용하지 않는 경우도 있는지 정리해보았습니다.

외래 키(FK)를 사용하지 않는 이유

데이터베이스 설계 시 외래 키(Foreign Key, FK)를 사용하면 데이터의 무결성과 일관성을 유지하는 데 큰 도움이 됩니다.

그럼에도 불구하고 일부 실무 개발자나 조직은 FK를 사용하지 않기로 선택하는 경우가 있다고 합니다.

성능 문제

  • 대규모 서비스의 경우, 행의 개수가 몇천만 ~ 억개가 넘어서 FK를 걸면 성능이 나빠질 수 있다고 합니다.
  • 연합 동아리 프로젝트이므로, 대규모 서비스까지 고려할 필요는 없고 성능 지표를 직접 측정해보진 않았으므로 성능 때문에 FK를 없애기엔 FK가 주는 안정성이 더 좋다고 생각합니다.

복잡한 마이그레이션

  • 외래 키가 존재하면 마이그레이션 과정이 복잡해질 수 있다고 합니다.
  • 이건 제가 수동으로 디비를 옮겨보면서 느낀 점이었습니다. 상품, 상품 카테고리 테이블이 있다고 할때, 상품 카테고리를 먼저 넣어야지 새로운 상품 행을 추가할 수 있습니다.

정리하자면, 귀찮아서 라고 생각합니다.

그럼에도 일부 컬럼에 대해 FK를 사용하지 않았는데요, 성능이나 마이그레이션 관점을 떠나 생명주기 관점에서 사용 유무를 정했습니다.

외래 키를 언제 쓰지?

프로젝트를 진행하며 생명주기에 따라 FK를 사용할지 말지 정하기로 했습니다.

예를 들어, member, badge, badge_achievement(배지 달성 정보) 3개의 테이블이 있다고 하겠습니다.

개인정보를 위해 member 테이블 행을 삭제하면 모든 관련 개인정보를 삭제해야 한다고 가정하겠습니다. (soft delete X)

따라서 member 행이 삭제되면, member가 보유중인 배지 정보(badge_achievement)도 삭제해야 합니다.

그렇기 때문에, member와 badge_achievement는 FK로 맺어야 member만 삭제하는 경우를 막을 수 있습니다.

JPA에서 FK 없이 지연 로딩하는 법

다음 Comment 엔티티를 예제로 살펴보겠습니다.

FK를 걸지 않기 위해서는 NO_CONSTRAINT를 사용하면 됩니다.

public class Comment {
    @ManyToOne(fetch = LAZY)
    @JoinColumn(nullable = false, foreignKey = @ForeignKey(NO_CONSTRAINT))
    private Post post;
}

주의할 점

주의할 점으로는, 해당 post table에 해당 post row가 없는 경우에서 Comment 객체를 가져온다면, JpaObjectRetrievalFailureException 에러가 발생합니다.

  • 위의 예외를 무시하고 싶다면, post에 @NotFound(action = NotFoundAction.IGNORE)를 사용할 수 있습니다.
  • 하지만 @ManyToOne, @OneToOne으로 끝나는 연관관계 매핑과 @NotFound(IGNORE)을 함께 사용한다면, Lazy가 아닌 Eager 로딩을 합니다.

entity uses both @NotFound(action = NotFoundAction.IGNORE) and FetchType.LAZY. The NotFoundAction.IGNORE @ManyToOne and @OneToOne associations are always fetched eagerly.

Lazy로 설정했음에도 Eager 로딩을 하는 이유

  • ~ToOne 연관 관계에서 지연 로딩을 사용할 때, Post 객체는 프록시로 래핑됩니다.
  • 그러나 Post가 존재하지 않는 경우 null이 반환되며, 프록시는 null을 처리할 수 없습니다. 따라서, JPA에서는 이러한 문제를 방지하기 위해 지연 로딩 대신 즉시 로딩을 사용합니다.

Profile picture

이재원

이해하기 쉬운 코드를 작성하려 고민합니다.


© 2024 Won's blog Built with Gatsby