
프로젝트가 끝나고 평화로운 멘토링 시간
"실무에선 외래키를 사용하지 않습니다."
"예??"
그날 베짱이의 DB 세상은 무너졌다
이번 포스팅은 시간의 흐름에 따른 DB 관점과, 잘못 알고 있었던 jpa 개념까지 간단하게 정리합니다.
과거에는 데이터 무결성을 온전히 DB에 위임하던 시절이 있었습니다.
유일성, 존재 유무, 참조 무결성 등 모든 제약을 데이터베이스에서 체크하고, 이를 어기는 입력은 아예 불가능하게 하죠.
하지만 시간이 지나며 시스템 규모가 커지면서 단일 애플리케이션 구조에서 벗어나, 마이크로서비스나 도메인 중심 설계 같은 분산 아키텍처가 도입되었고,
이로 인해 DB에 제약을 걸기보다 애플리케이션 계층에서 무결성과 제약을 검증하는 방식이 일반화되고 있습니다.
다시 말해,
과거에는 DB에 더 많은 책임을 주는 설계가 일반적이었고, 외래키나 UNIQUE 제약을 많이 활용했습니다.
하지만 지금은 애플리케이션 계층에서 먼저 존재 여부나 중복 여부를 확인하고,
DB는 마지막 방어선 또는 백업 수단 정도로만 활용하는 방향으로 흐름이 바뀌었습니다.
외래키는 부모 테이블에 존재하지 않는 값을 막아주는 역할을 하지만,
실무에서는 대부분 애플리케이션(서비스 계층)에서 먼저 존재 여부를 확인하고 예외를 처리합니다.
왜냐하면 DB가 던지는 에러는 사용자 친화적이지 않고,
비즈니스 흐름에 맞는 조건부 로직도 DB에서는 처리할 수 없기 때문입니다.
결국 외래키는 “있어도 직접 검증해야 한다”라는 점에서 중복된 비용이 발생할 수 있습니다.
DB의 ON DELETE CASCADE는 부모를 삭제하면 자식도 자동 삭제하는 기능이지만,
실무에서는 바로 삭제하는 경우가 드뭅니다.
"회원 탈퇴 시 리뷰도 삭제해야 한다"라는 요구가 있어도,
이처럼 비즈니스 조건이 개입되기 때문에,
자동 삭제보다는 서비스 계층에서 직접 명시적으로 처리합니다.
운영 환경에서 스키마 변경(ALTER TABLE)을 해야 할 경우,
외래키가 있는 테이블은 작업이 느려지거나 제한될 수 있습니다.
수천만~억 단위의 row를 가진 테이블에서는
ALTER 한 번에 수 시간이 걸리고, 그동안 락이 발생할 수도 있습니다.
즉, 데이터가 늘어날수록 외래키가 운영 측면에서 여러 제약이 발생합니다.
사실 이 게시글을 작성하게 된 이유입니다.
JPA를 학습하면서 가장 큰 장점이라고 느꼈던 점은,
엔티티 간의 관계를 객체처럼 탐색할 수 있다는 점, 즉 자바의 객체지향과 잘 어우러지는 ORM 특성이었습니다.
그래서 저는 자연스럽게 다음과 같이 생각했습니다.
완전 잘 못 이해하고 있었습니다.
fk를 사용하지 않는다고 해서 jpa 연관관계를 사용하지 못하는 게 아닙니다.
외래키를 사용하지 않더라도,
JPA는 @ManyToOne, @OneToMany, @JoinColumn 등으로
연관관계를 설정하고 객체 탐색이 가능합니다.
@Entity
public class Review {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
}
review.getUser().getNickname() //객체 탐색도 잘 됩니다.
Hibernate는 이 어노테이션을 통해 SQL JOIN 절에서 사용할 컬럼을 지정할 뿐이며,
DDL을 자동 생성할 때에만 FK 제약조건도 함께 생성합니다.
SELECT r.*, u.*
FROM reviews r
LEFT JOIN users u ON r.user_id = u.id
그럼 이 두가지 경우가 모두 가능합니다.
//방식 1 - ID만 갖는 경우
@Column(nullable = false)
private UUID userId;
//방식 2 - 객체를 갖는 경우
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
여기서 명확하게 인지해야할 부분은
외래키 제약조건을 없앤다는 건 정합성 책임을 DB가 아닌 애플리케이션이 진다는 뜻입니다.
개발자가 insert/update 전에 존재 여부를 확인하거나, 삭제 시 관련 엔티티를 직접 관리해야 합니다.
외래키가 없어도 JPA의 연관관계는 객체지향적으로 잘 동작합니다.
중요한 것은 외래키 존재 여부보다, 설계 목적과 상황에 따라 적절한 방식을 선택하는 것입니다.
세상이 무너지면 또 작성하러 오겠습니다.
세상이 자꾸 무너지면 성장하시겠군요!!