데이터베이스 스키마란?

src/content/documents/data-analysis/database-schema-guide.json

데이터베이스를 공부하다 보면 **스키마(Schema)**라는 말을 매우 자주 만나게 됩니다.

예를 들어 PostgreSQL에서 다음과 같은 SQL을 볼 수 있습니다.

SELECT *
FROM game.player;

여기에서

game.player

는 단순히 테이블 이름이 아닙니다.

game   → Schema
player → Table

이라는 의미입니다.

스키마를 가장 쉽게 설명하면 다음과 같습니다.

데이터베이스 안에서 테이블이나 뷰 같은 객체들을 묶어서 관리하는 하나의 영역

다만 스키마라는 단어는 문맥에 따라 데이터베이스 전체 구조를 의미하기도 하고, PostgreSQL 같은 DBMS에서는 **데이터베이스 내부의 이름 공간(namespace)**을 의미하기도 합니다.

이번 글에서는 두 가지 의미를 모두 게임 데이터베이스 예시를 통해 살펴보겠습니다.


스키마를 이해하기 전에 구조부터 보기

게임 서비스를 운영한다고 생각해봅시다.

하나의 게임에는 다양한 데이터가 필요합니다.

플레이어
아이템
길드
몬스터
퀘스트
결제
운영자
로그

이것들을 전부 하나의 공간에 넣을 수도 있습니다.

player
item
guild
monster
quest
payment
admin
battle_log
login_log

하지만 시스템이 커질수록 테이블이 수십 개, 수백 개로 증가합니다.

이때 관련 있는 테이블들을 묶어서 관리하면 훨씬 편리합니다.

예를 들어

데이터베이스 구조game — player — guild — item — inventoryshop — product — payment — purchase_historyadmin — administrator — permissionlog — login_log — battle_log
데이터베이스 구조

처럼 구분할 수 있습니다.

여기에서

game
shop
admin
log

가 각각 스키마가 될 수 있습니다.


스키마를 폴더처럼 생각해보기

처음 배울 때는 스키마를 폴더처럼 생각하면 이해하기 쉽습니다.

컴퓨터에 다음과 같은 파일 구조가 있다고 생각해봅시다.

데이터베이스 구조GameProject| —— Character| —— player.txt| —— guild.txt| —— Shop| —— item.txt| —— payment.txt| —— Log —— login.txt —— battle.txt
데이터베이스 구조

데이터베이스에서도 비슷하게

데이터베이스 구조Database| —— game| —— player| —— guild| —— inventory| —— shop| —— item| —— payment| —— log —— login_log —— battle_log
데이터베이스 구조

처럼 나눌 수 있습니다.

따라서 처음에는

Database = 큰 프로젝트

Schema = 폴더

Table = 파일

정도로 생각해도 좋습니다.

다만 실제로 스키마는 단순한 폴더보다 더 많은 역할을 합니다.

객체 이름을 구분하고, 권한을 분리하고, 관리 영역을 나누는 역할까지 할 수 있습니다.


스키마 안에는 무엇이 들어갈까?

스키마에는 테이블만 들어가는 것이 아닙니다.

DBMS에 따라 다양한 데이터베이스 객체가 포함될 수 있습니다.

대표적으로 다음과 같습니다.

데이터베이스 구조Schema| — Table — View — Function — Sequence — Index — 기타 데이터베이스 객체
데이터베이스 구조

PostgreSQL을 예로 들면 game이라는 스키마 안에

game.player
game.guild
game.item
game.inventory

같은 테이블뿐 아니라

game.player_summary

같은 View나

game.search_player()

같은 함수도 둘 수 있습니다.


테이블 이름 앞에 붙는 game은 무엇인가?

다음 SQL을 살펴보겠습니다.

SELECT *
FROM game.player;

구조를 나누면

데이터베이스 구조game.player———— ——————Schema Table
데이터베이스 구조

입니다.

조금 더 정확하게 표현하면

데이터베이스 구조Database | —— Schema | —— Table
데이터베이스 구조

구조라고 볼 수 있습니다.

예를 들어

데이터베이스 구조game_db | —— game | —— player | —— guild | —— inventory | —— shop —— item —— payment
데이터베이스 구조

처럼 구성할 수 있습니다.


왜 스키마를 사용하는가?

스키마를 사용하는 이유는 크게 몇 가지가 있습니다.

가장 먼저 관련 객체를 구분해서 관리할 수 있습니다.

예를 들어 게임 서비스의 테이블이 다음처럼 있다고 해봅시다.

player
guild
item
inventory
payment
purchase
administrator
permission
login_log
battle_log

테이블 이름만 보면 기능별 구분이 명확하지 않습니다.

하지만 스키마를 사용하면

game.player
game.guild
game.inventory

shop.item
shop.payment
shop.purchase

admin.administrator
admin.permission

log.login_log
log.battle_log

처럼 기능별로 구조를 나눌 수 있습니다.

데이터베이스가 커질수록 이러한 구분은 중요해집니다.


같은 이름의 테이블도 사용할 수 있다

스키마의 중요한 특징 중 하나는 이름 공간을 분리할 수 있다는 것입니다.

예를 들어 게임과 쇼핑 시스템에서 모두 item이라는 이름을 사용하고 싶다고 해봅시다.

스키마가 없다면

item
item

같은 이름의 테이블을 같은 공간에 만들 수 없습니다.

하지만 스키마를 나누면 가능합니다.

game.item

shop.item

둘은 이름이 item으로 동일하지만 서로 다른 스키마 안에 있기 때문에 별개의 테이블입니다.

예를 들어

game.item

item_id

item_name

attack

101

철검

20

102

마법 지팡이

35

게임에서 사용하는 장비입니다.

반면

shop.item

item_id

item_name

price

1

다이아 100개

1000

2

시즌 패스

9900

상점에서 판매하는 상품입니다.

둘 다 item이지만 의미가 다릅니다.

SELECT *
FROM game.item;

SELECT *
FROM shop.item;

은 완전히 다른 테이블을 조회합니다.


권한을 나누는 데에도 사용할 수 있다

스키마는 단순한 정리 기능뿐 아니라 접근 권한을 구분하는 데에도 유용합니다.

게임 회사에 다음 개발자들이 있다고 생각해봅시다.

개발자

담당 업무

게임 서버 개발자

Player, Guild, Inventory

결제 개발자

Payment, Purchase

운영 담당자

Admin

분석 담당자

Log

모든 개발자가 모든 데이터에 접근할 필요는 없습니다.

예를 들어 게임 서버 개발자는

game

스키마를 사용할 수 있지만,

결제 정보가 들어 있는

payment

스키마에는 접근하지 못하도록 제한할 수 있습니다.

개념적으로 보면 다음과 같습니다.

데이터베이스 구조게임 서버 개발자 | —— game Schema 접근 가능결제 개발자 | —— payment Schema 접근 가능
데이터베이스 구조

즉 스키마는 시스템 구조를 구분하면서 권한 관리의 단위로도 활용될 수 있습니다.


PostgreSQL의 public 스키마

PostgreSQL을 사용하다 보면 특별히 스키마를 만들지 않았는데도 다음과 같은 테이블을 만들 수 있습니다.

CREATE TABLE player (
    player_id INTEGER PRIMARY KEY,
    nickname VARCHAR(50)
);

이 경우 일반적인 기본 설정에서는 public이라는 스키마를 사용하게 됩니다.

즉 실제로는 개념적으로

public.player

에 만들어진 것입니다.

그래서 다음과 같이 명시적으로 작성할 수도 있습니다.

SELECT *
FROM public.player;

스키마를 따로 만들지 않고 PostgreSQL 실습을 했다면 대부분 public 스키마를 사용했을 가능성이 높습니다.


직접 스키마 만들기

게임용 스키마를 만들어보겠습니다.

CREATE SCHEMA game;

이제 game이라는 스키마가 생성됩니다.

그 안에 Player 테이블을 만들 수 있습니다.

CREATE TABLE game.player (
    player_id INTEGER PRIMARY KEY,
    nickname VARCHAR(30) NOT NULL,
    level INTEGER NOT NULL
);

Guild도 같은 스키마에 생성할 수 있습니다.

CREATE TABLE game.guild (
    guild_id INTEGER PRIMARY KEY,
    guild_name VARCHAR(50) NOT NULL
);

구조는 다음과 같습니다.

데이터베이스 구조Database| —— game | —— player —— guild
데이터베이스 구조

데이터를 넣어보자

Player에 데이터를 저장하겠습니다.

INSERT INTO game.player
    (player_id, nickname, level)
VALUES
    (1, '천기사', 35),
    (2, '흑마법사', 42),
    (3, '궁수왕', 28);

데이터는 다음과 같습니다.

game.player

player_id

nickname

level

1

천기사

35

2

흑마법사

42

3

궁수왕

28

조회할 때도 스키마 이름을 사용할 수 있습니다.

SELECT *
FROM game.player;

즉 기본적인 형태는

schema.table

입니다.


스키마가 다르면 같은 이름도 가능하다

이번에는 test라는 스키마도 만들어보겠습니다.

CREATE SCHEMA test;

그리고 동일한 이름의 Player 테이블을 만듭니다.

CREATE TABLE test.player (
    player_id INTEGER PRIMARY KEY,
    nickname VARCHAR(30)
);

그러면 데이터베이스에는

game.player

test.player

두 테이블이 동시에 존재할 수 있습니다.

game.player

player_id

nickname

level

1

천기사

35

2

흑마법사

42

test.player

player_id

nickname

1

테스트유저

2

개발자캐릭터

조회할 때

SELECT *
FROM game.player;

라고 하면 실제 게임 데이터를 조회하고,

SELECT *
FROM test.player;

라고 하면 테스트 데이터를 조회합니다.


스키마와 테이블은 다른 개념이다

처음에는 Schema와 Table을 혼동하기 쉽습니다.

차이를 정리하면 다음과 같습니다.

구분

Schema

Table

역할

객체를 묶는 영역

실제 데이터를 저장

내부에 데이터 행 저장

직접 저장하지 않음

저장함

game

player

표현

game

game.player

포함 대상

Table, View, Function 등

Row와 Column

쉽게 표현하면

데이터베이스 구조Schema — Table — Column — Row
데이터베이스 구조

구조입니다.


Database와 Schema는 무엇이 다를까?

이 부분도 많이 헷갈립니다.

PostgreSQL을 기준으로 단순화하면 다음과 같이 볼 수 있습니다.

데이터베이스 구조PostgreSQL Server| —— Database A| || —— Schema game| | —— player| | —— guild| || —— Schema shop| —— payment| —— Database B | —— Schema public —— test_table
데이터베이스 구조

즉 일반적으로

데이터베이스 구조ServerDatabaseSchemaTableRow / Column
데이터베이스 구조

순으로 생각할 수 있습니다.


게임으로 비교해보기

하나의 온라인 게임 회사에서 여러 게임을 운영한다고 생각해봅시다.

데이터베이스 구조PostgreSQL Server — RPG_DB| — game| | — player| | — monster| | — guild| || — shop| — product| — payment| — Racing_DB — game | — player | — car | — race | — shop — product
데이터베이스 구조

여기에서

RPG_DB
Racing_DB

는 Database이고,

game
shop

은 Schema이며,

player
monster
car
payment

는 Table입니다.


스키마와 ERD의 관계

스키마는 ERD와도 연결해서 생각할 수 있습니다.

예를 들어 게임의 ERD가 다음과 같다고 해봅시다.

데이터베이스 구조Guild | | 1:N |Player | | 1:N |Inventory | | N:1 |Item
데이터베이스 구조

이 테이블들을 모두 game 스키마 안에 둘 수 있습니다.

데이터베이스 구조game| —— guild —— player —— inventory —— item
데이터베이스 구조

ERD에서는 테이블 사이의 관계를 표현하고,

스키마는 이 테이블들을 어느 영역에서 관리할지 구분하는 역할을 합니다.


여러 스키마 사이에서도 관계를 만들 수 있다

테이블이 같은 스키마에 있어야만 관계를 만들 수 있는 것은 아닙니다.

예를 들어

game.player

shop.purchase

가 있다고 해봅시다.

구매 기록이 플레이어를 참조하도록 만들 수 있습니다.

데이터베이스 구조game.player——————————————player_id PKnicknameshop.purchase——————————————purchase_id PKplayer_id FKproduct_id
데이터베이스 구조

개념적으로

데이터베이스 구조shop.purchase.player_id | ————→ game.player.player_id
데이터베이스 구조

처럼 다른 스키마의 테이블을 참조하는 구조도 만들 수 있습니다.

즉 스키마가 다르다고 해서 데이터 관계까지 완전히 단절되는 것은 아닙니다.


Schema가 왜 Name Space라고 불릴까?

스키마를 설명할 때 Namespace라는 표현을 사용하기도 합니다.

이유는 같은 이름을 서로 다른 영역에서 구분할 수 있기 때문입니다.

예를 들어

game.player

test.player

admin.player

세 테이블이 모두 존재할 수 있습니다.

player라는 이름은 같지만 앞의 스키마 이름이 다르기 때문에 구별됩니다.

컴퓨터의 파일 경로와 비교하면

/game/player

/test/player

/admin/player

와 비슷한 느낌입니다.

따라서

Schema = 데이터베이스 객체의 이름 충돌을 막아주는 Namespace

라고도 설명할 수 있습니다.


스키마의 또 다른 의미 — 데이터 구조 자체

여기서 주의해야 할 점이 하나 있습니다.

Schema라는 단어는 상황에 따라 의미가 조금 달라집니다.

데이터베이스 이론에서는 다음과 같은 표현을 볼 수 있습니다.

데이터베이스 스키마는 데이터베이스의 구조와 제약조건을 정의한다.

이때 Schema는 단순히 PostgreSQL의 public, game 같은 영역만 의미하지 않습니다.

예를 들어 다음 구조 자체를 스키마라고 표현할 수 있습니다.

데이터베이스 구조Player————————————————player_id PKnicknamelevelguild_id FKGuild————————————————guild_id PKguild_name
데이터베이스 구조

어떤 테이블이 존재하는지

어떤 컬럼이 존재하는지

각 컬럼의 데이터 타입은 무엇인지

PK와 FK는 무엇인지

테이블끼리 어떻게 연결되는지

같은 데이터베이스의 전체적인 설계 구조를 Schema라고 부르기도 합니다.


Schema와 Instance

스키마를 배울 때 같이 등장하는 개념이 Instance입니다.

둘의 차이는 매우 중요합니다.

다음 Player 테이블을 보겠습니다.

데이터베이스 구조Player————————————————player_id INTEGERnickname VARCHAR(30)level INTEGER
데이터베이스 구조

이 구조 자체는 Schema입니다.

반면 실제로 들어 있는 데이터

player_id

nickname

level

1

천기사

35

2

흑마법사

42

3

궁수왕

28

Instance라고 볼 수 있습니다.

쉽게 말하면

Schema
= 데이터의 틀

Instance
= 그 틀 안에 현재 들어 있는 실제 데이터

입니다.


게임 캐릭터 생성창으로 비유하면

게임의 캐릭터 구조가 다음과 같다고 해봅시다.

캐릭터

닉네임
레벨
직업
HP
MP

이것은 어떤 정보를 가져야 하는지 정한 구조입니다.

즉 Schema에 가깝습니다.

실제로 캐릭터가 생성되면

닉네임

레벨

직업

HP

MP

천기사

35

기사

1500

200

흑마법사

42

마법사

800

1800

처럼 값이 들어갑니다.

이것이 Instance입니다.

데이터베이스 구조SchemaPlayer- nickname- level- job- hp- mpInstance천기사 / 35 / 기사 / 1500 / 200흑마법사 / 42 / 마법사 / 800 / 1800
데이터베이스 구조

Schema는 비교적 자주 바뀌지 않지만 Instance는 플레이어가 생성되고 삭제되면서 계속 변합니다.


논리적 스키마와 물리적 스키마

앞에서 살펴본 개념적·논리적·물리적 데이터 모델링과도 스키마 개념을 연결할 수 있습니다.

논리적인 수준에서는

데이터베이스 구조Player————————————————player_idnicknamelevelguild_id
데이터베이스 구조

처럼 구조를 정의할 수 있습니다.

물리적인 수준에서는

데이터베이스 구조player————————————————————————player_id BIGINT PKnickname VARCHAR(30) NOT NULLlevel SMALLINT DEFAULT 1guild_id BIGINT FK
데이터베이스 구조

처럼 DBMS에 맞게 구체화합니다.

즉 스키마라는 개념 역시 어느 수준의 데이터 구조를 이야기하느냐에 따라 논리적·물리적으로 구분해서 볼 수 있습니다.


PostgreSQL에서 스키마 확인하기

DBeaver에서 PostgreSQL을 연결하면 보통 다음과 같은 구조를 볼 수 있습니다.

데이터베이스 구조Database| —— Schemas | —— public | —— Tables | —— game —— Tables
데이터베이스 구조

예를 들어 game 스키마 안에 다음 테이블을 만들었다면

데이터베이스 구조game — player — guild — item — inventory
데이터베이스 구조

DBeaver에서도 Schema 아래에 Table들이 표시됩니다.

따라서 DBeaver에서 보이는 Schemas 폴더는 단순한 UI상의 폴더라기보다 실제 PostgreSQL의 Schema 구조를 보여주는 것입니다.


search_path란?

PostgreSQL에서 다음과 같이 입력할 수도 있습니다.

SELECT *
FROM player;

원래 정확하게 쓰면

SELECT *
FROM game.player;

처럼 스키마 이름이 필요합니다.

그런데 PostgreSQL에는 search_path라는 개념이 있습니다.

쉽게 말하면

스키마 이름을 생략했을 때 어떤 스키마부터 찾아볼 것인가?

를 정하는 설정입니다.

예를 들어 검색 순서가

game
public

이라면

SELECT *
FROM player;

라고 입력했을 때 PostgreSQL이 먼저

game.player

를 찾습니다.

없다면 다음 스키마를 확인합니다.

public.player

따라서 같은 이름의 테이블이 여러 스키마에 존재한다면 어떤 스키마를 사용하는지 주의해야 합니다.


스키마 이름을 명시하는 이유

그래서 실제 SQL에서는 다음처럼 스키마 이름을 명확하게 작성하는 경우가 많습니다.

SELECT *
FROM game.player;

다음 SQL보다

SELECT *
FROM player;

어떤 테이블을 사용하는지 명확합니다.

특히

game.player
test.player
admin.player

처럼 같은 이름이 여러 곳에 있다면 스키마 이름을 명시하는 것이 중요합니다.


실제 게임 데이터베이스 스키마 구조 예시

조금 더 큰 게임 서비스를 만들어보겠습니다.

데이터베이스 구조GameDB| —— account| —— user_account| —— login_history| —— ban_history| —— game| —— player| —— guild| —— item| —— inventory| —— monster| —— quest| —— shop| —— product| —— purchase| —— payment| —— log —— battle_log —— item_log —— access_log
데이터베이스 구조

기능별로 스키마를 분리하면

account
→ 계정 관련

game
→ 실제 게임 플레이 데이터

shop
→ 결제 및 상품

log
→ 기록 데이터

처럼 역할이 분명해집니다.

게임 데이터베이스의 스키마 구성PostgreSQL Server — GameDB — account schema | — player_account | — login_history — game schema | — player | — guild | — inventory — shop schema — item — purchase
게임 데이터베이스의 스키마 구성

테이블을 전부 public에 두면 안 되는가?

물론 가능합니다.

작은 실습 프로젝트라면

public.player
public.guild
public.item
public.inventory

처럼 모두 public 스키마에 두어도 큰 문제가 없습니다.

하지만 프로젝트가 커지면

public.player
public.guild
public.item
public.inventory
public.payment
public.purchase
public.login_log
public.battle_log
public.admin
public.permission
...

처럼 객체가 계속 증가합니다.

이 경우 기능별 구분이 어려워질 수 있습니다.

그래서 프로젝트 규모와 요구사항에 따라

game.*
shop.*
admin.*
log.*

처럼 스키마를 분리하는 방법을 사용할 수 있습니다.

중요한 것은 스키마를 많이 만드는 것이 무조건 좋은 설계라는 의미는 아니라는 것입니다.

시스템 규모, 권한 분리, 팀 구조, 데이터 관리 방식 등을 고려해서 결정해야 합니다.


Schema와 ERD, 정규화 연결하기

지금까지 배운 개념을 연결하면 다음과 같습니다.

데이터베이스 구조요구사항 분석개념적 모델링Player / Guild / Item 찾기논리적 모델링PK / FK / 관계 설계정규화Player / Guild / Item / Inventory 분리ERD테이블과 관계를 그림으로 표현물리적 모델링VARCHAR / INTEGER / INDEX 결정Schema 구성game / shop / log 등으로 관리 영역 구분CREATE TABLE실제 데이터베이스 구축
데이터베이스 구조

스키마는 앞에서 공부한 내용과 완전히 별개의 개념이 아니라 설계된 데이터베이스 객체를 실제 DBMS 안에서 어떻게 구성하고 관리할 것인가와 연결됩니다.


Database, Schema, Table, Row 한 번에 정리하기

게임 데이터베이스를 예로 전체 계층을 보면 다음과 같습니다.

데이터베이스 구조DatabaseGameDBSchemagameTableplayerColumnplayer_idnicknamelevelRow1 / 천기사 / 352 / 흑마법사 / 42
데이터베이스 구조

표로 정리하면 다음과 같습니다.

단계

게임 예시

역할

Database

GameDB

전체 데이터베이스

Schema

game

관련 객체를 묶는 영역

Table

player

같은 종류의 데이터 저장

Column

nickname

데이터의 속성

Row

천기사, 35

실제 하나의 데이터


Schema와 Instance 비교

마지막으로 이론에서 사용하는 Schema의 의미까지 정리해보겠습니다.

구분

Schema

Instance

의미

데이터 구조

현재 저장된 실제 데이터

player_id, nickname, level

1, 천기사, 35

변경 빈도

상대적으로 적음

매우 자주 변경

CREATE TABLE

Schema 정의

해당 없음

INSERT/UPDATE/DELETE

구조는 그대로

Instance 변경

예를 들어

CREATE TABLE game.player (
    player_id INTEGER PRIMARY KEY,
    nickname VARCHAR(30),
    level INTEGER
);

는 데이터의 구조를 정의합니다.

반면

INSERT INTO game.player
VALUES (1, '천기사', 35);

는 실제 데이터를 생성합니다.

CREATE TABLE
→ Schema를 정의

INSERT
→ Instance를 추가

라고 연결해서 이해할 수 있습니다.


정리

데이터베이스에서 Schema는 문맥에 따라 두 가지 관점으로 이해할 수 있습니다.

첫 번째는 데이터베이스 이론에서의 의미입니다.

테이블, 컬럼, 관계, 제약조건 등 데이터가 어떤 구조를 가지고 있는지를 정의한 설계

두 번째는 PostgreSQL 같은 DBMS에서의 의미입니다.

하나의 데이터베이스 안에서 Table, View, Function 등의 객체를 묶어 관리하는 Namespace

PostgreSQL을 기준으로 구조를 가장 간단하게 표현하면 다음과 같습니다.

데이터베이스 구조ServerDatabaseSchemaTableColumn / Row
데이터베이스 구조

게임 데이터베이스를 예로 들면

데이터베이스 구조GameDB — game| — player| — guild| — inventory| — shop| — product| — payment| — log — battle_log
데이터베이스 구조

처럼 구성할 수 있습니다.

따라서 스키마를 가장 쉽게 기억하면 다음과 같습니다.

Database가 하나의 큰 데이터 저장 공간이라면, Schema는 그 안의 테이블과 객체들을 역할별로 나누어 관리하는 영역이다.

그리고 데이터베이스 이론에서는 한 단계 더 넓게

Schema = 데이터가 어떤 형태와 관계를 가지고 저장될 것인지 정의한 구조

라는 의미로도 사용된다는 점을 함께 기억하면 됩니다.

댓글 0

댓글을 불러오는 중…