콘텐츠로 이동

PostgreSQL 데이터베이스와 스키마

11/9(월)

한 줄 요약: 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;
① 데이터베이스는 서버 단위로 만든다. 어느 DB에 접속해 있든 실행할 수 있다. ② 비밀번호는 여기 적지 않고 배포 설정의 비밀값으로 넣는다. 이 계정은 이 DB 하나에만 접속할 수 있다. ③ 스키마는 지금 접속한 DB 안에 생긴다. 접속을 옮기지 않고 실행하면 엉뚱한 DB에 칸막이가 생긴다. ④ 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번 물음 → 일지

🕸️ 연결된 주제