PostgreSQL 데이터베이스와 스키마¶
한 줄 요약: PostgreSQL은 서버 > 데이터베이스 > 스키마 > 표 > 줄 순서로 나뉜다. 데이터베이스는 서비스끼리 완전히 떼어 놓는 단위이고, 스키마는 그 안의 정리용 칸막이다. 새 서비스의 표를 "어디에" 만들지 정하려면 이 둘을 구분해야 한다.
📍 만난 상황¶
- 2026-11-09 — 대시보드 표 13개의 ERD를 그린 직후였다. 이걸 PostgreSQL에 실제로 만들 차례가 됐다. 3번 물음 → 그날 일지
- 15:11 "ERD를 그려 줘. 그리고 이걸 postgres의 워크스페이스 하나 만들어서 거기에 만드는 건가?"
- 15:43 표를 나눈 기준을 들은 뒤 다시 "postgres에 만든다고 하면 뭔가 워크스페이스 같은 걸 만드는 건가?" → 워크스페이스에 해당하는 것은 데이터베이스이고, 그 안의 칸막이가 스키마다.
- 15:44 "그럼 워크스페이스를 만드는 게 맞을까, 스키마를 만드는 게 맞을까?" → 둘 중 하나가 아니라 둘 다 만든다.
🧩 쉽게 말하면¶
회사 건물을 떠올리면 된다.
- 서버 = 건물 한 채
- 데이터베이스 = 사무실 하나. 열쇠가 따로 있다.
- 스키마 = 사무실 안의 캐비닛
- 표 = 캐비닛 속 장부, 줄 = 장부의 한 줄
다른 팀 사무실에는 열쇠가 없어서 들어갈 수 없다. 같은 사무실 안의 캐비닛은 열어 볼 수 있다. 칸막이가 없으면 모든 팀의 장부가 한 바닥에 섞여서, 남의 장부를 실수로 고치기 쉽다.
📖 제대로 알기¶
계층별 정의¶
- 서버(클러스터): PostgreSQL 프로그램 하나가 관리하는 데이터베이스 묶음이다. 쿠버네티스의 "클러스터"와는 다른 뜻이다.
- 롤(role: 로그인 계정이자 권한 묶음): 서버 전체에 공통이다. 같은 계정으로 여러 데이터베이스에 접속할 수 있다. 접속을 허락받은 곳에만 들어간다.
- 데이터베이스: 서로 완전히 분리된 공간이다. 한 연결은 한 데이터베이스에만 붙는다. 그래서 기본적으로 다른 데이터베이스의 표와 JOIN하지 못한다.
- 스키마: 한 데이터베이스 안의 이름 칸막이다.
app.dataset처럼스키마.표로 부른다. 같은 DB 안이면 스키마끼리 JOIN할 수 있다. - public 스키마: 데이터베이스를 만들면 기본으로 생기는 칸막이다. 스키마 이름을 생략하면 보통 여기로 간다.
- search_path: 스키마 이름을 생략했을 때 어느 스키마부터 찾을지 정한 순서다.
"워크스페이스"라는 말¶
PostgreSQL 공식 용어가 아니다. 다른 도구에서 쓰는 말이라, 사람들이 말하면 대개 데이터베이스를 가리킨다. PostgreSQL이 쓰는 이름은 서버, 데이터베이스, 스키마, 표뿐이다.
⚙️ 어떻게 동작하나¶
DB 관리 도구(pgAdmin, DBeaver)를 열면 왼쪽에 이런 나무 구조가 보인다.
flowchart TD
S["PostgreSQL 서버"] --> D1[("postgres<br/>기본 DB")]
S --> D2[("disaster_dashboard<br/>새 DB")]
D2 --> P["public 스키마<br/>(안 씀)"]
D2 --> A["app 스키마"]
A --> T1["app_user"]
A --> T2["dataset"]
A --> T3["favorite 외 10개"]
새로 만드는 것은 데이터베이스 하나(disaster_dashboard)와 그 안의 스키마 하나(app)다. 표 13개는 모두 app 아래에 들어간다.
만드는 순서는 이렇다. 1. 관리자 계정으로 서버에 접속한다. 2. 데이터베이스를 만든다. 처음 한 번만 한다. 3. 앱 전용 계정(롤)을 만든다. 이 계정이 새더라도 이 DB만 영향받게 하려는 것이다. 4. 새 데이터베이스로 접속을 옮긴다. 스키마와 표는 "지금 접속한 DB" 안에 생기기 때문이다. 5. 스키마와 표를 만든다. 표 구조가 바뀔 때마다 다시 하는 단계라서 마이그레이션 도구(표 구조 변경 이력을 관리하는 도구)가 맡는다. 6. 전용 계정에 그 스키마 권한만 준다.
왜 두 겹으로 나눌까. 데이터베이스는 보안과 운영의 경계(누가 들어오나, 무엇을 같이 백업하나)다. 스키마는 정리의 경계(이름이 겹치지 않게, 권한을 묶어서)다. 목적이 달라서 둘 다 둔다.
🧪 예시¶
-
예시 1 · 일상에 빗대어: 건물(서버)에 회계팀 사무실과 대시보드팀 사무실(데이터베이스)이 있다. 대시보드팀 직원 출입증(롤)으로는 회계팀 문이 열리지 않는다. 대시보드팀 사무실 안에는 기본 캐비닛(public)과 새 캐비닛(app)이 있다. 장부 13권은 새 캐비닛에 넣는다. 프로젝트가 끝나면 사무실을 통째로 비우면 된다(
DROP DATABASE). 회계팀은 아무 영향이 없다. -
예시 2 · 실제로는:
-- 관리자 계정으로, 처음 한 번
CREATE DATABASE disaster_dashboard; -- ①
CREATE ROLE dashboard_app LOGIN PASSWORD '...'; -- ②
GRANT CONNECT ON DATABASE disaster_dashboard TO dashboard_app;
-- disaster_dashboard로 접속을 옮긴 뒤 (psql이면 \c disaster_dashboard)
CREATE SCHEMA app; -- ③
CREATE TABLE app.dataset (id text PRIMARY KEY, title text);
GRANT USAGE ON SCHEMA app TO dashboard_app; -- ④
GRANT SELECT, INSERT, UPDATE, DELETE
ON ALL TABLES IN SCHEMA app TO dashboard_app;
USAGE는 칸막이 안을 들여다볼 권한이다. 표마다 읽고 쓰는 권한은 그다음 줄에서 따로 준다.
⚖️ 비교 · 흔한 오해¶
| 항목 | 데이터베이스 | 스키마 |
|---|---|---|
| 나누는 정도 | 완전 분리 | 같은 DB 안 칸막이 |
| 서로 JOIN | 기본적으로 불가 | 가능 |
| 주된 목적 | 서비스끼리 분리 | 정리·권한 묶기 |
| 백업·삭제 | 기본 단위 | DB와 함께 묶임 |
그날의 판단 기준은 "이미 있는 DB에 끼어 들어가는가"였다.
| 상황 | 만드는 것 |
|---|---|
| 전용 서버이거나 DB를 새로 만들 권한이 있음 | 데이터베이스 + app 스키마 |
| "기존 DB 안에 자리만 줄게" | 스키마만 |
어느 쪽이든 표를 만드는 SQL은 같다. 첫 줄이 CREATE SCHEMA app이라서 어디에 접속해서 실행하느냐만 달라진다.
오해 1. "워크스페이스 아니면 스키마, 둘 중 하나를 고른다." "워크스페이스"라는 한 단어로 생각하면 만들 것이 하나뿐인 것처럼 느껴진다. 실제로는 두 겹이다. 둘 다 만드는 것이 기본이다.
오해 2. "스키마 = 표 설계도" 일상에서 "스키마"는 표 구조(설계)를 뜻하기도 한다. "스키마를 설계한다"는 말이 그렇다. PostgreSQL의 스키마 객체는 표를 담는 칸막이다. 같은 단어가 두 뜻으로 쓰여 헷갈리기 쉽다.
오해 3. "다른 DB 제품도 같다."
MySQL에서는 SCHEMA와 DATABASE가 같은 뜻이다. PostgreSQL에서는 다르다. 다른 제품 경험이 섞이면 이 계층이 흐려진다.
🛠️ 실무에서는¶
- 별도 서비스는 전용 데이터베이스로 시작한다. 백업·복원·이전·삭제를 따로 할 수 있다.
- 앱 계정은 관리자 계정과 분리하고, 필요한 스키마 권한만 준다.
public은 쓰지 않고 이름 있는 스키마(app)를 쓴다. 다른 도구가 만든 표와 섞이지 않는다.GRANT ... ON ALL TABLES는 지금 있는 표에만 적용된다. 나중에 만들 표까지 주려면ALTER DEFAULT PRIVILEGES를 함께 쓴다.- 운영 DB를 정할 때 플랫폼팀에 이렇게 묻는다. "대시보드 전용 데이터베이스를 하나 만들 수 있나요, 기존 DB 안에 스키마로 들어가야 하나요?"
문제가 생기면 먼저 볼 것
- "relation does not exist"(표가 없음): 스키마 이름을 붙였나(app.dataset). search_path에 app이 있나. 맞는 DB에 접속했나.
- "permission denied"(권한 없음): 스키마 USAGE와 표 권한을 둘 다 줬나. 새로 만든 표라 기존 GRANT에서 빠진 것은 아닌가.
❓ 자주 묻는 질문¶
- Q. 매번
app.을 붙여야 하나? A. 앱 계정의 search_path를app으로 정하면 생략할 수 있다. 다만 SQL 파일과 코드에는 붙여 두는 편이 헷갈리지 않는다. - Q. 로컬 Docker의 PostgreSQL에서는 어떻게 만드나?
A. 공식 이미지는
POSTGRES_DB환경 변수로 첫 실행 때 데이터베이스를 만들어 준다. 스키마와 표는 그다음 마이그레이션으로 만든다. - Q. 데이터베이스 두 개의 표를 함께 조회해야 하면? A. 기본으로는 안 된다. 같은 DB 안의 다른 스키마로 두거나, 앱 서버가 따로 물어 합친다.
🔭 더 깊이 보기¶
- 롤과 권한(GRANT, 기본 권한)은 어떻게 설계하나? — 앱 계정의 권한을 최소로 주는 실무의 핵심이라서.
- 마이그레이션 도구는 표 변경 이력을 어떻게 관리하나? — 5단계(표 만들기)를 매번 손으로 하지 않으려면 필요해서.
pg_dump로 DB나 스키마만 골라 백업하려면? — 데이터베이스를 따로 두는 가장 큰 이점이 백업·복원이라서.- 관련 주제: ERD 관계와 카디널리티(1:N) · 애플리케이션 서버와 DB의 역할 분담
📚 참고 자료¶
- PostgreSQL 한국어 문서 — "스키마" 장 (PostgreSQL 한국 사용자 모임 번역, postgresql.kr) — 공식 문서의 스키마 설명을 한국어로 · 링크 없이 "postgresql.kr 스키마"로 검색
- Schemas (PostgreSQL 공식 문서, 영문) — public 스키마, search_path, 스키마 권한 · 링크
- Managing Databases — Overview (PostgreSQL 공식 문서, 영문) — 서버·데이터베이스·스키마 계층과 "한 연결은 한 DB" 원칙 · 링크
🗓️ 이력¶
- 2026-11-09: "워크스페이스"를 만드는 건지, 워크스페이스와 스키마 중 무엇을 만드는지를 3번 물음 → 일지