한 NestJS 강의를 보며 놀랐던 부분이 있다.
강의에서 커머스 서비스에 대한 스키마를 설계 중이였는데 다른 테이블의 ID를 외래키로 설정하지 않는 것이였다. 설정하지 않은 이유는 성능 오버헤드였다. 당황스러운 마음에 처음엔 이해가 되지 않았지만 잠시 생각해보니 그 이유를 알 수 있었다.
외래키는 무결성을 보장할 수 있는 다른 테이블의 기본키이다. 종종 개발을 하다보면 이 외래키(무결성 제약)로 인해 개발자 입장에서 불편한 일을 겪곤 한다. 외래키 설정 시 Hard Delete 를 고려해 casacade, restrict 를 고민하기도하고 때때론 특정 데이터를 수정해야 할 때 고민을 하게 만든다. 나는 지금까지 이런 일은 소위 개발자가 겪어야 할 당연한 일이라고 생각했다. 여기까진 당연한 일이 맞다. 이유는 그 다음 너머에 있었다.
외래키는 주로 `insert`, `update`, `delete` 같은 데이터 변경 작업(CUD) 에서 동작한다. 이런 동작이 발생할 때 당연히 DB에선 해당 row의 외래 키가 유효한지 검사하기 위해 관련 테이블의 기본키를 탐색한다. 이 과정에 이유가 있다. 단순히 한번의 동작이라면 무시해도 되지만 만약 초당 수백만건의 트랜잭션을 처리해야 하는 상황이라면 말이 달라진다. 수천, 수억만개의 row로 이루어져있는 다른 테이블의 기본키를 조회하는 동작은 꽤나 무시하진 못할 정도의 동작이다. 서비스 규모가 커짐에 따라 이는 더 비대해질 것이다.
커뮤니티 의견
이러한 상황에 놓인 개발자는 선택을 해야한다. 참조 무결성이냐, 성능이냐. 커뮤니티에선 많은 개발자의 의견이 첨애하게 대립한다. 당연하다, 개발에 정답은 없다.
위 Github comment 는 gh-ost 라는 DB 마이그레이션 관련 Github 공식 서비스 레포에서 한 유저가 해당 프로젝트엔 외래키가 적용되어있지 않다는 말에 Github 개발자가 대답한 내용이다. 2016년 코멘트이지만 외래키에 꽤나 회의적이고 확고한 의견이다.
위 코멘트를 간략히 정리하자면 샤딩을 방해하고, 특히 insert/delete 에서 오버헤드를 보이며, 마이그레이션 시 적합하지 않다는 내용이다. 특히 마이그레이션 시 적합하지 않다는 내용은 글의 마지막 부분에 쉽고 정확한 예시를 통해 잘 설명해주고 있다.
이런 의견이 있는 반면 외래키에 우호적인 반응도 적지 않다.
위 코멘트는 Stackoverflow 에서 외래키 성능에 대한 논의에 대해 평판점수 약 96,718점인 DB 엔지니어가 대답한 것이다. '
간략하게 정리하자면 'FK 제약은 실제로 유의미한 오버헤드를 발생시키지 않는다(미미하다).' , '발생하더라도 외래키를 제약을 없애 성능을 향상시키는 것보다 데이터 무결성이 더욱 중요하다.' 라는 의견이다. 또한 '만약 성능 이슈로 걸고 넘어진다면 외래키 제약 조건을 해제해서 테스트한 수치와 함께 속히 말하는 아무런 의미없는 고아 레코드(외래키 제약을 설정하지 않을 떄 발생할 수있는 외래키에 대한 부모가 없는 레코드) 를 보여주어라' 라고 얘기하고 있다.
또, 평판 34만점의 한 유저는 위 사진과 같이 MS SQL Server 가이드라인에서 말하는 '외래키 설정 시 DB Server에서 최적화된 참조 실행 계획을 세운다' 를 근거로 외래키에 우호적인 입장을 내비치고 있다. 물론, 참조 최적화가 오버헤드를 발생시키지 않는다는 아닌 것을 알고 있다.
이 외에도 외래키 성능 이슈에 대한 논의는 끊이지 않고 각각 첨애하게 대립하고 있는 것으로 보인다. 전체적으로 외래키 찬성 측은 '성능 이슈는 미미하다, 무결성이 중요하다.' 라는 의견이고 반대 측은 '성능 이슈는 유의미하다. 무결성을 포기하더라도 얻는 비지니스 이익이 더 크다' 라는 의견으로 대립하는 것 같다.
나의 의견
나는 그래도 외래키 제약조건은 사용하는 게 좋지않을까? 라는 의견이다.
개발을 하다보면 외래키와 같은 제약 조건들로 인해 고민을 해야 하는 일을 심심치 않게 마주한다. 또, 직접적으로 비지니스엔 도움이 되지 않지만 안정적인 운영을 위해선 꼭 필요한 백업 파이프라인 설계, 오토 스케일링 설정, 보안 관리, 테스트 코드 등 마치 보험 같은 작업들도 마주한다. 이런 작업들은 시간과 돈이 들 뿐더러 개발자 입장에서도 불편하고 귀찮다. 분명 이런 작업을 할 시간에 비지니스 관련 버그를 고치고 매출을 올리는 관련 기능 개발을 하는 것이 매출에 도움이 될 것이다.
하지만 이런 보험같은 작업을 무시하는 것은 옳지 않다고 생각한다. 서비스는 당장의 매출이 전부가 아니기 때문이다. 보험같은 작업이 쌓여 안정적인 운영을 만들어내고 이는 미래 매출에 기여하며 매출 그 이상의 값어치를 만든다.
외래키를 거는 일도 이런 보험같은 작업의 일환이라고 생각한다. 당장 퍼포먼스를 향상시키고 불편한 작업을 덜어 비지니스에 박차를 가할 수는 있겠지만 그에 따른 정합성, 신뢰도 문제가 없을 거라 단언하기 어려워진다.
성능을 위해 관리에 초점을 맞춘 기능을 포기하는 방법보다는 성능은 성능을 위한 작업와 수단을 통해 향상시키는게 좋지 않을까 라는 생각이 든다.
'NestJS' 카테고리의 다른 글
[NestJS] Sharp를 이용한 이미지 포맷 최적화 (to webp) (0) | 2025.03.21 |
---|---|
NestJS 11 업데이트 살펴보기 (0) | 2025.01.31 |