InfoGrab DocsInfoGrab Docs

단일 테이블 상속

단일 테이블 상속(Single Table Inheritance, STI) 패턴의 문제점과 대안, 그리고 마이그레이션에서의 STI 비활성화 방법을 설명합니다.

요약 : 단일 테이블 상속(Single Table Inheritance, STI)을 사용하는 새로운 테이블을 설계하지 마세요. STI 패턴을 사용하는 기존 테이블의 경우, 새로운 타입 추가를 피하고 별도의 테이블로 분리하는 것을 검토하세요. STI는 단일 테이블에 여러 타입의 레코드를 저장하는 데이터베이스 설계 패턴입니다. 이 레코드들은 공유 칼럼의 부분 집합과, 해당 레코드를 어떤 객체로 표현할지 애플리케이션에 알려주는 또 다른 칼럼을 가집니다. 예를 들어, 두 가지 다른 유형의 SSH 키를 동일한 테이블에 저장하는 데 사용할 수 있습니다. ActiveRecord는 이를 활용하며 STI 사용을 보다 편리하게 만드는 몇 가지 기능을 제공합니다. 더 이상 새로운 STI 테이블을 허용하지 않는 이유는 다음과 같습니다: 테이블을 작게 유지하려는 노력과 달리, 많은 수의 행을 가진 테이블로 이어집니다. 추가 인덱스가 필요하여 경량 락(lightweight lock) 사용이 증가하며, 이러한 락의 포화는 장애를 유발할 수 있습니다. 모든 데이터를 특정 값으로 필터링해야 하는 오버헤드가 발생하여, 읽기 시 더 많은 페이지 접근이 이루어집니다. 객체에 맞는 올바른 클래스를 로드하기 위해 class_name 을 사용하지만, 클래스 이름을 저장하는 것은 비용이 많이 들고 불필요합니다. STI 대신 다음과 같은 대안을 고려하세요: 각 타입별로 다른 테이블을 사용하세요. *_type 칼럼 추가를 피하세요. 이는 향후 새로운 타입이 추가될 수 있음을 나타내는 코드 스멜이며, 나중에 리팩토링하기가 훨씬 어려워집니다. 이미 _type 칼럼을 사용하는 사실상의 STI 테이블이 있다면, 다음을 고려하세요: 기존 데이터를 여러 테이블로 분리하세요. 기존 테이블은 유지하면서 새로운 타입을 새 테이블로 추가할 수 있도록 리팩토링하세요(예: 기본 클래스의 로직을 concern으로 이동). 위의 모든 단점과 대안을 충분히 고려한 후에도 STI가 유일한 해결책이라면, 열거형(enum) 타입과 EnumInheritance concern을 사용하여 레코드에 클래스 이름을 저장하는 문제를 최소화할 수 있습니다: class Animal < ActiveRecord::Base include EnumInheritance enum species: { dog: 1, cat: 2 } def self.inheritance_column_to_class_map = { dog: 'Dog', cat: 'Cat' } def self.inheritance_column = 'species' end class Dog < Animal self.allow_legacy_sti_class = true end class Cat < Animal self.allow_legacy_sti_class = true end 테이블에 이미 *_type 이 있는 경우, 다양한 타입에 대한 새로운 클래스를 필요에 따라 추가할 수 있습니다. 마이그레이션에서 # 마이그레이션에서 모델이 사용될 때마다 단일 테이블