백엔드 데이터 인프라 5편 — 데이터베이스 만들기 CREATE DATABASE

2026-05-17백엔드 데이터 인프라

백엔드 데이터 인프라 5편. 새 프로젝트의 시작점 CREATE DATABASE와 createdb 명령으로 PostgreSQL 데이터베이스를 만드는 표준 패턴 풀어쓴 학습 노트.

📚 백엔드 데이터 인프라 · 5편 — 데이터베이스 만들기 CREATE DATABASE

이 글은 백엔드 데이터 인프라 시리즈 70편 중 5편이에요. 4편 psql 까지 손에 익혔으니, 이번 5편은 새 프로젝트의 가장 첫 단계 — 데이터베이스 한 개 만들기.

데이터베이스 = 격리된 작업 공간

3편 아키텍처 의 4단계 계층을 떠올려보면 Cluster → Database → Schema → Table 구조예요. 새 프로젝트마다 "Database" 한 개를 만들고 그 안에서 모든 스키마·테이블 작업을 진행해요.

같은 클러스터 안에 여러 DB가 있어도 서로 격리돼 있어서, 다른 DB의 테이블에 직접 접근할 수 없고 사용자 권한도 DB 단위로 부여돼요.

두 가지 방법 — CREATE DATABASE vs createdb

방법 1 — SQL CREATE DATABASE

psql에 접속한 상태에서:

CREATE DATABASE myappdb;

한 줄이면 끝, 옵션 없이도 충분해요.

방법 2 — createdb OS 명령

2편 설치 에 함께 설치된 OS 명령이에요.

createdb myappdb

내부적으로는 CREATE DATABASE 를 호출하는데, 셸 스크립트나 CI(지속적 통합 자동화 파이프라인)에서 쓰기 편해요.

CREATE DATABASE 옵션

옵션 없는 단순 형태 외에 실무에서 자주 박는 6가지가 있어요.

CREATE DATABASE myappdb
    WITH OWNER       = appuser
         ENCODING    = 'UTF8'
         LC_COLLATE  = 'ko_KR.UTF-8'
         LC_CTYPE    = 'ko_KR.UTF-8'
         TEMPLATE    = template0
         CONNECTION LIMIT = 100;
옵션 의미
OWNER 데이터베이스 소유자 (사용자)
ENCODING 문자 인코딩 — UTF8 표준
LC_COLLATE 문자열 정렬 순서
LC_CTYPE 대소문자·문자 분류
TEMPLATE 복제 원본 (보통 template0)
CONNECTION LIMIT 동시 접속 수 제한

UTF8 인코딩

한글·이모지·다국어를 다룬다면 UTF8이 무조건이에요. 옛 "EUC-KR" 같은 인코딩은 위험하니 운영 DB를 처음 만들 때 명시해 두세요.

TEMPLATE — template0 vs template1

PG에는 두 가지 "DB 템플릿" 이 있어요.

  • template1 = 기본 템플릿. CREATE DATABASE 시 자동 복제 원본
  • template0 = 빈 깨끗한 템플릿. 다른 인코딩·로케일 지정 시 사용

template1 에 다른 사람이 객체를 박아 뒀다면 우리 새 DB에도 같이 박히기 때문에, 깨끗하게 시작하려면 TEMPLATE template0 을 명시해 주세요.

CONNECTION LIMIT

특정 DB의 동시 접속 수를 제한해서, 운영 환경에서 "한 DB가 폭주해 다른 DB까지 영향" 가는 상황을 막아줘요.

소유자(OWNER) 모범 사례

운영 환경에선 슈퍼유저(postgres)가 DB 소유자가 아닌 게 표준이에요.

-- 1. 앱 전용 사용자 생성
CREATE USER appuser WITH PASSWORD 'strongpass' LOGIN;

-- 2. 그 사용자 소유로 DB 생성
CREATE DATABASE myappdb OWNER appuser;

-- 3. 앱은 appuser 로 접속 (postgres 슈퍼유저 X)

이유는 간단해요. 앱이 슈퍼유저로 접속하면 "앱 버그 = DB 전체 위험" 이 되니까요. 최소 권한 원칙을 따르는 거죠.

접속 변경

-- psql에서
\c myappdb

-- 결과
You are now connected to database "myappdb" as user "postgres".
myappdb=#

새 DB로 접속이 바뀌면 프롬프트도 postgres=# 에서 myappdb=# 로 변해요.

외부 접속 (명령행)

psql -U appuser -d myappdb -h localhost -W
# -W 옵션 = 비밀번호 프롬프트

데이터베이스 목록 확인

\l

# 또는 더 자세히
SELECT datname, pg_size_pretty(pg_database_size(datname)) AS size
FROM pg_database
ORDER BY pg_database_size(datname) DESC;

pg_database 시스템 뷰가 클러스터 안 모든 DB 정보를 제공해요.

데이터베이스 삭제 — DROP DATABASE

DROP DATABASE myappdb;

-- 위험 방지 IF EXISTS
DROP DATABASE IF EXISTS myappdb;

또는 OS 명령으로:

dropdb myappdb

함정 — 자기 자신에게 접속해 있으면 못 지워요.

ERROR: cannot drop the currently open database

해결은 다른 DB로 접속을 바꾼 뒤 시도하는 거예요.

\c postgres
DROP DATABASE myappdb;

또 다른 사용자가 접속해 있어도 못 지워요.

-- 다른 접속 모두 끊기
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE datname = 'myappdb' AND pid <> pg_backend_pid();

-- 그 후 DROP
DROP DATABASE myappdb;

운영에선 운영 시간 외백업 후에만 DROP하세요. 절대 실수로 누르면 안 돼요.

Spring Boot 연결

자바 백엔드 입문 13편 application.yml 에서 다룬 패턴 그대로예요.

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/myappdb
    username: appuser
    password: ${DB_PASSWORD}
    driver-class-name: org.postgresql.Driver

URL의 마지막 /myappdb"방금 만든 DB" 인데, 이 부분이 다르면 다른 DB로 붙어요.

멱등 스크립트 — 마이그레이션 패턴

CI에서 "이미 있으면 패스, 없으면 생성" 으로 돌리는 패턴이에요.

-- DB가 이미 있는지 확인
SELECT 1 FROM pg_database WHERE datname = 'myappdb';

-- 또는 PL/pgSQL 익명 블록
DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_database WHERE datname = 'myappdb') THEN
        PERFORM dblink_exec('host=localhost user=postgres',
                            'CREATE DATABASE myappdb');
    END IF;
END
$$;

또는 셸에서 OS 명령으로:

psql -U postgres -tAc "SELECT 1 FROM pg_database WHERE datname='myappdb'" | grep -q 1 \
    || createdb -U postgres myappdb

함정 5가지

(1) UTF8 안 박음

오래된 시스템에서 "SQL_ASCII" 인코딩으로 시작했다가 한글이 깨지는 일이 잦아요. 처음부터 UTF8로 박아두세요.

(2) 슈퍼유저가 앱 DB 소유

앱 코드의 SQL 인젝션·버그가 슈퍼유저 권한으로 동작하면 클러스터 전체가 위험해져요.

(3) template1 오염

template1 에 박은 객체는 모든 새 DB에 자동 복제돼서, "실수로 박힌 테스트 테이블" 이 운영 DB로 퍼지는 사고로 이어져요.

(4) CONNECTION LIMIT 없음

기본값이 무제한이라 폭주 시 클러스터 전체가 영향받아요. 운영에선 적정 값을 박아두세요.

(5) DROP 실수

DROP DATABASE취소 불가예요. 백업과 운영 시간 외 작업, 다중 확인을 꼭 거치세요.

🎯 운영 표준 한 줄

CREATE DATABASE myappdb OWNER appuser ENCODING 'UTF8' TEMPLATE template0 CONNECTION LIMIT 100;. 다섯 가지 옵션이 90% 운영 시나리오에 충분.

한 줄 정리 — CREATE DATABASE name OWNER user ENCODING 'UTF8' TEMPLATE template0 표준 패턴. 슈퍼유저 X — 앱 전용 사용자에 소유권. DROP은 백업 + 운영 시간 외. createdb·dropdb OS 명령은 스크립트에 편함.

시험 직전 한 번 더 — CREATE DATABASE 입문자가 매번 헷갈리는 것

  • CREATE DATABASE = SQL 명령
  • createdb = OS 명령 (내부적으로 같은 SQL)
  • 새 프로젝트 = 새 DB 한 개
  • 인코딩 = UTF8 무조건
  • OWNER = 앱 전용 사용자 (슈퍼유저 X)
  • TEMPLATE = template0 (깨끗한 시작)
  • CONNECTION LIMIT = 동시 접속 제한
  • LC_COLLATE = 정렬 순서 (한글 = ko_KR.UTF-8)
  • LC_CTYPE = 대소문자·문자 분류
  • \c db = psql 접속 변경
  • \l = DB 목록
  • pg_database = 시스템 뷰 (DB 정보)
  • DROP DATABASE = 취소 불가 — 백업 후 운영 시간 외
  • 자기 자신 DB 안에서 DROP 불가 — 다른 DB로 이동 후
  • 다른 사용자 접속 있으면 = pg_terminate_backend 로 끊고
  • IF EXISTS = 안전 가드
  • \c db user = 사용자 변경
  • Spring jdbc:postgresql://host:port/dbname 의 dbname
  • 멱등 스크립트 = pg_database 검사 + 조건부 생성
  • template1 오염 주의 — 모든 새 DB에 복제됨
  • pg_size_pretty = DB 크기 사람 친화적 표시
  • 한 클러스터 안 DB 수 = 보통 5~50개, 큰 시스템 = 100+

시리즈 다른 편 (앞뒤 글 모음)

이전 글:

다음 글:

※ 이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

답글 남기기

error: Content is protected !!