PostgreSQL 교재 · 4편 / 24편

데이터 타입 — 무엇을 골라야 후회가 없나

PostgreSQL 의 핵심 데이터 타입 7묶음과 실전 선택 가이드.

입문읽는 시간 8분2026-05-16
PostgreSQL 의 다양한 데이터 타입이 카테고리별로 정리된 일러스트

데이터 타입은 한 번 정하면 바꾸기 비싸요. 100만 건 쌓인 뒤 ALTER TABLE 로 타입을 바꾸려면 락이 걸리고 시간이 오래 걸립니다. 그래서 4편은 처음에 잘 고르는 법을 정리합니다. PostgreSQL 의 수십 개 타입 중 실무 90% 를 차지하는 7묶음만 쳐냅니다.

숫자 — integer · numeric

타입범위 / 정밀도언제 쓰나
smallint-32K~32K매우 작은 enum 등
integer±21억대부분의 PK·카운터
bigint±9 × 10¹⁸대용량 서비스 PK (권장)
numeric(p,s)소수 정확 (느림)돈·세금·정확 계산
real / double precision부동소수측정값·과학 계산
serial / bigserial자동 증가레거시 패턴 — 새 코드는 IDENTITY 권장
-- 돈은 항상 numeric 으로
price NUMERIC(10, 2)   -- 10자리, 소수 2자리: 99999999.99 까지

-- float 의 함정
SELECT 0.1 + 0.2;             -- 0.30000000000000004
SELECT 0.1::numeric + 0.2;    -- 0.3  (정확)

문자열 — text 만 기억해도 된다

-- 거의 모든 경우 text
description TEXT

-- varchar(N) 도 있지만 PostgreSQL 에선 text 와 성능 차이 거의 없음
-- 길이 제약을 정말 강제하고 싶을 때만
code VARCHAR(8)

-- char(N) 은 피하세요 (남는 자리를 공백으로 채움)

한 줄. "PostgreSQL 에서는 text 만 써도 된다." MySQL 출신은 varchar(N) 에 익숙하지만, Postgres 에서는 길이 제약이 곧 성능이 아닙니다. 사용자 입력 검증은 애플리케이션 레이어에서, 정말 DB 레벨이 필요하면 CHECK (length(x) <= 100) 가 더 유연합니다.

시간 — timestamptz 가 거의 정답

created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
-- 'with time zone' — UTC 로 저장, 클라이언트 시간대로 출력

-- timestamp (without tz) — 권장하지 않음
-- date — 날짜만, time — 시간만, interval — 기간

왜 timestamptz 인가. Postgres 는 timestamptz 를 항상 UTC 로 저장합니다. 클라이언트가 SET TIME ZONE 'Asia/Seoul' 만 하면 자동으로 한국 시간으로 보입니다. timestamp(없는 버전) 은 시간대 정보가 사라져서, 서버 이전·DST·국제 사용자에서 버그의 온상이 됩니다.

boolean · uuid · enum

is_active BOOLEAN NOT NULL DEFAULT true

-- uuid — 분산 환경 PK 로 자주
id UUID PRIMARY KEY DEFAULT gen_random_uuid()
-- 사전: CREATE EXTENSION IF NOT EXISTS pgcrypto;

-- enum (CREATE TYPE)
CREATE TYPE order_status AS ENUM ('pending', 'paid', 'shipped', 'done');
status order_status NOT NULL DEFAULT 'pending';

enum vs check 제약 vs lookup 테이블: enum 은 값 추가가 무겁고(ALTER TYPE … ADD VALUE), 변경이 잦으면 lookup 테이블이 낫습니다. 작고 안정적인 상태값에 enum, 자주 추가될 분류는 별도 테이블.

jsonb — 구조 자유 데이터

attributes JSONB NOT NULL DEFAULT '{}'::jsonb

-- 입력
INSERT INTO products (name, attributes)
VALUES ('티셔츠', '{"size":"L","color":"black","tags":["sale","new"]}');

-- 조회 (12편에서 자세히)
SELECT name, attributes->>'color'  AS color
FROM   products
WHERE  attributes @> '{"size":"L"}';

-- 인덱스
CREATE INDEX ix_products_attrs ON products USING GIN (attributes);

json vs jsonb: 항상 jsonb 입니다. json 은 텍스트 그대로, jsonb 는 파싱된 바이너리 — 저장은 살짝 느리고 조회·연산은 훨씬 빠릅니다.

jsonb 의 함정. 편하다고 모든 걸 jsonb 에 넣지 마세요. 자주 검색·정렬하는 필드는 정식 컬럼으로 빼는 게 거의 항상 좋습니다. jsonb 는 "스키마 변경 비용 없이 가벼운 메타 정보" 용도가 본업입니다.

배열 — 정렬된 같은 타입 묶음

tags TEXT[]                  -- 문자열 배열
scores INTEGER[]             -- 정수 배열

INSERT INTO posts (title, tags) VALUES ('첫글', ARRAY['intro','draft']);

-- 검색
SELECT * FROM posts WHERE 'draft' = ANY(tags);
-- 인덱스
CREATE INDEX ix_posts_tags ON posts USING GIN (tags);

관계형 DB 정통 패턴은 별도 테이블이지만, "태그·역할·플래그 같은 간단한 다중 값" 은 배열이 훨씬 간결합니다. 검색·JOIN 이 복잡해지면 그때 별도 테이블로 분리.

실전 선택 — "처음 만드는 사람" 의 체크리스트

  • PK → BIGINT + GENERATED ALWAYS AS IDENTITY (또는 BIGSERIAL).
  • 분산·외부 노출 ID → UUID + gen_random_uuid().
  • 문자열 → TEXT. 길이 검증은 앱에서.
  • 시간 → TIMESTAMPTZ 항상. NOW() 가 기본값.
  • 돈 → NUMERIC(p, s). float 금지.
  • 구조 자유 메타 → JSONB + GIN.
  • 상태값 → 처음엔 TEXT CHECK, 안정되면 enum/lookup.

5편 — 첫 CRUD (INSERT·SELECT·UPDATE·DELETE)

이제 만든 테이블에 데이터를 넣고·읽고·바꾸고·지워봅니다. 안전한 UPDATE/DELETE 의 단 한 가지 규칙도 같이.

📚 PostgreSQL 배우기 교재
이전: 3편 DB·테이블 · 현재: 4편 (입문) · 다음 → 5편 CRUD · 진행: 4/24

© 2026 주나이테크(주) @JUNAITECH