SI DB 운영 · 스키마 설계와 표준 · 이론
왜 컬럼 이름부터 정하는가
한 줄 요약
컬럼 이름을 먼저 정하는 이유는 미관이 아니라, 표준이 없으면 같은 뜻의 컬럼이 이름도 길이도 다르게 세 벌 생기고 그 대가를 3 년 뒤 이관에서 치르기 때문이다.
왜 이게 문제인가
표준이 없어도 개발은 잘 굴러간다. 각 팀이 각자 합리적으로 이름을 짓기 때문이다. 문제는 그 셋을 합칠 때 비로소 드러난다 — 통합 조회 화면, 정산 대사, 그리고 차세대 이관.
가장 아픈 것은 길이다. 고객명을 한 곳은 50 자, 다른 곳은 30 자로 잡아 두면, 긴 이름이 들어오는 날 조용히 잘린 채로 적재된다. 오류가 나지 않으므로 아무도 모르고, 몇 달 뒤 "이 고객 이름이 왜 이래요" 라는 문의로 발견된다.
3년 뒤에 벌어지는 일
프로젝트 초기에 데이터 표준을 안 잡으면 이렇게 된다.
고객관리 팀: CUSTOMER (CUST_NAME VARCHAR(50))주문 팀: ORDERS (CUSTOMER_NM VARCHAR(100))정산 팀: SETTLEMENT (CUST_NM VARCHAR(30))같은 고객명인데 이름이 셋, 길이가 셋이다. 처음엔 아무 문제 없다.
문제는 조인할 때, 통합 조회를 만들 때, 그리고 3년 뒤 데이터를 이관할 때 터진다.
- 통합 검색 화면을 만들려니 세 테이블의 컬럼명을 매번 다르게 써야 한다
- 정산 팀 테이블에 50자 이름을 넣으려니 잘린다. 조용히 잘린다
- 차세대 프로젝트에서 이관하려니 매핑표를 사람이 손으로 만들어야 한다
그래서 SI 프로젝트는 개발 시작 전에 데이터 표준을 확정한다.
공공 사업이면 「공공기관의 데이터베이스 표준화 지침」에 따라
표준단어·표준도메인·표준용어·표준코드 정의서를 산출물로 낸다.
표준의 네 단계
1. 표준단어사전 의미의 최소 단위와 그 약어 고객→CUST, 주문→ORD, 상품→PROD, 명칭→NM, 일자→DT, 금액→AMT, 수량→QTY, 번호→NO, 여부→YN, 코드→CD2. 표준도메인 같은 성격의 값이 갖는 타입·길이 규칙 금액 → NUMBER(15,2) 일자 → CHAR(8) 여부 → CHAR(1) Y/N 코드 → VARCHAR(10) 명칭 → VARCHAR(100) 번호(내부키) → NUMBER(18)3. 표준용어 단어를 조합한 논리명과 물리명 고객명 → CUST_NM (도메인: 명칭) 주문금액 → ORD_AMT (도메인: 금액) 주문일자 → ORD_DT (도메인: 일자)4. 컬럼 정의 실제 DDL ORD_AMT NUMBER(15,2) NOT NULL DEFAULT 0이 순서를 지키면 누가 만들어도 같은 이름과 같은 타입이 나온다.
그리고 새 컬럼이 필요할 때 "이건 어떤 도메인인가"만 정하면 나머지는 자동이다.
관행에 대한 솔직한 이야기
국내 SI 에는 마음에 안 들 수 있는 관행이 있다.
- 날짜를
CHAR(8) YYYYMMDD로 — DATE 타입이 더 낫다. 맞다. - 여부를
CHAR(1) Y/N으로 — BOOLEAN 이 더 낫다. 맞다. - 모든 테이블에
REG_DT,REG_ID,UPD_DT,UPD_ID— 감사 컬럼. 이건 꽤 유용하다. - 논리 삭제(
DEL_YN) — 물리 삭제를 안 한다. 복구와 감사 때문.
앞의 둘은 레거시 호환 때문이다. 새 시스템만 다르게 가면
**연동·이관 지점마다 변환 코드가 생기고, 한 군데라도 빠지면
조용히 잘못된 데이터가 쌓인다.** 그 비용이 타입 개선의 이득보다 클 수 있다.
표준은 '최선'이 아니라 '합의'다. 이 문장을 이해하면
"왜 이렇게 후진 방식을 쓰죠"라는 질문 대신
"이 표준을 언제 어떻게 바꿀 수 있을까"를 묻게 된다.
다만 논리 삭제는 주의가 필요하다. DEL_YN='N' 조건을 빠뜨린 쿼리 하나가
삭제된 데이터를 화면에 뿌린다. 그래서 조회는 항상 뷰를 통해서 하도록
설계하거나, 최소한 코드 리뷰 체크리스트에 넣는다.
제약조건은 문서가 아니라 코드다
테이블정의서에 "필수 항목"이라고 적어 놓고 DDL 에는 NOT NULL 이 없으면,
그 문서는 지켜지지 않는다. 제약은 DB 에 걸어야 강제된다.
| 제약 | 무엇을 막는가 | SI 에서의 현실 |
| --- | --- | --- |
| PRIMARY KEY | 중복 행 | 반드시 건다 |
| NOT NULL | 필수값 누락 | 반드시 건다 |
| CHECK | 값 범위/코드값 위반 | 자주 생략된다. 걸어야 한다 |
| UNIQUE | 업무 키 중복 | 자주 생략된다. 중복 데이터 사고의 주범 |
| FOREIGN KEY | 참조 무결성 | 논쟁적이다 |
| DEFAULT | 예상 못 한 NULL | 걸어 두면 코드가 단순해진다 |
FK 가 논쟁적인 이유가 있다. 대량 배치에서 FK 검사가 성능을 먹고,
이관 시 적재 순서를 강제하며, 논리 삭제와 충돌한다.
그래서 "FK 는 개발계에만 걸고 운영계에서는 뺀다"는 조직도 있다.
어느 쪽이든 명시적으로 결정하고 문서화하라. 최악은
"어떤 테이블엔 있고 어떤 테이블엔 없는" 상태다.
그러면 무결성을 애플리케이션이 보장하는지 DB 가 보장하는지 아무도 모른다.
인덱스는 설계 산출물이다
인덱스를 "느리면 나중에 추가하는 것"으로 취급하면 오픈 후에 고생한다.
설계 단계에서 조회 패턴을 정리하고 인덱스를 함께 설계한다.
화면 SCR-021 주문조회: WHERE CUST_ID = ? AND ORD_DT BETWEEN ? AND ? → IX_ORDERS_01 (CUST_ID, ORD_DT)배치 BAT-005 일마감: WHERE ORD_DT = ? AND ORD_STS_CD = '완료' → IX_ORDERS_02 (ORD_DT, ORD_STS_CD)복합 인덱스의 컬럼 순서가 핵심이다. 규칙은 이렇다.
1. 등치 조건(=)으로 쓰는 컬럼을 앞에
2. 범위 조건(BETWEEN, >, <)으로 쓰는 컬럼을 뒤에
3. 선택도가 좋은(값 종류가 많은) 컬럼을 앞쪽에
(CUST_ID, ORD_DT) 와 (ORD_DT, CUST_ID) 는 완전히 다른 인덱스다.WHERE CUST_ID = ? AND ORD_DT BETWEEN ? AND ? 에는 전자가 맞다.
후자는 날짜 범위 전체를 훑고 그 안에서 고객을 걸러야 한다.
그리고 인덱스는 공짜가 아니다. INSERT/UPDATE/DELETE 마다 인덱스도
갱신되므로, 인덱스 10개짜리 테이블에 대량 적재를 하면 매우 느리다.
데이터 이관 때 인덱스를 지우고 적재한 뒤 다시 만드는 이유가 이것이다.
코드 테이블 — 하드코딩과의 전쟁
주문상태를 '01', '02', '03' 으로 저장한다고 하자.
이 값의 의미는 어디 있는가?
- 애플리케이션 상수 — 화면마다 다르게 하드코딩된다
- 공통코드 테이블 — 한 곳에서 관리, 운영 중 추가 가능
SI 에서는 거의 항상 후자다. 그리고 대개 이런 구조다.
CREATE TABLE COMMON_CODE ( GRP_CD VARCHAR(20) NOT NULL, -- 'ORD_STS' CD VARCHAR(20) NOT NULL, -- '01' CD_NM VARCHAR(100) NOT NULL, -- '접수' SORT_NO INTEGER NOT NULL, USE_YN CHAR(1) NOT NULL DEFAULT 'Y', PRIMARY KEY (GRP_CD, CD));주의점 하나: 공통코드에 있다고 아무거나 들어올 수 있으면 안 된다.
상태 컬럼에 CHECK 를 걸거나, 최소한 코드값 검증을 어디서 하는지 정해야 한다.
"공통코드 테이블이 있으니 괜찮다"는 말은 아무도 검증하지 않는다는 뜻일 때가 많다.
테이블정의서는 DB 에서 뽑는다
설계 산출물로 테이블정의서를 내는데, 개발이 끝날 때쯤이면
문서와 실제 스키마가 어긋나 있다. 항상 그렇다.
그래서 실무 요령: 최종 테이블정의서는 실제 DB 메타데이터에서 생성한다.
컬럼 목록·타입·NULL 여부·기본값·제약은 쿼리로 뽑을 수 있다.
사람이 채워야 할 것은 설명(comment) 뿐이다.
그리고 그 설명은 DB 의 COMMENT 기능에 넣어 두면 문서와 스키마가 영원히 함께 간다.
이게 안 되는 DBMS 를 쓴다면 최소한 생성 스크립트를 형상관리에 두고
거기서 문서를 만든다. 문서를 손으로 유지하는 순간 그 문서는 곧 거짓말이 된다.
현장에서 만나는 모습
표준을 정해 놓고도 지켜지지 않는 것이 더 흔한 실패다. 정의서는 있는데 테이블에는 표준에 없는 이름이 섞여 있고, 아무도 그걸 검사하지 않는다.
그래서 표준은 문서가 아니라 검사할 수 있는 형태로 두어야 한다. 테이블정의서를 손으로 쓰지 않고 pragma_table_info 나 시스템 카탈로그에서 뽑는 것도 같은 이유다 — 손으로 쓴 정의서는 반드시 실제 스키마와 어긋나고, 어긋난 정의서는 있는 것보다 나쁘다. 읽는 사람이 그것을 믿기 때문이다.
제약조건도 마찬가지다. "수량은 1 이상이어야 합니다" 가 문서에만 있으면 언젠가 0 이 들어온다. CHECK 으로 걸어 두면 그 문장은 코드가 되고, 지켜지지 않을 방법이 없어진다.