← 블로그 목록

참조 무결성은 데이터베이스 규칙이면서 동시에 런타임 안전 규칙이기도 하다

참조 무결성은 외래 키 규칙으로만 생각하기 쉽지만, 런타임에서 포인터·핸들·식별자가 가리키는 대상의 수명을 관리하는 문제에도 거의 같은 구조가 깔려 있다. 데이터베이스가 ‘존재하는 행만 가리키게 하라’고 한다면 런타임은 ‘살아 있는 객체만 가리키게 하라’를 요구한다. 결국 핵심은 소유권과 수명, 유효성 확인을 함께 설계하는 일이다.

참조 무결성은 데이터베이스 규칙이면서 동시에 런타임 안전 규칙이기도 하다

참조 무결성은 데이터베이스 규칙이면서 동시에 런타임 안전 규칙이기도 하다

참조 무결성이라는 말은 보통 데이터베이스에서 먼저 배운다. 테이블 하나의 값이 다른 테이블의 실제 행을 가리키도록 보장하는 규칙이다. 하지만 이 개념은 데이터베이스에서만 중요한 것이 아니다. 프로그램 런타임에서도 비슷한 문제가 계속 나타난다.

포인터, 참조, 핸들, 식별자처럼 어떤 값이 다른 객체를 가리키고 있다면, 그 대상이 정말 존재하는지, 아직 살아 있는지, 바뀌지 않았는지를 계속 관리해야 한다. 결국 참조 무결성은 관계가 깨지지 않게 유지하는 일 전반을 가리키는 말로 읽을 수 있다.


데이터베이스에서의 참조 무결성은 외래 키로 표현된다

PostgreSQL 문서는 Foreign Keys를 설명하면서, 외래 키 제약이 어떤 열의 값이 다른 테이블의 실제 행과 일치해야 한다고 말한다. 즉 존재하지 않는 대상을 가리키는 값을 막는 장치다.

이 규칙이 필요한 이유는 단순하다.

데이터베이스에서는 이런 문제를 외래 키, 삭제 정책, 업데이트 정책으로 다룬다. 핵심은 참조 관계를 사람이 매번 기억해서 맞추지 않게 만드는 것이다.


런타임에서도 같은 종류의 문제가 생긴다

프로그램 안에서도 상황은 비슷하다. 객체 A가 객체 B를 가리키고 있는데 B가 먼저 파괴되면, A가 들고 있던 참조는 더 이상 유효하지 않다. 이건 데이터베이스에서 없는 행을 참조하는 것과 구조적으로 비슷한 문제다.

C++ Core Guidelines는 수명(lifetime) 관련 규칙에서 포인터가 가리키는 객체보다 오래 살아남아서는 안 된다고 명확히 말한다. 이유도 분명하다. 이런 상황은 찾기 어려운 오류와 정의되지 않은 동작으로 이어지기 때문이다.

즉 데이터베이스의 참조 무결성이 존재하는 행만 가리키게 하라는 규칙이라면, 런타임의 참조 무결성은 살아 있는 객체만 가리키게 하라는 규칙에 가깝다.


핵심은 소유권과 수명 관리를 분리해서 생각하는 것이다

참조 무결성이 깨지는 가장 흔한 이유는 누가 소유하는가누가 참조하는가를 섞어 버릴 때다.

그래서 런타임 설계에서는 보통 이런 구분이 중요하다.

데이터베이스가 외래 키와 제약 조건으로 관계를 관리하듯, 프로그램도 소유권 모델과 수명 규칙으로 관계를 관리해야 한다.


핸들, 식별자, 레퍼런스 카운팅은 모두 같은 문제의 다른 해법이다

게임 개발이나 엔진 코드에서는 직접 포인터를 넘기지 않고 핸들이나 ID를 쓰는 경우가 많다. 이유는 객체가 이동하거나 파괴되어도 중앙 관리자에서 유효성을 확인할 수 있기 때문이다.

반대로 참조 계수를 늘려 객체 수명을 연장하는 방식도 있다. 이건 대상을 너무 일찍 파괴하지 않게 해 준다. 다만 순환 참조 같은 또 다른 문제가 생길 수 있다.

어떤 방식을 쓰든 공통 질문은 같다.

  1. 이 참조는 누가 만들었는가
  2. 대상은 언제 파괴되는가
  3. 파괴된 뒤 참조자는 어떻게 알 수 있는가
  4. 참조가 오래 남아도 안전한가

참조 무결성은 결국 이 네 질문에 일관되게 답하는 설계에서 나온다.


핵심 정리

참조 무결성은 데이터베이스의 외래 키 규칙으로만 생각하기 쉽지만, 실제로는 런타임 객체 관리에서도 거의 같은 문제를 다룬다. 존재하지 않는 행을 가리키는 것도 문제고, 이미 파괴된 객체를 가리키는 것도 문제다.

그래서 안전한 프로그램을 만들려면 참조 자체보다 소유권, 수명, 유효성 확인을 함께 설계해야 한다. 데이터베이스와 런타임은 도구는 다르지만, 관계가 깨지지 않게 지켜야 한다는 점에서는 같은 문제를 풀고 있다.

참고 자료

← 목록으로
Related

함께 읽으면 좋은 글

객체지향상속델리게이트
상속을 줄이고 델리게이트와 시그널로 푸는 이유

상속은 ‘무엇의 하위 타입인가’를 표현하는 데 강하지만, UI 이벤트나 객체 간 통신처럼 호출 관계가 자주 바뀌는 영역에서는 델리게이트나 시그널과 슬롯 같은 느슨한 연결이 더 잘 맞는다. C# 델리게이트와 Qt 시그널의 사례를 빌려, 좋은 설계는 상속을 줄이는 신념이 아니라 ‘타입 관계’와 ‘실행 시 연결 관계’를 분리해 보는 습관에서 나온다는 점을 정리한다.

서평소프트웨어 개발조엘 스폴스키
『Joel on Software』를 다시 읽으면 남는 것은 화려한 이론보다 운영 감각이다

조엘 스폴스키의 『Joel on Software』는 거대한 이론 대신 The Joel Test, 버그 추적, 새는 추상화 같은 낮지만 치명적인 마찰을 집요하게 건드린다. 좋은 팀은 우연히 굴러가지 않는다는 감각, 즉 빌드·버그·채용 같은 기본기를 잃지 않게 만드는 운영 감각이 이 책의 가장 큰 가치라는 점을 다시 짚는다.

함수형 프로그래밍불변성자료구조
불변 자료구조가 비효율적으로 보이는데도 계속 쓰이는 이유

불변 자료구조는 매번 전체를 복사하는 비효율적인 방식처럼 보이지만, 실제 구현은 구조적 공유와 영속 자료구조를 바탕으로 훨씬 더 실용적으로 동작한다.