데이터 베이스/설계 2편, 실무에서 반드시 마주치는 9가지 설계 패턴

Ch06. 상속 관계 설계 - 구현 클래스마다 테이블 전략

webmaster 2026. 7. 23. 21:53

구현 클래스마다 테이블 전략"은 자식 클래스마다 별도의 테이블을 만들고, 각 테이블에 부모의 속성까지 모두 포함시키는 방식이다.

테이블 설계

DROP TABLE IF EXISTS book;
DROP TABLE IF EXISTS electronics;
DROP TABLE IF EXISTS clothing;

-- 도서 테이블 (부모 속성 + 도서 고유 속성)
CREATE TABLE book (
	product_id BIGINT PRIMARY KEY AUTO_INCREMENT,
	name VARCHAR(200) NOT NULL,
	price INT NOT NULL,
	stock_quantity INT NOT NULL DEFAULT 0,
	created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
	-- 도서 고유 속성
	author VARCHAR(100) NOT NULL,
	isbn VARCHAR(13),
	publisher VARCHAR(100)
);

-- 전자제품 테이블 (부모 속성 + 전자제품 고유 속성)
CREATE TABLE electronics (
	product_id BIGINT PRIMARY KEY AUTO_INCREMENT,
	name VARCHAR(200) NOT NULL,
	price INT NOT NULL,
	stock_quantity INT NOT NULL DEFAULT 0,
	created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
	-- 전자제품 고유 속성
	manufacturer VARCHAR(100) NOT NULL,
	warranty_months INT NOT NULL DEFAULT 12,
	power_consumption INT
);

-- 의류 테이블 (부모 속성 + 의류 고유 속성)
CREATE TABLE clothing (
	product_id BIGINT PRIMARY KEY AUTO_INCREMENT,
	name VARCHAR(200) NOT NULL,
	price INT NOT NULL,
	stock_quantity INT NOT NULL DEFAULT 0,
	created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
	-- 의류 고유 속성
	size VARCHAR(10) NOT NULL,
	color VARCHAR(50) NOT NULL,
	material VARCHAR(100)
);
  • 각 테이블이 "name", "price", "stock_quantity", "created_at"이라는 공통 속성을 모두 가지고 있다.
  • 그리고 각자 고유한 속성들을 추가로 가진다.

데이터 입력

-- 도서 데이터 입력
INSERT INTO book (name, price, stock_quantity, author, isbn, publisher) VALUES
('클린 코드', 33000, 100, '로버트 마틴', '9788966260959', '인사이트'),
('이펙티브 자바', 36000, 50,'조슈아 블로크', '9788966262281','인사이트'),
('JPA 프로그래밍', 43000, 30,'김영한', '9788960777330','에이콘');

-- 전자제품 데이터 입력
INSERT INTO electronics (name, price, stock_quantity, manufacturer, warranty_months, power_consumption) VALUES
('갤럭시', 1800000, 200, '삼성전자', 24, 15),
('맥북 프로 14', 2500000, 50, '애플', 12, 96),
('LG 그램 17', 1800000, 80, 'LG전자', 24, 65);

-- 의류 데이터 입력
INSERT INTO clothing (name, price, stock_quantity, size, color, material) VALUES
('오버핏 맨투맨', 35000, 500, 'L','블랙', '면 100%'),
('슬림핏 청바지', 59000, 300, 'M', '인디고', '데님'),
('패딩 점퍼', 189000, 100, 'XL','네이비', '폴리에스터');

 

조회하기

-- 도서 조회
SELECT product_id, name, price, stock_quantity, author, publisher
FROM book;

쿼리 결과

-- 전자제품 조회
SELECT product_id, name, price, manufacturer, warranty_months
FROM electronics;

쿼리 결과

구현 클래스마다 테이블 전략의 장점

  • 단순하고 직관적이다
    • 테이블 구조가 명확하다.
      • 도서는 도서 테이블, 전자제품은 전자제품 테이블에서 찾으면 된다.
    • 조인 없이 단일 테이블만 조회하면 되므로 쿼리가 단순하다.
  • 각 유형별 조회 성능이 좋다
    • 특정 유형의 상품만 조회할 때 해당 테이블만 보면 된다.
    • 다른 유형의 데이터가 섞여있지 않으므로 특정 유형의 상품만 조회할 때는 조회 성능이 좋다.
  • "NOT NULL" 제약조건을 걸 수 있다
    • 각 테이블에 해당 유형에 필요한 컬럼만 있으므로, "NOT NULL" 제약조건을 자유롭게 설정할 수 있다.

구현 클래스마다 테이블 전략의 단점

하지만 이 전략에는 심각한 문제들이 있다. 실무에서 이 전략을 사용하기 어려운 이유를 살펴보자.

 

문제점 - 전체 상품 조회가 어렵다

쇼핑몰에서 "전체 상품 목록"을 보여줘야 하는 상황을 생각해보자. 메인 페이지에서 신상품을 보여주거나, 검색 결과에서 모든 종류의 상품을 함께 보여줘야 한다.

-- 전체 상품을 최신순으로 조회하려면?
SELECT product_id, name, price, created_at, 'BOOK' AS product_type
FROM book

UNION ALL

SELECT product_id, name, price, created_at, 'ELECTRONICS' AS product_type
FROM electronics

UNION ALL

SELECT product_id, name, price, created_at, 'CLOTHING' AS product_type
FROM clothing
ORDER BY created_at DESC;

쿼리 결과

  • 모든 테이블을 "UNION ALL"로 합쳐야 한다.
    • 상품 유형이 늘어날 때마다 "UNION'을 추가해야 한다.
  • 만약 가구, 식품, 스포츠용품 등이 추가된다면? 쿼리가 점점 복잡해지고 유지보수가 어려워진다.

문제점 - 상품 ID로 조회할 때 어느 테이블인지 모른다

주문 테이블에서 상품 ID를 참조한다고생각해 보자. "product_id"가 1인 상품을 조회하려면 어느 테이블을 봐야 할까? 도서일 수도 있고, 전자제품일 수도 있고, 의류일 수도 있다. 결국 세 테이블을 모두 뒤져봐야 한다.

-- product_id = 1인 상품 찾기 (어느 테이블에 있는지 모름)
SELECT 'BOOK' AS product_type, name, price
FROM book WHERE product_id = 1

UNION ALL

SELECT 'ELECTRONICS' AS product_type, name, price
FROM electronics WHERE product_id = 1

UNION ALL

SELECT 'CLOTHING' AS product_type, name, price
FROM clothing WHERE product_id = 1;
  • 매우 비효율적이다. 실무에서는 이런 조회가 수없이 발생하는데, 매번 모든 테이블을 조회해야 한다.

문제점 - 외래 키 설정이 불가능하다

더 심각한 문제가 있다. 주문 테이블에서 상품 테이블로 외래키를 설정할 수 없다.

-- 이런 외래 키는 설정할 수 없다!
-- product_id가 book, electronics, clothing 중 어디를 참조해야 하는가?
ALTER TABLE orders
ADD CONSTRAINT fk_orders_product
FOREIGN KEY (product_id) REFERENCES ???(product_id);
  • 외래키는 하나의 테이블만 참조할 수 있다. 여러 테이블 중 하나를 참조하는 것은 불가능하다.
  • 따라서 데이터 무결성을 데이터베이스 레벨에서 보장할 수 없다.

문제점 - 상품 ID 중복 위험

각 테이블이 독립적으로 "AUTO_INCREMENT"를 사용하므로 상품 ID가 중복될 수 있다.

-- book 테이블의 product_id = 1 (클린 코드)
-- electronics 테이블의 product_id = 1 (갤럭시)
-- clothing 테이블의 product_id = 1 (오버핏 맨투맨)
  • 세 개의 서로 다른 상품이 모두 "product_id = 1"을 가진다.
  • 주문 테이블에서 "product_id = 1"을 참조하면 어떤 상품인지 구분할 수 없다.
  • 이를 해결하려면 별도의 "product_type" 컬럼을 추가해야 한다.
CREATE TABLE orders (
    order_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    product_type VARCHAR(20) NOT NULL, -- 'BOOK', 'ELECTRONICS', 'CLOTHING'
    product_id BIGINT NOT NULL,
    quantity INT NOT NULL,
    order_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
  • 또는 모든 상품의 키를 통합해서 별도로 관리해야 한다.
  • 하지만 이렇게 해도 테이블이 나누어져 있으므로 외래 키는 설정할 수 없고, 애플리케이션에서 관리해야 하는 복잡성이 증가한다.

문제점 - 컬럼 변경 시 모든 테이블 수정 필요

공통 속성을 변경해야 할 때를 생각해보자. 예를 들어 모든 상품에 discount_rate(할인율) 컬럼을 추가하려면?

ALTER TABLE book ADD COLUMN discount_rate INT DEFAULT 0;
ALTER TABLE electronics ADD COLUMN discount_rate INT DEFAULT 0;
ALTER TABLE clothing ADD COLUMN discount_rate INT DEFAULT 0;
-- 새로운 상품 유형이 추가될 때마다 수정 필요
  • 테이블이 많아질수록 공통 속성의 변경 작업이 번거롭고 누락 위험이 있다.

 

정리

장점  단점
구조가 단순하고 직관적  전체 상품 조회 시 UNION 필요
각 유형별 조회 성능 우수  상품 ID로 조회 시 모든 테이블 검색
NOT NULL 제약조건 사용 가능  외래 키 설정 불가능

상품 ID 중복 위험

공통 속성 변경 시 모든 테이블 수정