짧은 기록 #2 - 실무에선 외래키 FK 제약조건을 안 쓴다?

Soohyeok Kim·2025년 10월 22일

짧은 기록

목록 보기
2/2
post-thumbnail

프로그래머스 백엔드 부트캠프에서 JPA를 사용한 프로젝트를 진행할 때 였다.

JPA의 @ManyToOne으로 참조하는 릴레이션 매핑 기능을 사용해서 엔티티를 설계했었는데

스프링 크론 스케줄러를 테스트 하기위해 개발 DB의 데이터를 직접 delete하는 컨트롤을 하려다가 constraint 제약조건 위반 에러가 발생해서

핵심 엔티티의 데이터를 삭제하기 위해 User 테이블, Auth 테이블까지 직접 다뤄야했었다. (굉장히 번거로웠고 스트레스였다)

그러다 어느날 백엔드 커뮤니티에서 FK 주제로 불타오르는 걸 봤다.

백엔드 실무자들로 보이는 사람들이 실무에서는 '성능'이랑 '관리'측면을 들어서 'FK 안쓴다' 라고 이야기를 나누고 있었다.

"db에서 성능을 위해서 FK 안쓰는게 답임"
"우리도 안 쓰는데 마이그레이션할 때 머리 터짐"
"락도 락인데 관리가 겁나 귀찮아짐"
"모르면 외워 실제로 제약조건 안걸고 앱단에서만 거는거야"
"은행 같은곳 아니면 fk 안 쓰는게 정석이여"

실제로 최근에 면접스터디에서 실무에선 fk 제약조건을 안쓴다는 이야기를 막연히 들었는데 실제로 이 논쟁을 보게되니까

RDBMS는 데이터 정합성이 중요한 가치중 하나 아니었나? 이런 관행?이 왜 생긴건지 궁금해졌다.

FK를 안쓴다??

처음에 사람들이 막연히 'FK'를 안쓴다 라고 해서 뭐.. Posts 테이블에 user_id 같은 칼럼을 안 쓴다는 말인가? 하고 당황스러웠다.

그런데 조금 더 살펴보니 핵심은 그게 아니었고 어느 유저가

"FK제약 안건다는 소리를 FK가 없다는 소리로 받아들이네 ㅉㅉ"

라고 지적한 것을 보고 확실히 방향성을 잡을 수 있게 됐다. 용어 구분이 필요했다.

  • FK(칼럼): post.user_id 처럼 다른 테이블의 PK를 저장하기 위한 칼럼 그 자체 (이건 엔티티 관계를 위해 당연히 사용)
  • FK 제약조건(Constraint): FOREIGN KEY ~ REFERENCES ~ 처럼 DB 물리 참조 무결성을 강제하는 규칙

다른 유저도
"명시적으로 거는 거: 물리 fk
앱단에서 제어하는 거: 논리적 fk"
라고 정리를 했었다.

결국 논쟁은 FK 제약조건을 DB에 거느냐 아니면 데이터정합성을 애플리케이션 레벨에서 코드로 처리하느냐 하는 문제였다.

이 관행은 왜 생겼을까? (현실적인 한계?)

커뮤니티의 주장들을 종합해보면 "외래키 FK 제약조건을 피한다"는 관행은 현실적인 한계와 공수 때문에 생긴 거로 보인다.

성능 (테이블 락, 공유 락)

"인덱스가 없으면 테이블락이 걸리는게 문제긴 함"
"모델링 상에서만 fk걸고 실제 테이블 제약조건엔 안검 ㅇㅇ 성능 넘 구려져서"

공유 락

  • 자식 테이블에 INSERT, UPDATE 할 때, DB가 매번 이 FK가 유효한지 부모 테이블을 조회하는데 이 '검증'자체가 I/O 병목을 유발함
    • (1) post.user_id = 123 을 INSERT한다고 쳤을 때
      (2) 부모 테이블에서 id=123 존재를 확인해야 함
      (3) DB가 부모 테이블을 조회하기 위해서 공유 락을 걸어버림 (부모 레코드에 UPDATE 하려는등의 트랜잭션들을 막기 위함)
      => 락 경합, 데드락 가능성 증가

테이블 락

  • FK 칼럼에 인덱스가 없으면 부모 데이터를 DELETE할 때, DB는 부모 테이블 전체에 테이블락을 걸어버리고 서비스 전체의 동시성을 저하 시킴
    • (1) DB가 이 데이터를 참조하는 자식 테이블의 행이 있는지 확인하는데
      (2) 만약 FK 칼럼에 인덱스가 없다면
      (3) DB는 자식 테이블의 전체를 Full Table Scan을 때려버림
      (4) 그 동안 다른 트랜잭션이 해당 자식 테이블에 데이터를 INSERT하는 것을 막기 위해서
      (5) DB는 안전할 수 있도록 테이블 전체 락을 걸어버림
      => 락으로 인한 동시성 저하

(하지만 MySQL InnoDB는 FK 생성 할 때 자동으로 인덱스를 생성하고 PostgresQL에는 테이블락이 적용되는 등 DB 회사별로 다르게 적용될 수 있는 이야기이긴 하다)

관리 지옥 (운영, 개발)

"급하게 수정이나 삭제 필요할때 fk때문에 실행오류 걸리면 영향 분석만 한세월임"
"마이그레이션할 때 머리터짐"
"계정 삭제하려면 댓글, 게시글 전부 삭제하거나 null 처리해야함"

  • 긴급하게 운영 데이터를 수정/삭제해야 할 때, 많은 자식 테이블이 fk를 물고 있어서 작업이 실패함
  • 테스트 데이터를 자유롭게넣고 뺄 수 없어서 개발 속도가 저하됨

이런 성능 리스크와 관리 공수 때문에 많은 실무 환경에서 데이터정합성이라는 책임을 DB가 아니라 애플리케이션에게 넘기는 관행을 선택한 것으로 생각된다.

그럼 내 부트캠프 경험은 왜?? (JPA, ddl-auto)

이제 왜 실무에서 관행적으로 물리적 fk 제약조건을 안거는 곳이 많은지 이해가 간다.

근데 그렇다면 나는 @ManyToOne을 썼을 뿐인데 왜 물리적 제약조건이 걸려서 날 귀찮게 했을까?

정답은 애플리케이션의 ddl-auto 설정 때문이었다.

@ManyToOne 어노테이션 자체는 DB가 아닌 JPA 자바 객체를 위한 '논리적인' 매핑 선언인데

이 논리적 매핑이 어떻게 실제 DB의 '물리적 제약조건'이 되는지는 ddl-auto옵션이 결정하는 것이었다.

  • create/update (주로 개발/테스트 서버): Hibernate가 @ManyToOne 같은 논리적 매핑을 읽어서 FK 제약조건 DDL을 자동으로 실행한다. 내 테스트 과정에서 불편함의 원인이 바로 이것이었다
  • none/validate (주로 운영 서버): 실무에서는 대부분 none을 사용. Hibernate가 DDL을 실행하지도 않는다

실무에서는 JPA를 사용한다면 ddl-auto: none으로 옵션을 설정하고 Flyway같은 마이그레이션 툴로 DDL을 수동 관리한다고 한다.

바로 여기서 위에서 이야기 했듯이 성능과 관리를 위해서 의도적으로 DDL을 빼기로 합의할 수 있다.

그런데 그럼 RDBMS 왜 씀?

"RDB 쓰는 이유가 데이터 무결성인데 FK를 안 쓰면 NoSQL쓰지 RDBMS 왜 쓰냐?"

라는 댓글이 있었다.

확실히 타당한 이유라고 생각이 들고 몇 가지 의문과 우려가 있기도 하다.

댓글대로, 그리고 애플리케이션에서만 논리적으로 FK 정합성을 다루게 된다면

  • 개발자가 실수로 애플리케이션 로직에서 정합성 체크 로직을 누락할 수 있음
  • 배치 작업이나 다른 서비스가 애플리케이션을 안거치고 DB에 직접 접근하면 '고아 데이터' 같은 데이터 정합성이 깨질 가능성이 있음

즉 애플리케이션 로직이 완벽하지 않을 수도 있다.

그렇지만 이 우려를 제외하고서도 RDBMS를 쓰는 이유는 다양하다.

  • 트랜잭션 존재 여부
  • 쿼리 최적화
  • RDBMS 생태계 성숙도
  • 명확한 스키마
  • 조인 가능성 등등

저 댓글을 다신 분은 이 요소들을 간과한 것 같다는 생각이 든다. (아니면 정합성 관점에서만 집중하신 내용이라거나)

결론

확실히 커뮤니티의 여론을 보면 (특히 다양한 트래픽을 다루는 웹 서비스에서는) 성능과 유연성을 위해서 실무에서 외래키 FK 제약조건은 대체로 포기하는 것이 일반적인 관행처럼 보인다.

그러나 이 관행이 생긴 이유 중 하나였던 '테이블 락' 으로 인한 성능 저하 우려는 '인덱스가 없는' FK 칼럼이 원인이었다.

커뮤니티에서 보게된 정희락님의 FK 제약조건 기술 자료에 따르면 FK 칼럼에 인덱스를 생성하는 것으로 테이블락을 방지하고 동시성도 확보 할 수 있다고 한다.

물론 인덱스를 걸어도 FK 검증을 위한 부모 조회 병목, 공유 락 경합, 인덱스 갱신 비용 등의 근본적인 성능 한계와 관리 공수 라는 현실적인 한계는 여전히 존재한다.

정리해보자면 관행을 맹목적으로 따르기보다는 그 관행이 생긴 이유를 이해하고 해결책을 알고 있는 것이 중요하다고 본다.

결국 트레이드오프인 것 같은데 데이터 정합성이 매우 중요한 금융권 같은 도메인에서는 'FK 제약조건 + 인덱스'를 적용하고 고성능 매니퓰레이션이 필요한 테이블에서는 '예외적으로 하는 것'이 더 성숙한 설계가 아닐까...

나도 실제 트래픽이 있는 실무환경을 경험해 보지 못한 것은 맞기 때문에 현실적인 제약을 경험해보지 못한 개발자가 떠드는 허울만 좋은 건방진 이야기 일 수 있다.

이해는 하고 있되 경험을 통해 체화가 필요해보인다.

profile
백엔드 개발자

2개의 댓글

comment-user-thumbnail
2025년 11월 5일

그런이유로 안쓴다면 다행이지만
대부분은....

1개의 답글