데이터베이스 컬럼 네이밍 컨벤션 가이드
이름은 스키마에서 가장 값싸게 바꿀 수 있어 보이지만 실제로는 가장 바꾸기 어려운 부분입니다. 컬럼명 하나가 애플리케이션 코드, ORM 매핑, API 응답, 리포트 쿼리 곳곳에 퍼지기 때문입니다. 처음부터 일관된 규칙을 정해 두는 것이 결국 가장 저렴한 선택입니다.
테이블명 규칙
- 스네이크 케이스(snake_case) —
order_items처럼 소문자와 언더스코어를 씁니다. MySQL·PostgreSQL 모두 대소문자를 구분하는 환경이 운영체제에 따라 갈리기 때문에, 아예 소문자로 통일하면 이 함정을 피할 수 있습니다. - 복수형 vs 단수형 — 정답은 없지만 팀 안에서는 반드시 통일하세요.
복수형(
users,orders)은 "이 테이블은 여러 행의 집합"이라는 느낌을 직관적으로 주고, 단수형(user,order)은 ORM에서 모델 클래스명과 1:1로 대응시키기 편합니다. 둘 다 널리 쓰이는 관행이니, 새 프로젝트를 시작한다면 첫 테이블을 만들 때 팀 규칙으로 명시해 두세요. - 접두어 남용 자제 —
tbl_user,t_order처럼 테이블이라는 사실을 이름에 반복하는 접두어는 대부분 불필요합니다. 스키마 안에서 테이블이라는 건 이미 자명하기 때문입니다.
기본키 이름
흔히 쓰이는 두 가지 패턴이 있습니다.
| 패턴 | 예 | 장점 | 단점 |
|---|---|---|---|
id 고정 | users.id | 어느 테이블이든 PK 컬럼명이 항상 같아 예측 가능. ORM 기본값과 잘 맞음 | 조인 후 SELECT * 결과에서 여러 id가 뒤섞여 구분이 안 됨 |
{table}_id | users.user_id | 조인 결과에서도 어느 테이블의 id인지 바로 구분됨 | 테이블명이 바뀌면 PK 컬럼명도 같이 바꿔야 하는 부담 |
어느 쪽이든 팀 전체에서 예외 없이 통일하는 게 중요합니다. 절반은 id,
절반은 {table}_id를 쓰면 새 테이블을 만들 때마다 "이번엔 어느 쪽이지"를
매번 찾아봐야 합니다.
외래키 이름
기본 원칙은 참조하는 테이블의 PK 이름을 그대로 따르는 것입니다.
orders 테이블이 customers.customer_id를 참조한다면,
orders.customer_id로 이름 짓습니다. 이렇게 하면 컬럼명만 보고도
어느 테이블을 참조하는지 바로 알 수 있습니다.
예외는 한 테이블이 같은 부모를 여러 역할로 참조할 때입니다.
배송지 주소와 청구지 주소를 둘 다 addresses 테이블에서 참조한다면
그대로 이름을 따라 짓는 게 불가능합니다. 이때는 역할을 나타내는 접두어를 붙입니다.
CREATE TABLE orders (
order_id BIGINT PRIMARY KEY,
shipping_address_id BIGINT NOT NULL REFERENCES addresses(address_id),
billing_address_id BIGINT NOT NULL REFERENCES addresses(address_id)
);
자기 자신을 참조하는 재귀 관계(예: 조직도의 상위 부서, 댓글의 부모 댓글)도 같은
원칙을 씁니다 — employees.manager_id처럼 역할 이름을 접두어로 붙여
그냥 employee_id를 또 쓰는 혼란을 피합니다.
불리언 컬럼
is_, has_ 접두어로 참/거짓을 묻는 형태의 이름을 짓습니다.
active보다 is_active가, coupon보다
has_coupon이 값의 성격을 이름만 보고 바로 알 수 있게 해줍니다.
status처럼 값이 여러 개일 수 있는 컬럼과 불리언 컬럼을 이름 규칙으로
구분해 두면, 나중에 "이거 true/false 컬럼인가 enum인가"를 코드를 열어보지 않고도
알 수 있습니다.
날짜·시간 컬럼
_at접미어 — 시각(타임스탬프)을 나타냅니다.created_at,updated_at,deleted_at처럼 "언제 그 일이 일어났는가"를 기록합니다. 대부분의 프레임워크·ORM이created_at/updated_at이름을 기본 관행으로 채택하고 있어 그대로 따르는 게 여러모로 유리합니다._date접미어 — 시각이 아니라 순수한 날짜(연-월-일)를 나타냅니다.birth_date,due_date처럼 시간 정보가 의미 없는 값에 씁니다.
이 둘을 구분해 두면 컬럼명만 보고도 타입이 TIMESTAMP인지 DATE인지 짐작할 수 있어 쿼리를 짤 때 실수를 줄여줍니다.
예약어·모호한 이름 피하기
order, group, user, key,
desc 같은 이름은 여러 DBMS에서 예약어와 충돌하거나 충돌 직전까지
갑니다. 이런 이름을 쓰면 쿼리를 짤 때마다 매번 백틱이나 따옴표로 감싸야 하고,
어느 순간 감싸는 걸 깜빡해 문법 오류가 나는 식으로 계속 발목을 잡습니다.
orders처럼 복수형으로 짓거나 user_groups처럼 조금 더
구체적인 이름으로 바꾸는 것만으로 이런 문제 대부분이 사라집니다.
data, info, misc, value처럼
범용적이라 아무 의미도 전달하지 못하는 이름도 피하세요. 급하게 만든 컬럼일수록
이런 이름이 붙기 쉬운데, 반년 뒤에 그 컬럼이 정확히 뭘 담고 있었는지 이름만으로는
전혀 알 수 없게 됩니다.
물리명·논리명 병기 — 국내 실무 관행
국내 데이터베이스 설계 산출물에서는 컬럼의 물리명(영문, 실제 DB 컬럼명)과
논리명(한글, 사람이 읽는 설명)을 함께 적는 관행이 널리 쓰입니다.
예를 들어 물리명 cust_grade_cd에 논리명 "고객등급코드"를 나란히
붙여두는 식입니다. 영문 이름만으로는 축약이 많아 읽기 어렵고, 한글 이름만으로는
실제 쿼리를 짤 수 없기 때문에 둘을 함께 관리하는 게 실용적입니다.
체크리스트
- 테이블명·컬럼명 모두 소문자 스네이크 케이스로 통일했는가
- 기본키 이름 패턴(
id또는{table}_id)을 팀 전체가 같은 규칙으로 쓰는가 - 외래키가 참조하는 테이블의 PK 이름을 그대로 따르는가(역할이 겹치면 접두어로 구분했는가)
- 불리언 컬럼에
is_/has_를 붙였는가 - 타임스탬프는
_at, 날짜는_date로 구분했는가 - DBMS 예약어와
data류 범용 이름을 피했는가