데이터베이스를 다루는 언어인 SQL(Structured Query Language)은 크게 세 가지, 데이터를 정의하는 DDL(Data Definition Language), 조작하는 DML(Data Manipulation Language), 제어하는 DCL(Data Control Language)로 나뉩니다. 이 중 DDL은 데이터베이스라는 거대한 건축물의 설계도이자 골격을 만드는 가장 근본적인 언어입니다. 우리가 데이터를 저장하고 활용하기 전에 데이터가 어떤 구조와 형식으로 존재할지를 먼저 정의해야 하는데, 바로 이 역할을 DDL이 수행합니다.
DDL은 마치 건축가가 건물을 짓기 전에 땅의 용도를 정하고(도메인), 전체적인 설계도(스키마)를 그리며, 방(테이블)을 만들고, 창문(뷰)을 내고, 각 방을 쉽게 찾아갈 수 있도록 안내판(인덱스)을 설치하는 과정과 같습니다. 이처럼 DDL은 데이터의 구조를 정의하고, 제약 조건을 설정하며, 관계를 구축하는 모든 과정을 관장합니다. 이번 글에서는 DDL의 핵심 작업 대상인 도메인, 스키마, 테이블, 뷰, 인덱스가 각각 무엇이며, 이들이 어떻게 유기적으로 연결되어 견고한 데이터베이스 구조를 만들어내는지 심도 있게 알아보겠습니다.
도메인 (Domain): 데이터의 국적과 신분을 정하다
가장 먼저 살펴볼 DDL의 대상은 다소 생소할 수 있는 ‘도메인’입니다. 도메인은 데이터베이스에 저장될 데이터의 ‘타입’과 ‘허용 가능한 값의 범위’를 미리 정의하는 객체입니다. 쉽게 말해, 특정 속성(Attribute)에 입력될 데이터의 국적과 신분을 정해주는 규칙의 집합이라고 할 수 있습니다. 예를 들어, ‘성별’이라는 속성에는 ‘남’, ‘여’라는 값만 들어와야 하고, ‘나이’ 속성에는 0 이상의 숫자만 와야 한다는 규칙을 도메인으로 정의할 수 있습니다.
이렇게 도메인을 먼저 정의해두면, 여러 테이블에서 동일한 의미와 제약 조건을 가진 속성을 사용해야 할 때 매우 유용합니다. 가령 ‘직원’ 테이블의 ‘성별’ 속성과 ‘고객’ 테이블의 ‘성별’ 속성이 모두 ‘남’ 또는 ‘여’라는 값만 가져야 한다면, ‘GENDER_DOMAIN’이라는 도메인을 하나 만들어두고 두 테이블의 ‘성별’ 속성이 이 도메인을 따르도록 지정하면 됩니다. 이렇게 하면 코드의 재사용성이 높아져 일관성을 유지하기 쉽고, 향후 ‘성별’에 대한 규칙이 변경될 때(예: ‘기타’ 추가) 도메인만 수정하면 이를 참조하는 모든 속성에 일괄적으로 변경 사항이 적용되어 유지보수가 매우 편리해집니다.
도메인의 역할과 장점
도메인의 핵심적인 역할은 데이터의 무결성을 보장하는 것입니다. 특정 속성에 들어올 수 있는 값의 종류와 범위를 사전에 제한함으로써, 잘못된 데이터가 입력되는 것을 원천적으로 차단합니다. 예를 들어, ‘학점’을 나타내는 속성에 ‘A+’, ‘F’ 같은 값이 아닌 ‘Z’나 ‘Excellent’ 같은 엉뚱한 값이 들어오는 것을 막을 수 있습니다. 이는 데이터의 일관성과 정확성을 높이는 데 결정적인 기여를 합니다.
또한, 도메인은 데이터베이스의 논리적인 설계를 더욱 명확하게 만들어줍니다. ‘가격’을 나타내는 속성은 ‘0 이상의 양수를 허용하는 숫자 타입’이라는 도메인을 사용하고, ‘우편번호’는 ‘5자리 숫자로 구성된 문자열’이라는 도메인을 사용하도록 정의함으로써, 각 속성이 담고 있는 데이터의 의미를 직관적으로 파악할 수 있게 돕습니다. 이는 여러 개발자가 협업하는 대규모 프로젝트에서 특히 중요하며, 시스템 전체의 이해도를 높이는 효과를 가져옵니다.
최근의 데이터베이스 관리 시스템(DBMS)에서는 이러한 도메인의 개념을 더욱 확장하여 사용자가 직접 데이터 타입을 정의하는 ‘사용자 정의 타입(User-Defined Type)’ 기능을 제공하기도 합니다. 이는 단순한 값의 범위를 넘어 복잡한 구조를 가진 데이터 타입을 직접 만들어 재사용할 수 있게 함으로써 객체지향적인 데이터베이스 설계를 가능하게 합니다.
스키마 (Schema): 데이터베이스의 청사진
도메인이 개별 데이터의 규칙을 정하는 것이라면, 스키마는 데이터베이스의 전체적인 구조와 제약 조건, 관계를 종합적으로 담고 있는 설계도입니다. 스키마는 데이터베이스에 어떤 테이블들이 존재하고, 각 테이블은 어떤 속성들로 구성되며, 속성들의 데이터 타입은 무엇인지, 그리고 테이블 간에는 어떤 관계(기본키, 외래키 등)가 맺어져 있는지를 총체적으로 정의합니다.
스키마는 데이터베이스를 바라보는 관점에 따라 세 가지 계층으로 나눌 수 있습니다. 가장 바깥쪽에는 사용자가 실제로 데이터를 조작하고 조회할 때 사용하는 ‘외부 스키마(External Schema)’가 있습니다. 이는 전체 데이터베이스 중에서 사용자가 필요로 하는 일부만을 보여주는 ‘뷰(View)’의 개념과 유사하며, 사용자마다 다른 관점의 데이터 구조를 가질 수 있습니다.
그 안쪽에는 데이터베이스 관리자(DBA)의 관점에서 모든 데이터의 논리적인 구조와 관계를 정의하는 ‘개념 스키마(Conceptual Schema)’가 있습니다. 우리가 흔히 ‘스키마’라고 부르는 것이 바로 이 개념 스키마에 해당하며, 데이터베이스 전체에 대한 단 하나의 정의를 가집니다. 여기에는 모든 테이블, 속성, 관계, 제약 조건 등이 포함됩니다.
가장 깊은 곳에는 데이터가 물리적인 저장 장치에 실제로 어떻게 저장되는지를 정의하는 ‘내부 스키마(Internal Schema)’가 있습니다. 여기에는 데이터의 저장 방식, 인덱스의 구조, 데이터 압축 방법 등 물리적인 측면에 대한 상세한 내용이 포함됩니다. DDL은 주로 개념 스키마와 일부 외부 스키마를 정의하는 데 사용됩니다.
스키마의 중요성과 역할
스키마는 데이터베이스의 일관성과 무결성을 유지하는 중심축 역할을 합니다. 스키마에 정의된 규칙(예: 특정 속성은 NULL 값을 허용하지 않음, 특정 속성의 값은 고유해야 함)을 통해 데이터의 중복이나 누락, 오류를 방지할 수 있습니다. 예를 들어, ‘학생’ 테이블의 ‘학번’ 속성에 ‘UNIQUE’ 제약 조건을 걸어두면, 동일한 학번을 가진 학생이 두 번 등록되는 것을 시스템 차원에서 막을 수 있습니다.
또한, 스키마는 데이터 독립성을 보장하는 기반이 됩니다. 데이터 독립성이란, 하위 계층의 스키마를 변경하더라도 상위 계층의 스키마나 응용 프로그램에는 영향을 미치지 않는 성질을 말합니다. 예를 들어, 내부 스키마에서 데이터의 저장 위치나 방식을 변경하더라도(물리적 독립성), 개념 스키마나 외부 스키마는 그대로 유지되므로 사용자는 아무런 변화를 느끼지 못합니다. 마찬가지로, 개념 스키마에 새로운 테이블이나 속성이 추가되더라도(논리적 독립성), 기존의 외부 스키마를 사용하는 응용 프로그램은 수정 없이 그대로 사용할 수 있습니다. 이러한 데이터 독립성은 시스템의 유연성과 확장성을 크게 향상시킵니다.
현대의 클라우드 기반 데이터베이스 서비스(DBaaS)나 데이터 웨어하우스 환경에서는 스키마 관리가 더욱 중요해지고 있습니다. 예를 들어, AWS Redshift나 Google BigQuery 같은 서비스에서는 데이터 분석 성능을 최적화하기 위해 스키마 설계 시 데이터의 분포(Distribution)나 정렬 키(Sort Key)를 신중하게 고려해야 합니다. 이는 전통적인 스키마의 역할을 넘어 물리적인 데이터 배치까지 제어하여 성능을 극대화하는 사례라고 볼 수 있습니다.
테이블 (Table): 데이터가 사는 집
테이블은 DDL을 통해 생성되는 가장 기본적인 데이터 저장 단위입니다. 관계형 데이터베이스에서 모든 데이터는 행(Row, 튜플)과 열(Column, 속성)으로 구성된 2차원 표 형태의 테이블에 저장됩니다. DDL의 CREATE TABLE 구문은 바로 이 테이블이라는 집을 짓는 명령어입니다.
테이블을 생성할 때는 테이블의 이름과 함께 테이블을 구성할 각 열의 이름과 데이터 타입, 그리고 다양한 제약 조건을 정의해야 합니다. 예를 들어, ‘학생’ 테이블을 만든다고 가정해 봅시다. 이 테이블에는 ‘학번’, ‘이름’, ‘전공’, ‘학년’ 등의 열이 필요할 것입니다. 이때 ‘학번’은 중복되지 않는 고유한 값이므로 기본키(Primary Key)로 지정하고, ‘이름’과 ‘전공’은 비워둘 수 없도록 ‘NOT NULL’ 제약 조건을 추가할 수 있습니다. ‘학년’은 1에서 4 사이의 숫자만 입력 가능하도록 ‘CHECK’ 제약 조건을 설정할 수도 있습니다.
테이블 설계와 제약 조건
좋은 테이블 설계는 데이터베이스 전체의 성능과 안정성을 좌우합니다. 테이블을 설계할 때는 정규화(Normalization) 과정을 통해 데이터의 중복을 최소화하고, 데이터 간의 종속 관계를 명확하게 만들어야 합니다. 이는 데이터의 일관성을 유지하고, 데이터 수정 시 발생할 수 있는 이상 현상(Anomaly)을 방지하는 데 필수적입니다.
DDL은 테이블을 정의할 때 다음과 같은 다양한 제약 조건을 활용하여 데이터의 무결성을 강제합니다.
- NOT NULL: 해당 열에 NULL 값이 입력되는 것을 허용하지 않습니다.
- UNIQUE: 해당 열의 모든 값은 유일해야 합니다. NULL 값은 여러 개 존재할 수 있습니다. (DBMS에 따라 다름)
- PRIMARY KEY: NOT NULL과 UNIQUE 제약 조건을 모두 만족하며, 테이블의 각 행을 식별하는 유일한 키입니다. 테이블당 하나만 지정할 수 있습니다.
- FOREIGN KEY: 다른 테이블의 기본키를 참조하는 열로, 테이블 간의 관계를 맺어주는 역할을 합니다. 참조 무결성을 보장합니다.
- CHECK: 해당 열에 입력될 수 있는 값의 조건을 명시합니다. (예:
나이 > 0)
이러한 제약 조건들은 데이터베이스 스스로가 데이터의 정합성을 지키도록 만드는 강력한 도구입니다. 응용 프로그램 레벨에서 데이터의 유효성을 검사할 수도 있지만, 데이터베이스 테이블 자체에 제약 조건을 설정해두면 어떤 경로로 데이터가 들어오든 일관된 규칙을 적용할 수 있어 훨씬 안정적입니다.
최신 트렌드로는 기존의 정형 데이터를 다루는 관계형 테이블뿐만 아니라, JSON, XML과 같은 반정형 데이터를 저장하고 처리할 수 있는 기능을 테이블에 통합하는 경우가 많아지고 있습니다. PostgreSQL의 JSONB 타입이나 MySQL의 JSON 타입은 스키마가 유연한 데이터를 관계형 테이블 내에서 효율적으로 다룰 수 있게 해주어, DDL의 CREATE TABLE 구문도 이러한 새로운 데이터 타입을 지원하도록 발전하고 있습니다.
뷰 (View): 데이터를 바라보는 가상의 창문
뷰는 하나 이상의 테이블로부터 유도된 가상의 테이블입니다. 실제 데이터를 저장하고 있지는 않지만, 사용자에게는 마치 실제 테이블처럼 보입니다. 뷰는 미리 정의된 SQL 쿼리문을 통해 기존 테이블의 데이터를 조합하거나 특정 조건에 맞는 데이터만을 선택하여 보여주는 역할을 합니다. 즉, 데이터를 바라보는 하나의 ‘창문’과 같습니다.
예를 들어, ‘직원’ 테이블에 ‘이름’, ‘부서’, ‘급여’, ‘개인 연락처’ 등의 민감한 정보가 포함되어 있다고 가정해 봅시다. 모든 사용자에게 이 테이블 전체를 보여주는 것은 보안상 위험할 수 있습니다. 이때 DDL의 CREATE VIEW 구문을 사용하여 ‘이름’과 ‘부서’ 열만 포함하는 ‘부서별_직원_목록’이라는 뷰를 만들 수 있습니다. 이렇게 하면 사용자들은 뷰를 통해 허용된 데이터에만 접근할 수 있게 되어 데이터 보안 수준을 높일 수 있습니다.
뷰의 장점과 활용 사례
뷰의 가장 큰 장점 중 하나는 논리적 데이터 독립성을 제공한다는 것입니다. 뷰의 기반이 되는 테이블의 구조가 변경되더라도, 뷰의 정의만 수정하면 뷰를 사용하는 응용 프로그램은 변경할 필요가 없습니다. 예를 들어, ‘학생’ 테이블이 ‘학생_기본정보’와 ‘학생_성적정보’ 테이블로 분리되더라도, 두 테이블을 조인(JOIN)하여 기존 ‘학생’ 테이블과 동일한 구조로 보여주는 뷰를 만들면, 응용 프로그램은 테이블 구조 변경을 인지하지 못하고 이전과 동일하게 작동할 수 있습니다.
또한, 뷰는 복잡한 SQL 쿼리를 단순화하는 데 매우 효과적입니다. 여러 테이블을 조인하고 복잡한 조건을 거쳐야 하는 쿼리가 자주 사용된다면, 이 쿼리 자체를 뷰로 만들어 저장해둘 수 있습니다. 그러면 사용자들은 길고 복잡한 쿼리문 대신 간단한 SELECT * FROM MY_VIEW; 구문만으로 원하는 결과를 얻을 수 있습니다. 이는 쿼리의 재사용성을 높이고 사용자의 편의성을 증대시킵니다.
최근에는 데이터 분석 및 비즈니스 인텔리전스(BI) 분야에서 뷰의 활용도가 더욱 높아지고 있습니다. 분석가들은 원본 데이터를 직접 건드리지 않고, 분석 목적에 맞게 데이터를 가공하고 조합한 다양한 뷰를 생성하여 리포트를 작성하거나 시각화 자료를 만듭니다. 특히, 데이터 웨어하우스 환경에서는 사실 테이블(Fact Table)과 차원 테이블(Dimension Table)을 조인하여 의미 있는 정보를 추출하는 ‘스타 스키마(Star Schema)’ 구조를 뷰로 미리 만들어두는 경우가 많습니다.
다만, 뷰는 실제 데이터를 저장하지 않는 가상 테이블이므로, 뷰에 대한 데이터 수정(INSERT, UPDATE, DELETE)에는 제약이 따를 수 있습니다. 여러 테이블을 조인하거나 집계 함수를 사용한 뷰는 대부분 수정이 불가능하며, 수정 가능한 뷰라 할지라도 몇 가지 엄격한 조건을 만족해야 합니다.
인덱스 (Index): 데이터 검색 속도를 높이는 초고속 엘리베이터
인덱스는 테이블의 데이터 검색 속도를 획기적으로 향상시키기 위해 사용하는 데이터 구조입니다. 책의 맨 뒤에 있는 ‘찾아보기’와 같은 원리로 작동합니다. 우리가 책에서 특정 단어를 찾을 때 처음부터 끝까지 모든 페이지를 넘겨보는 대신, 찾아보기에서 해당 단어가 있는 페이지 번호를 바로 찾아가는 것처럼, 인덱스는 특정 데이터가 테이블의 어느 위치에 저장되어 있는지를 빠르게 알려줍니다.
사용자가 WHERE 절을 사용하여 특정 조건의 데이터를 검색하는 쿼리를 실행하면, 데이터베이스 시스템은 먼저 해당 열에 인덱스가 있는지 확인합니다. 만약 인덱스가 존재한다면, 시스템은 테이블 전체를 스캔(Full Table Scan)하는 대신 인덱스를 탐색하여 원하는 데이터의 물리적 주소를 신속하게 찾아냅니다. 이는 대용량 테이블에서 엄청난 성능 향상을 가져옵니다.
DDL의 CREATE INDEX 구문을 사용하여 특정 테이블의 하나 이상의 열에 대해 인덱스를 생성할 수 있습니다. 기본키(Primary Key)나 고유키(Unique Key) 제약 조건이 있는 열은 대부분의 DBMS에서 자동으로 인덱스가 생성됩니다.
인덱스의 원리와 장단점
인덱스는 일반적으로 B-Tree(Balanced Tree)라는 자료 구조를 사용하여 구현됩니다. B-Tree는 데이터가 정렬된 상태로 저장되어 있어, 특정 값을 찾는 데 매우 효율적인 탐색 성능을 보장합니다. 이 외에도 데이터의 종류나 쿼리 패턴에 따라 해시 인덱스(Hash Index), 전문 검색 인덱스(Full-text Index), 공간 인덱스(Spatial Index) 등 다양한 종류의 인덱스가 사용됩니다.
인덱스의 가장 큰 장점은 단연 검색(SELECT) 성능의 향상입니다. 하지만 세상에 공짜는 없듯이, 인덱스에도 단점이 존재합니다. 우선, 인덱스는 원본 데이터와는 별도의 저장 공간을 차지합니다. 인덱스를 많이 만들수록 더 많은 디스크 공간이 필요하게 됩니다.
더 중요한 단점은 데이터 변경(INSERT, UPDATE, DELETE) 작업의 성능 저하입니다. 테이블에 새로운 데이터가 추가되거나 기존 데이터가 수정/삭제될 때마다, 데이터베이스 시스템은 해당 변경 사항을 인덱스에도 똑같이 반영해야 합니다. 이 과정에서 인덱스를 재정렬하는 등의 부가적인 작업이 발생하여 DML 작업의 속도가 느려질 수 있습니다. 따라서 무분별하게 인덱스를 많이 생성하는 것은 오히려 전체 시스템 성능에 악영향을 줄 수 있습니다. 인덱스는 검색이 빈번하고 데이터 변경이 상대적으로 적은 열에 대해 신중하게 생성해야 합니다.
최근의 데이터베이스 기술 트렌드 중 하나인 인메모리 데이터베이스(In-Memory Database)나 컬럼 기반 스토리지(Columnar Storage)는 전통적인 B-Tree 인덱스와는 다른 방식으로 빠른 검색 속도를 구현합니다. 하지만 여전히 대부분의 OLTP(Online Transaction Processing) 시스템에서는 B-Tree 인덱스가 데이터 검색 성능을 보장하는 핵심적인 기술로 널리 사용되고 있습니다.
마무리: 견고한 데이터베이스 설계를 위한 첫걸음
지금까지 데이터 정의어(DDL)의 주요 대상인 도메인, 스키마, 테이블, 뷰, 인덱스에 대해 알아보았습니다. 이 다섯 가지 요소는 각각 독립적으로 존재하기보다는 서로 유기적으로 연결되어 데이터베이스라는 하나의 거대한 구조물을 이룹니다. 도메인으로 데이터의 규칙을 정하고, 스키마로 전체적인 뼈대를 그리며, 테이블에 데이터를 차곡차곡 쌓고, 뷰를 통해 필요한 창을 내고, 인덱스로 데이터로 가는 지름길을 만드는 이 모든 과정이 바로 DDL의 역할입니다.
견고하고 효율적인 데이터베이스를 구축하기 위해서는 이러한 DDL의 대상을 정확히 이해하고 목적에 맞게 활용하는 것이 무엇보다 중요합니다. 데이터의 특성을 고려하여 적절한 도메인과 제약 조건을 설정하고, 정규화를 통해 중복을 최소화하는 테이블을 설계하며, 보안과 편의성을 위해 뷰를 활용하고, 쿼리 성능을 최적화하기 위해 신중하게 인덱스를 생성하는 능력은 모든 데이터 전문가가 갖춰야 할 핵심 역량입니다. DDL을 자유자재로 다루는 것은 단순히 문법을 아는 것을 넘어, 데이터의 본질을 꿰뚫고 미래의 변화까지 예측하는 통찰력을 필요로 하는 일이며, 이는 성공적인 데이터 기반 시스템을 만드는 가장 중요한 첫걸음이 될 것입니다.
