테이블·컬럼 명명 규칙 정하는 법

· 갱신

DB 명명 규칙 문서는 대개 비슷하게 생겼습니다. “테이블명은 소문자 스네이크로 한다”로 시작해 서른 줄쯤 이어지다가, 정작 판단이 갈리는 자리에서는 “상황에 따라 협의한다”로 끝납니다. 그리고 반년 뒤 스키마에는 TB_ORDER, orders, order_master가 나란히 존재합니다.

문제는 규칙의 개수가 아니라 결정되지 않은 항목의 개수입니다. 그래서 이 글은 항목마다 선택지를 늘어놓는 대신, 선택지를 제시한 다음 한쪽을 권고합니다. 다른 쪽을 고를 이유가 있는 조직도 있겠지만, 아무것도 고르지 않는 것보다는 어느 쪽이든 고르는 편이 언제나 낫습니다.

규칙을 정하기 전에 합의할 것

이름 이야기를 시작하기 전에 세 가지가 먼저 정해져야 합니다. 이것을 건너뛰면 나머지 규칙이 아무리 정교해도 지켜지지 않습니다.

첫째, 무엇이 표준화 대상인가. 이름을 가진 객체는 테이블과 컬럼만이 아닙니다. 아래 표의 항목 중 규칙이 없는 줄이 있다면, 그 줄은 개발자마다 다르게 만들어집니다.

대상규칙이 없을 때 생기는 일
테이블customer접두사가 팀마다 붙었다 안 붙었다 합니다
컬럼customer_id같은 값에 세 가지 이름이 생깁니다
기본키 제약pk_customerDB 자동 생성명이 섞여 이관 때 충돌합니다
외래키 제약fk_order_customer_id오류 메시지를 읽어도 어느 관계인지 모릅니다
인덱스ix_order_order_dt중복 인덱스를 만들고도 알아채지 못합니다
뷰·시퀀스v_order_summary테이블과 구분되지 않습니다

둘째, 기존 스키마를 소급 적용할 것인가. 권고는 신규 객체에만 적용하고, 기존 객체는 어차피 손대야 할 때 함께 고치는 것입니다. 전면 개명은 애플리케이션·배치·리포트·외부 연계를 동시에 멈추는 작업이고, 되돌릴 수 없습니다. 다만 이름이 낡았다는 사실은 기록해 두어야 합니다. 기록이 없으면 다음 사람은 그 이름을 표준으로 오해하고 복제합니다.

셋째, 예외를 어떻게 다룰 것인가. 예외는 반드시 생깁니다. 외부 기관과 규격을 맞춘 컬럼, 이관해 온 레거시 테이블이 그렇습니다. 권고는 예외를 금지하지 말고 표시하는 것입니다. 표시되지 않은 예외는 곧 표준이 되고, 그다음부터는 누구도 무엇이 규칙인지 말할 수 없게 됩니다.

대소문자와 구분자

선택지는 네 가지입니다. lower_snake, UPPER_SNAKE, camelCase, PascalCase.

여기서 취향보다 먼저 봐야 할 사실이 하나 있습니다. 대부분의 DB는 인용부호로 감싸지 않은 식별자의 대소문자를 한 방향으로 접습니다. 소문자로 접는 DB도 있고 대문자로 접는 DB도 있습니다. 그래서 camelCase로 만든 컬럼은 쓸 때마다 인용부호가 필요하고, 인용부호가 필요해지는 순간 그 의무는 애플리케이션 쿼리, 마이그레이션 스크립트, 덤프, 리포트 도구 전부로 퍼집니다. 그중 하나가 인용을 빠뜨리면 그때 실패합니다.

권고: 스네이크 표기를 쓰고, 대소문자는 그 DB가 접는 방향에 맞춥니다. 소문자로 접는 DB에서는 lower_snake, 대문자로 접는 DB에서는 UPPER_SNAKE입니다. 인용부호로 대소문자를 보존하려는 시도는 하지 않는 편이 좋습니다. 구분자는 _ 하나로 통일합니다. 하이픈은 대부분의 DB에서 인용을 요구하므로 같은 문제를 그대로 안습니다.

단수와 복수

customer인가 customers인가. 오래된 논쟁이고, 양쪽 다 일관되게만 쓰면 실무에서 문제를 일으키지 않습니다. 그래서 결정 기준은 미학이 아니라 자동화 가능성입니다.

권고: 단수입니다. 세 가지 이유가 있습니다.

이미 복수형으로 만들어진 스키마라면 그대로 두시기 바랍니다. 위반은 복수형 자체가 아니라 두 방식이 섞인 상태입니다.

약어를 언제 쓰고 언제 쓰지 않을 것인가

약어 규칙을 “가급적 줄여 쓴다”로 적으면 아무것도 정한 것이 아닙니다. 줄일지 말지를 컬럼마다 판단하게 되고, 판단하는 사람이 매번 다르기 때문입니다. 그 결과가 한 스키마 안의 cust, cstmr, customer입니다.

권고: 약어 여부를 컬럼이 아니라 낱말 단위로 한 번만 결정합니다. 사전에 등록된 약어만 쓰고, 등록되지 않은 낱말은 줄이지 않습니다. 그러면 “이 컬럼을 줄일까”라는 질문 자체가 사라집니다.

등록 기준은 두 가지입니다. 자주 나오는 낱말이거나, 길이 제한을 압박하는 긴 낱말입니다.

낱말권고이유
고객(customer)cust 등록거의 모든 테이블에 나옵니다
번호(number)no 등록num·nbr과 섞이기 쉬워 하나로 못박아야 합니다
일자(date)dt 등록접미사로 매우 자주 쓰입니다
이름(name)등록하지 않음이미 짧아 줄여도 얻는 것이 없습니다
유형(type)등록하지 않음같은 이유입니다

짧은 낱말을 억지로 줄이면 읽기만 나빠집니다. 대략 여섯 자 이하의 영단어는 그대로 두는 편이 좋습니다. 그리고 한 낱말에 약어를 두 개 두는 일은 어떤 경우에도 피해야 합니다. 이 원칙을 실제 자산으로 만드는 방법은 데이터 표준화 글에서 단어사전·용어사전·도메인으로 나누어 다뤘고, 단어사전을 만드는 순서와 운영은 표준단어사전 만드는 법에 따로 정리했습니다.

접두사와 접미사

테이블 접두사(TB_, T_)는 쓰지 않기를 권고합니다. 모든 테이블이 테이블이므로 정보량이 0이고, 길이 한도만 두 글자에서 세 글자씩 갉아먹습니다. 반면 성격이 다른 객체를 구분하는 접두사는 값이 있습니다. 뷰(v_), 임시 테이블(tmp_), 이력 테이블(_hist 접미사)처럼 잘못 조회하면 사고가 나는 것들입니다.

주제영역 접두사(ord_, pay_)는 조건부로 권고합니다. 스키마를 분리할 수 있는 DB라면 스키마로 나누는 편이 낫습니다. 한 스키마에 수백 개의 테이블이 몰리는 환경이라면 접두사가 실제로 도움이 됩니다.

기본키는 <테이블명>_id 형식을 권고합니다. 대안인 id는 짧지만, 조인이 두 개만 겹쳐도 별칭 없이는 어느 id인지 알 수 없습니다. customer_id로 두면 자식 테이블의 외래키와 이름이 같아져 조인 조건이 자명해지고, 컬럼명만 보고도 어느 테이블의 키인지 알 수 있습니다. 비용은 테이블명이 컬럼명에 한 번 반복된다는 것뿐입니다.

외래키는 부모의 기본키와 같은 이름을 씁니다. 한 테이블이 같은 부모를 두 번 참조할 때만 역할 접두사를 붙입니다(shipping_address_id, billing_address_id). 자기참조 관계도 같은 문제를 일으키므로 역할 접두사가 필요합니다.

접미사는 도메인과 1:1로 대응시켜 두는 것이 핵심입니다. 접미사를 보면 타입이 예측되어야 합니다.

접미사의미비고
_id식별자기본키·외래키
_no채번된 업무 번호식별자와 구분합니다
_nm명칭
_dt날짜시각 없음
_dtm일시날짜와 시각
_amt금액통화 단위는 별도 컬럼
_cnt건수
_yn여부아래 참고

플래그 컬럼은 갈리는 지점입니다. _yn 접미사에 CHAR(1)을 쓰는 방식과, is_ 접두사에 네이티브 불리언을 쓰는 방식이 있습니다. 권고는 DB가 불리언 타입을 제대로 지원하면 is_ + 불리언, 아니면 _yn + CHAR(1)이고, 한 스키마 안에서 둘을 섞지 않는 것입니다. 어느 쪽을 택하든 부정형 이름은 피해야 합니다. is_not_deletedN일 때 그 행이 삭제된 것인지 아닌지 잠시 멈춰서 생각하게 된다면, 그 이름은 이미 실패한 이름입니다.

예약어와 길이 제한

order, group, user, level, comment, key, value는 여러 DB에서 예약어이거나 예약어에 준하게 취급됩니다. 인용부호로 감싸면 쓸 수는 있지만, 권고는 아예 쓰지 않는 것입니다. 인용 의무가 모든 쿼리와 도구로 전파되고, 그중 하나만 빠뜨려도 실패하기 때문입니다. DB를 옮기면 예약어 목록도 함께 바뀌므로, 오늘 통과한 이름이 다음 이관에서 걸릴 수도 있습니다.

실무에서 이것을 막는 가장 확실한 방법은 검사보다 앞단에 있습니다. 낱말 사전에서 “주문”의 표준 약어를 order가 아니라 ord로 고정해 두면, order라는 이름은 애초에 만들어질 수 없습니다.

길이 제한은 방언마다 다릅니다. 구체적인 숫자는 제품과 버전에 따라 달라지므로 규칙 문서에 특정 숫자를 적어 두는 것은 위험하지만, 차이의 모양은 알아 둘 필요가 있습니다.

가장 먼저 한도에 부딪히는 것은 테이블명이 아니라 자동 생성되는 제약명과 인덱스명입니다. fk_ 접두사에 테이블명과 컬럼명이 이어 붙으므로, 테이블명이 여유 있어 보여도 제약명은 이미 한도를 넘고 있을 수 있습니다.

권고: 이식할 가능성이 있는 DB 중 가장 낮은 한도를 팀의 기준값으로 삼고, 모든 이름을 그 값으로 검사합니다. 그리고 검사 대상에 제약명과 인덱스명을 반드시 포함시킵니다.

정한 규칙이 지켜지게 만드는 법

여기까지 정하고 나면 문서가 한 장 나옵니다. 그리고 대부분의 팀에서 그 문서는 그대로 잊힙니다. 이유는 단순합니다. 규칙을 어기는 데 아무 비용이 들지 않기 때문입니다.

검사할 수 있는 시점은 세 곳입니다.

  1. 이름을 만드는 순간 — 위반을 만들 수 없게 합니다.
  2. 리뷰 시점 — 만든 뒤에 지적합니다. 이미 코드가 그 이름에 의존하기 시작했습니다.
  3. 점검 시점 — 분기 점검에서 발견합니다. 되돌리기에는 늦었습니다.

뒤로 갈수록 고치는 비용이 커지고, 그래서 뒤로 갈수록 “이번만 예외로”가 늘어납니다. 권고는 명확합니다. 물리명을 사람이 타이핑하지 않게 만드는 것입니다. 논리명을 업무 언어로 적고, 단어사전과 명명 규칙이 물리명을 만들어 내면, 위반은 발생한 뒤에 잡히는 것이 아니라 애초에 발생하지 않습니다.

Sqemo는 이 순서로 동작하는 ERD 도구입니다. 논리명을 적으면 단어사전과 명명 규칙(구분자·대소문자·미등록 낱말 처리)에 따라 물리명이 생성되고, 사전에 없는 낱말은 경고로 표시하거나 그대로 쓰거나 로마자로 옮기도록 선택할 수 있습니다. 예외가 필요한 자리는 수동으로 지정하되 그렇게 표시되고 자동 재생성에서 빠집니다. 선택한 방언의 식별자 길이 한도를 알고 있어 제약명과 인덱스명까지 초과 여부를 검사하고, 대소문자만 다른 중복 컬럼명처럼 눈으로는 지나치기 쉬운 위반도 함께 잡습니다 — 이 두 검사는 앱 화면과 lint_erd MCP 도구가 담당합니다. 팀 단위로는 워크스페이스의 표준 사전을 여러 ERD가 함께 씁니다. CI에는 npx sqemo-mcp lint schema.erd.json 을 걸 수 있는데, 이 명령이 보는 것은 미등록 낱말(경고 모드일 때)과 물리명 드리프트 두 가지이고 위반이 있으면 빌드가 실패합니다. 생성 규칙의 세부 동작은 Naming Standards 문서 (영문)에 정리되어 있습니다.

명명 규칙 문서를 다시 쓰기 전에, 지금 스키마가 위 항목 중 몇 개에서 실제로 일관적인지 먼저 세어 보시길 권합니다. 쓰던 DDL을 브라우저에서 바로 열어 가져오면, 지금 규칙과 실제 이름이 어디서 갈라져 있는지 몇 분 만에 확인하실 수 있습니다.