사실 공통 코드 도입자체는 어렵지 않다. 하지만 이로 인해서 복잡성과 성능에 관련된 고민이 생긴다.
공통 코드를 실제로 사용해보면 알겠지만 언젠가는 생각보다 이 패턴이 지저분하다는 생각, 그래서 어떻게 보완할지 고민이 들기 마련이다.
테스트를 위해 데이터를 넣어보자.
-- ## 공통 코드의 단점
-- ### 공통 코드 사용 시 단점
DROP TABLE IF EXISTS payments;
DROP TABLE IF EXISTS orders;
DROP TABLE IF EXISTS members;
DROP TABLE IF EXISTS common_code_attribute; -- 복습시 문제가 되는 테이블 제거
DROP TABLE IF EXISTS common_code_detail;
DROP TABLE IF EXISTS common_code_group;
-- 그룹 코드 테이블
CREATE TABLE common_code_group (
group_code VARCHAR(50) PRIMARY KEY,
group_name VARCHAR(100) NOT NULL
);
-- 상세 코드 테이블
CREATE TABLE common_code_detail (
group_code VARCHAR(50) NOT NULL,
code VARCHAR(50) NOT NULL,
name VARCHAR(100) NOT NULL,
sort_order INT NOT NULL DEFAULT 0,
use_yn CHAR(1) NOT NULL DEFAULT 'Y',
PRIMARY KEY (group_code, code),
FOREIGN KEY (group_code) REFERENCES common_code_group(group_code)
);
-- 회원 테이블
CREATE TABLE members (
member_id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL,
grade VARCHAR(20) NOT NULL DEFAULT 'NORMAL'
);
-- 주문 테이블
CREATE TABLE orders (
order_id BIGINT PRIMARY KEY AUTO_INCREMENT,
member_id BIGINT NOT NULL,
order_status VARCHAR(20) NOT NULL DEFAULT 'ORDER',
total_amount INT NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (member_id) REFERENCES members(member_id)
);
-- 결제 테이블
CREATE TABLE payments (
payment_id BIGINT PRIMARY KEY AUTO_INCREMENT,
order_id BIGINT NOT NULL,
payment_method VARCHAR(20) NOT NULL,
payment_status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
amount INT NOT NULL,
FOREIGN KEY (order_id) REFERENCES orders(order_id)
);
-- 공통 코드 데이터
INSERT INTO common_code_group (group_code, group_name) VALUES
('ORDER_STATUS', '주문상태'),
('MEMBER_GRADE', '회원등급'),
('PAYMENT_STATUS', '결제상태'),
('PAYMENT_METHOD', '결제수단');
INSERT INTO common_code_detail (group_code, code, name, sort_order) VALUES
('ORDER_STATUS', 'ORDER', '주문접수', 1),
('ORDER_STATUS', 'PAID', '결제완료', 2),
('ORDER_STATUS', 'SHIPPING', '배송중', 3),
('ORDER_STATUS', 'DELIVERED', '배송완료', 4),
('ORDER_STATUS', 'CANCEL', '주문취소', 5),
('MEMBER_GRADE', 'NORMAL', '일반회원', 1),
('MEMBER_GRADE', 'VIP', 'VIP회원', 2),
('MEMBER_GRADE', 'VVIP', 'VVIP회원', 3),
('PAYMENT_STATUS', 'PENDING', '결제대기', 1),
('PAYMENT_STATUS', 'COMPLETE', '결제완료', 2),
('PAYMENT_STATUS', 'FAILED', '결제실패', 3),
('PAYMENT_STATUS', 'CANCEL', '결제취소', 4),
('PAYMENT_METHOD', 'CARD', '신용카드', 1),
('PAYMENT_METHOD', 'BANK', '계좌이체', 2),
('PAYMENT_METHOD', 'MOBILE', '휴대폰결제', 3);
-- 테스트 데이터
INSERT INTO members (name, email, grade) VALUES
('션', '[email protected]', 'NORMAL'),
('네이트', '[email protected]', 'VIP'),
('이순신', '[email protected]', 'VVIP');
INSERT INTO orders (member_id, order_status, total_amount, created_at) VALUES
(1, 'ORDER', 50000, '2026-01-15 10:00:00'),
(1, 'PAID', 75000, '2026-01-15 11:00:00'),
(2, 'SHIPPING', 120000, '2026-01-16 09:00:00'),
(2, 'DELIVERED', 85000, '2026-01-14 15:00:00'),
(3, 'CANCEL', 45000, '2026-01-13 14:00:00');
INSERT INTO payments (order_id, payment_method, payment_status, amount) VALUES
(1, 'CARD', 'PENDING', 50000),
(2, 'CARD', 'COMPLETE', 75000),
(3, 'BANK', 'COMPLETE', 120000),
(4, 'MOBILE', 'COMPLETE', 85000),
(5, 'CARD', 'CANCEL', 45000);
테스트 데이터를 위와같이 넣어뒀음.
공통 코드를 사용하지 않고 단순 조회 해보자.
SELECT
o.order_id,
m.name AS member_name,
o.order_status,
o.total_amount
FROM orders o
JOIN members m ON o.member_id = m.member_id
ORDER BY o.order_id;

SQL만 보면 그냥 이해가 된다. 뭐를 의도하는 SQL인지 알수가 있음. 비즈니스관련된 SQL만 있어서 군더더기가 없어서 이해하는데 어려움이 없다.
여기에 이제 공통 코드 테이블을 끼얹어보자.
SELECT
o.order_id,
m.name AS member_name,
o.order_status,
os.name AS order_status_name,
o.total_amount
FROM orders o
JOIN members m ON o.member_id = m.member_id
JOIN common_code_detail os
ON os.group_code = 'ORDER_STATUS' AND o.order_status = os.code
ORDER BY o.order_id;

결과집합은 전보다 더 보기 쉽다. 대신 SQL은 좀더 잘 이해가 안되기 시작한다. 그리고 심지어 여기서 끝이 아니다. 여기서 회원 등급까지 조회를 시작한다면? 그냥 컬럼만 넣는다면
SELECT
o.order_id,
m.name AS member_name,
m.grade, #이 컬럼만 추가하면 결과집합에 의미가 불충분해진다.
o.order_status,
os.name AS order_status_name,
o.total_amount
FROM orders o
JOIN members m ON o.member_id = m.member_id
JOIN common_code_detail os
ON os.group_code = 'ORDER_STATUS' AND o.order_status = os.code
ORDER BY o.order_id;