백엔드 데이터 인프라 32편 — 데이터 타입 개요

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

백엔드 데이터 인프라 32편. PG 데이터 타입의 큰 그림 — 숫자·문자·날짜·UUID·배열·범위 등 카테고리별 풀어쓴 학습 노트.

📚 백엔드 데이터 인프라 · 32편 — 데이터 타입 개요

이 글은 백엔드 데이터 인프라 시리즈 70편 중 32편이에요. 9편 CREATE TABLE 에서 "자주 쓰는 6가지 타입" 을 다뤘으니, 이번 32편은 그 위에 — PG(PostgreSQL의 줄임말) 전체 타입 카테고리.

PG 타입의 카테고리

PG는 40+ 내장 타입 + 사용자 정의 무한. 카테고리별:

카테고리
숫자 SMALLINT·INTEGER·BIGINT·NUMERIC·REAL·DOUBLE PRECISION
문자 CHAR·VARCHAR·TEXT
날짜·시간 DATE·TIME·TIMESTAMP·TIMESTAMPTZ·INTERVAL
불리언 BOOLEAN
UUID UUID
JSON JSON·JSONB
배열 INT[]·TEXT[] 등
범위 INT4RANGE·TSTZRANGE 등
네트워크 INET·CIDR·MACADDR
기하 POINT·LINE·POLYGON
이진 BYTEA
열거형 ENUM
복합 Composite Types
도메인 CREATE DOMAIN

실제로 자주 쓰는 건 숫자·문자·날짜·BOOLEAN·UUID·JSONB·배열 정도예요.

숫자 타입

타입 크기 범위
SMALLINT 2바이트 -32768 ~ 32767
INTEGER / INT 4바이트 -2.1B ~ 2.1B
BIGINT 8바이트 -9.2 × 10^18 ~
NUMERIC(p, s) 가변 정확한 소수
REAL 4바이트 부동소수
DOUBLE PRECISION 8바이트 부동소수
SERIAL / BIGSERIAL 4·8바이트 자동 증가 정수
IDENTITY INTEGER·BIGINT 기반 자동 증가 SQL 표준

정수 — INTEGER vs BIGINT

  • INTEGER = 21억 한계. 작은 카운터·플래그.
  • BIGINT = 거의 무한. 기본 키·통계 모두 — 한국 회사 표준.

소수 — NUMERIC vs FLOAT

NUMERIC(10, 2)   -- 10자리 중 소수 2자리 (정확)
DOUBLE PRECISION   -- 부정확 (부동소수)

금액·통화 = NUMERIC 무조건. 0.1 + 0.2 ≠ 0.3 함정.

SELECT 0.1::DOUBLE PRECISION + 0.2::DOUBLE PRECISION;
-- 0.30000000000000004 (부정확)

SELECT 0.1::NUMERIC + 0.2::NUMERIC;
-- 0.3 (정확)

문자 타입

타입 의미
CHAR(N) / CHARACTER(N) 고정 길이, 공백 패딩
VARCHAR(N) / CHARACTER VARYING 가변 (길이 제한)
TEXT 무제한 가변

9편 에서 다뤘듯이 — PG 표준 = TEXT. 길이 제한은 CHECK 제약으로 잡아요.

title TEXT CHECK (char_length(title) <= 200)

성능 차이가 없어서 CHAR·VARCHAR 는 거의 안 써요.

citext — 대소문자 무관

CREATE EXTENSION citext;
CREATE TABLE users (
    email CITEXT UNIQUE
);

INSERT INTO users (email) VALUES ('Alice@Example.com');
SELECT * FROM users WHERE email = 'alice@example.com';   -- 매칭!

이메일 대소문자 함정을 피할 수 있어요.

날짜·시간 타입

타입 의미 크기
DATE 날짜만 4바이트
TIME 시간만 (시간대 X) 8바이트
TIMETZ 시간 + 시간대 12바이트 (안 권장)
TIMESTAMP 날짜·시간 (시간대 X) 8바이트
TIMESTAMPTZ 시간대 포함 8바이트
INTERVAL 기간 16바이트

TIMESTAMP vs TIMESTAMPTZ

거의 항상 TIMESTAMPTZ. "시간대 포함" 이 안전.

-- TIMESTAMP (시간대 X)
'2026-05-17 14:00:00'::TIMESTAMP
-- 어느 시간대인지 모름

-- TIMESTAMPTZ (시간대 포함)
'2026-05-17 14:00:00 KST'::TIMESTAMPTZ
-- 한국 시각 명확

PG 내부는 TIMESTAMPTZ 도 UTC 로 저장해두고, 조회할 때 클라이언트 시간대로 변환해줘요.

INTERVAL — 기간

INTERVAL '7 days'
INTERVAL '1 month'
INTERVAL '2 years 3 months'
INTERVAL '1 hour 30 minutes'

-- 연산
NOW() + INTERVAL '7 days'         -- 7일 후
created_at - INTERVAL '30 days'   -- 30일 전
AGE(NOW(), birth)                  -- 나이 계산

기간을 다루기 좋은 강력한 도구예요.

DATE_TRUNC·EXTRACT

DATE_TRUNC('day', created_at)         -- 일 단위로 자르기
DATE_TRUNC('month', created_at)        -- 월 단위
EXTRACT(YEAR FROM birth)               -- 연도만
EXTRACT(EPOCH FROM (NOW() - start))   -- 초로 변환

대시보드·통계에서 매일 쓰게 돼요.

BOOLEAN

is_active BOOLEAN NOT NULL DEFAULT TRUE

값은 TRUE·FALSE·NULL. 단축으로 't'·'f'·'yes'·'no'·1·0 도 받아요.

UUID — 분산 표준

CREATE EXTENSION "uuid-ossp";

CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    ...
);

분산 시스템·마이크로서비스에서는 UUID 가 우세해요. BIGSERIAL 의 "순차 증가 충돌" 도 피할 수 있고요.

장단점: - ✓ 분산 시스템에서 고유 - ✓ 노출돼도 비밀번호 추측 X - ✗ 16바이트 (BIGINT 8 의 2배) - ✗ 정렬 비효율 (시계열 인덱스에 약함)

해결 = UUIDv7 (시간 정렬 UUID, PG 18+ 또는 확장).

JSON·JSONB

CREATE TABLE events (
    id BIGSERIAL PRIMARY KEY,
    payload JSONB
);

INSERT INTO events (payload) VALUES ('{"user": 1, "action": "click"}'::JSONB);

차이: - JSON = 텍스트 저장, 원본 포맷 보존 - JSONB = 이진 저장, 인덱스 가능, 키 정렬, 거의 항상 JSONB

연산자:

payload -> 'user'         -- JSON 추출
payload ->> 'user'        -- 텍스트 추출
payload #> '{a,b}'        -- 깊은 JSON
payload @> '{"x": 1}'     -- 포함
payload ? 'key'           -- 키 존재

33편에서 깊이.

배열 — INT[]·TEXT[] 등

CREATE TABLE posts (
    id BIGSERIAL PRIMARY KEY,
    tags TEXT[]
);

INSERT INTO posts (tags) VALUES (ARRAY['java', 'spring', 'postgres']);

-- 검색
SELECT * FROM posts WHERE 'java' = ANY(tags);
SELECT * FROM posts WHERE tags @> ARRAY['java'];
SELECT * FROM posts WHERE tags && ARRAY['java', 'python'];  -- 겹침

-- GIN(범용 역색인) 인덱스
CREATE INDEX idx_posts_tags ON posts USING GIN (tags);

태그나 작은 컬렉션에 아주 자주 써요.

범위 — RANGE

CREATE TABLE bookings (
    id BIGSERIAL PRIMARY KEY,
    period TSTZRANGE NOT NULL,
    EXCLUDE USING GIST (period WITH &&)   -- 겹침 방지
);

INSERT INTO bookings (period)
VALUES ('[2026-05-17 10:00 KST, 2026-05-17 12:00 KST)');

타입: - INT4RANGE — 정수 범위 - INT8RANGE — BIGINT - NUMRANGE — NUMERIC - TSRANGE — TIMESTAMP - TSTZRANGE — TIMESTAMPTZ (자주) - DATERANGE — DATE

23편 EXCLUDE 의 핵심 도구.

네트워크 — INET·CIDR

ip_address INET    -- IP 주소
network CIDR        -- 네트워크

-- 연산
SELECT * FROM logs WHERE ip << '192.168.1.0/24'::CIDR;  -- 서브넷 포함

CDN·방화벽·접근 제어 로그에서 자주 만나요.

ENUM

CREATE TYPE order_status AS ENUM ('PENDING', 'PAID', 'SHIPPED', 'DELIVERED', 'CANCELED');

CREATE TABLE orders (
    status order_status NOT NULL
);

장단점: - ✓ 타입 안전 - ✗ 값 변경·삭제 어려움 - ✗ JPA(자바 ORM 표준) 매핑 조금 복잡

대안은 TEXT + CHECK 가 더 유연해요.

함정 5가지

(1) VARCHAR(N) 박기

MySQL 습관. PG = TEXT 우세.

(2) FLOAT 로 금액

0.1 + 0.2 ≠ 0.3. NUMERIC 무조건.

(3) TIMESTAMP (without TZ)

시간대 모호. TIMESTAMPTZ 표준.

(4) JSON (not JSONB)

인덱스 X·키 순서 보존 (의미 X). JSONB 표준.

(5) ENUM 값 변경 어려움

한 번 박은 ENUM 값은 거의 못 빼요. "상태 집합이 영구히 변하지 않는다" 는 확신이 없다면 TEXT + CHECK 가 무난해요.

🎯 한국 회사 표준 타입

기본 키 = BIGINT IDENTITY 또는 UUID. 문자 = TEXT + CHECK. 시각 = TIMESTAMPTZ. 금액 = NUMERIC. JSON = JSONB. 상태 = TEXT + CHECK (ENUM 신중).

한 줄 정리 — PG 풍부한 타입. 한국 회사 표준 = BIGINT·TEXT·TIMESTAMPTZ·NUMERIC·JSONB·UUID. VARCHAR·FLOAT·TIMESTAMP·JSON·ENUM 은 함정. 배열·범위·네트워크·기하 등 PG 특별 타입은 강력한 도구.

시험 직전 한 번 더 — 데이터 타입 입문자가 매번 헷갈리는 것

  • 숫자 = SMALLINT·INTEGER·BIGINT·NUMERIC·REAL·DOUBLE PRECISION
  • 한국 표준 = BIGINT 기본 키, NUMERIC 금액
  • FLOAT 로 금액 X (0.1 + 0.2 ≠ 0.3)
  • TEXT = PG 문자열 표준 (CHAR·VARCHAR 거의 X)
  • TEXT 길이 = CHECK 제약
  • citext = 대소문자 무관 TEXT (이메일)
  • TIMESTAMPTZ = 시간대 포함 (표준)
  • TIMESTAMP = 시간대 X (위험)
  • INTERVAL = 기간 (INTERVAL '7 days')
  • DATE_TRUNC·EXTRACT = 시간 조작
  • BOOLEAN = TRUE·FALSE·NULL
  • UUID = 분산 시스템 (uuid-ossp)
  • UUIDv7 = 시간 정렬 (PG 18+)
  • JSONB = 이진 JSON·인덱스·키 정렬
  • JSON = 텍스트 보존 (거의 안 씀)
  • JSONB 연산자 = ->·->>·@>·?
  • 배열 = TEXT[]·INT[]
  • ANY·@>·&& 연산자
  • GIN 인덱스 = 배열·JSONB
  • 범위 타입 = TSTZRANGE·INT4RANGE
  • EXCLUDE 제약과 짝
  • INET·CIDR = IP·네트워크
  • ENUM = 안정적 상태만 (값 변경 어려움)
  • TEXT + CHECK = 더 유연
  • 기하 = POINT·LINE·POLYGON (PostGIS 확장)
  • BYTEA = 바이너리 (이미지·파일 — 작은 것만)

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

이전 글:

다음 글:

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

답글 남기기

error: Content is protected !!