프로그래머스 백엔드 부트캠프에서 JPA를 사용한 프로젝트를 진행할 때 였다.
JPA의 @ManyToOne으로 참조하는 릴레이션 매핑 기능을 사용해서 엔티티를 설계했었는데
스프링 크론 스케줄러를 테스트 하기위해 개발 DB의 데이터를 직접 delete하는 컨트롤을 하려다가 constraint 제약조건 위반 에러가 발생해서
핵심 엔티티의 데이터를 삭제하기 위해 User 테이블, Auth 테이블까지 직접 다뤄야했었다. (굉장히 번거로웠고 스트레스였다)
그러다 어느날 백엔드 커뮤니티에서 FK 주제로 불타오르는 걸 봤다.
백엔드 실무자들로 보이는 사람들이 실무에서는 '성능'이랑 '관리'측면을 들어서 'FK 안쓴다' 라고 이야기를 나누고 있었다.
"db에서 성능을 위해서 FK 안쓰는게 답임"
"우리도 안 쓰는데 마이그레이션할 때 머리 터짐"
"락도 락인데 관리가 겁나 귀찮아짐"
"모르면 외워 실제로 제약조건 안걸고 앱단에서만 거는거야"
"은행 같은곳 아니면 fk 안 쓰는게 정석이여"
실제로 최근에 면접스터디에서 실무에선 fk 제약조건을 안쓴다는 이야기를 막연히 들었는데 실제로 이 논쟁을 보게되니까
RDBMS는 데이터 정합성이 중요한 가치중 하나 아니었나? 이런 관행?이 왜 생긴건지 궁금해졌다.
처음에 사람들이 막연히 'FK'를 안쓴다 라고 해서 뭐.. Posts 테이블에 user_id 같은 칼럼을 안 쓴다는 말인가? 하고 당황스러웠다.
그런데 조금 더 살펴보니 핵심은 그게 아니었고 어느 유저가
"FK제약 안건다는 소리를 FK가 없다는 소리로 받아들이네 ㅉㅉ"
라고 지적한 것을 보고 확실히 방향성을 잡을 수 있게 됐다. 용어 구분이 필요했다.
post.user_id 처럼 다른 테이블의 PK를 저장하기 위한 칼럼 그 자체 (이건 엔티티 관계를 위해 당연히 사용)FOREIGN KEY ~ REFERENCES ~ 처럼 DB 물리 참조 무결성을 강제하는 규칙다른 유저도
"명시적으로 거는 거: 물리 fk
앱단에서 제어하는 거: 논리적 fk"
라고 정리를 했었다.
결국 논쟁은 FK 제약조건을 DB에 거느냐 아니면 데이터정합성을 애플리케이션 레벨에서 코드로 처리하느냐 하는 문제였다.
커뮤니티의 주장들을 종합해보면 "외래키 FK 제약조건을 피한다"는 관행은 현실적인 한계와 공수 때문에 생긴 거로 보인다.
"인덱스가 없으면 테이블락이 걸리는게 문제긴 함"
"모델링 상에서만 fk걸고 실제 테이블 제약조건엔 안검 ㅇㅇ 성능 넘 구려져서"
(하지만 MySQL InnoDB는 FK 생성 할 때 자동으로 인덱스를 생성하고 PostgresQL에는 테이블락이 적용되는 등 DB 회사별로 다르게 적용될 수 있는 이야기이긴 하다)
"급하게 수정이나 삭제 필요할때 fk때문에 실행오류 걸리면 영향 분석만 한세월임"
"마이그레이션할 때 머리터짐"
"계정 삭제하려면 댓글, 게시글 전부 삭제하거나 null 처리해야함"
이런 성능 리스크와 관리 공수 때문에 많은 실무 환경에서 데이터정합성이라는 책임을 DB가 아니라 애플리케이션에게 넘기는 관행을 선택한 것으로 생각된다.
이제 왜 실무에서 관행적으로 물리적 fk 제약조건을 안거는 곳이 많은지 이해가 간다.
근데 그렇다면 나는 @ManyToOne을 썼을 뿐인데 왜 물리적 제약조건이 걸려서 날 귀찮게 했을까?
정답은 애플리케이션의 ddl-auto 설정 때문이었다.
@ManyToOne 어노테이션 자체는 DB가 아닌 JPA 자바 객체를 위한 '논리적인' 매핑 선언인데
이 논리적 매핑이 어떻게 실제 DB의 '물리적 제약조건'이 되는지는 ddl-auto옵션이 결정하는 것이었다.
@ManyToOne 같은 논리적 매핑을 읽어서 FK 제약조건 DDL을 자동으로 실행한다. 내 테스트 과정에서 불편함의 원인이 바로 이것이었다none을 사용. Hibernate가 DDL을 실행하지도 않는다실무에서는 JPA를 사용한다면 ddl-auto: none으로 옵션을 설정하고 Flyway같은 마이그레이션 툴로 DDL을 수동 관리한다고 한다.
바로 여기서 위에서 이야기 했듯이 성능과 관리를 위해서 의도적으로 DDL을 빼기로 합의할 수 있다.
"RDB 쓰는 이유가 데이터 무결성인데 FK를 안 쓰면 NoSQL쓰지 RDBMS 왜 쓰냐?"
라는 댓글이 있었다.
확실히 타당한 이유라고 생각이 들고 몇 가지 의문과 우려가 있기도 하다.
댓글대로, 그리고 애플리케이션에서만 논리적으로 FK 정합성을 다루게 된다면
즉 애플리케이션 로직이 완벽하지 않을 수도 있다.
그렇지만 이 우려를 제외하고서도 RDBMS를 쓰는 이유는 다양하다.
저 댓글을 다신 분은 이 요소들을 간과한 것 같다는 생각이 든다. (아니면 정합성 관점에서만 집중하신 내용이라거나)
확실히 커뮤니티의 여론을 보면 (특히 다양한 트래픽을 다루는 웹 서비스에서는) 성능과 유연성을 위해서 실무에서 외래키 FK 제약조건은 대체로 포기하는 것이 일반적인 관행처럼 보인다.
그러나 이 관행이 생긴 이유 중 하나였던 '테이블 락' 으로 인한 성능 저하 우려는 '인덱스가 없는' FK 칼럼이 원인이었다.
커뮤니티에서 보게된 정희락님의 FK 제약조건 기술 자료에 따르면 FK 칼럼에 인덱스를 생성하는 것으로 테이블락을 방지하고 동시성도 확보 할 수 있다고 한다.
물론 인덱스를 걸어도 FK 검증을 위한 부모 조회 병목, 공유 락 경합, 인덱스 갱신 비용 등의 근본적인 성능 한계와 관리 공수 라는 현실적인 한계는 여전히 존재한다.
정리해보자면 관행을 맹목적으로 따르기보다는 그 관행이 생긴 이유를 이해하고 해결책을 알고 있는 것이 중요하다고 본다.
결국 트레이드오프인 것 같은데 데이터 정합성이 매우 중요한 금융권 같은 도메인에서는 'FK 제약조건 + 인덱스'를 적용하고 고성능 매니퓰레이션이 필요한 테이블에서는 '예외적으로 하는 것'이 더 성숙한 설계가 아닐까...
나도 실제 트래픽이 있는 실무환경을 경험해 보지 못한 것은 맞기 때문에 현실적인 제약을 경험해보지 못한 개발자가 떠드는 허울만 좋은 건방진 이야기 일 수 있다.
이해는 하고 있되 경험을 통해 체화가 필요해보인다.
그런이유로 안쓴다면 다행이지만
대부분은....