본 포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성하였습니다.
오늘은 ERD 설계, Spring MVC, REST API, MyBatis를 활용한 데이터베이스 연동, 세션 기반 인증과 SHA256 해싱까지 웹 애플리케이션 개발 전반을 다루는 강의를 들었다. 사실 내용 자체는 평소 자주 접하던 것들이라 크게 새로울 건 없었다. 그래도 시퀀스 다이어그램이랑 ERD 작성할 때 쓸 만한 툴을 새로 알게 된 건 좋았다.
실습으로는 MyBatis 사용해서 사용자 관련 API 구현하고, 세션 기반 로그인 로직 짜보는 시간을 가졌다. 아직 까지 강의 내용이 그렇게 많지 않았다. ERD 얘기가 나온 김에, 예전부터 궁금했던 MySQL의 Foreign Key 제약조건을 데이터베이스 레벨에서 거는 것이 좋을지, 아니면 애플리케이션 코드 레벨에서 관리하는 것이 좋을지에 대해 생각해보는 시간을 가져보았다.
Foreign Key Constraints
외래키 제약조건은 데이터 무결성을 보장하고 참조 무결성을 강제하는 데이터베이스 설계의 요소 중 하나이다. 결국은 참조 무결성이 보장되는것이 핵심이다.
데이터베이스 레벨
DB 레벨 FK를 사용하면 DB 인프라 레벨에 최적화된 쿼리 실행 계획을 사용할 수 있을 것이며 동시 요청과 분산 부하를 자동으로 처리해줄 것이다. 또한 앞서 말한대로 대규모 시스템의 데이터라도 무결성을 보장해주기 때문에 일관성을 보장해주는 장점이 있다.
하지만 DB 레벨 FK를 사용하면 생각보다 까다로운 부분이 많다. 첫째, 테이블 구조를 변경할 때 제약이 크다. 마이그레이션 스크립트 작성할 때 순서도 신경 써야 하고, 데이터의 볼륨이 증가하는 경우 제약조건 수정/제거의 복잡성이 확장성에 영향을 주기 때문이다.
성능 오버헤드도 무시할 수 없다. INSERT할 때마다 부모 테이블에 해당 키가 있는지 검증해야 하고, UPDATE도 마찬가지다. DELETE는 더 심각한데, CASCADE 옵션이 있으면 부모 하나 지우는 게 자식들을 줄줄이 삭제할 수 있다. 계층이 깊으면 예상치 못한 대량 삭제가 발생할 수도 있다. Lock 문제도 있다. MySQL InnoDB는 FK 검사할 때 부모 테이블에 Shared Lock을 걸고, CASCADE 작업 시에는 Write Lock을 건다. 동시성 높은 환경에서 여러 테이블이 FK로 얽혀있으면 Lock 대기가 길어져서 처리량이 떨어진다.
애플리케이션 레벨
그럼 애플리케이션 레벨에서 관리하면 어떨까? 먼저 DB 레벨의 단점들이 어느정도 해소된다. 테이블 구조 변경할 때 FK 신경 쓸 필요 없고, 마이그레이션도 훨씬 자유롭다. 대량 배치 작업할 때도 순서 제약 없이 편하게 처리할 수 있다. 성능 측면에서도 DB 왕복 없이 애플리케이션 메모리에서 검증하면 되니까 오버헤드가 줄어든다. 특히 마이크로서비스 아키텍처에서는 각 서비스가 독립적인 DB를 가지니까 물리적으로 FK를 걸 수가 없다. 이런 환경에서는 애플리케이션 레벨 관리가 유일한 선택지다.
하지만 코드 레벨 관리도 만만치 않다. 가장 큰 문제는 데이터 불일치 가능성이다. DB 레벨 FK는 누가 어떻게 접근하든 무조건 막아주는데, 코드 레벨은 개발자가 빼먹으면 그대로 버그가 된다. 여러 팀이나 여러 애플리케이션에서 같은 DB를 공유할 때 특히 위험하다. 애플리케이션의 볼륨이 증가하여 참조 관계 로직이 코드에 흩어지면 나중에 유지보수할 때 어디서 뭘 검증하는지 찾기도 어렵다. CASCADE 같은 동작도 직접 구현해야 하니까 복잡도가 올라간다. 또한 동시성 관리가 복잡하기 때문에 고도의 동시 환경에서는 오히려 성능이 떨어질 수도 있을 것이다.
결국 정답은 없을 것이며 상황에 맞게 선택하는 것 혹은 적절히 섞어가며 사용하는 것이 좋을 것이다.




참고 출처
- https://new-pow.tistory.com/69
- https://medium.com/@devjoemar2016/understanding-the-performance-impact-of-foreign-key-constraints-in-database-design-918acc9612ec