sungyup's.

PostgreSQL / PostgreSQL / 2.3 Database-Side Validation and Constraints

2.3Database-Side Validation and Constraints

데이터 단위 제약을 설정해 데이터 무결성 보장하기

TL;DR

Row-Level Validation: 데이터 단위 제약 설정하기

PostgreSQL에서는 행(row) 단위로 유효성 검사를 할 수 있는 다양한 방법이 제공된다. 이를 통해 잘못된 데이터가 들어오는 것을 근본적으로 차단할 수 있다.

예시 테이블 구조

sql
CREATE TABLE products ( id SERIAL PRIMARY KEY, name VARCHAR(40), department VARCHAR(40), price INTEGER, weight INTEGER )

1. NOT NULL 제약

값이 반드시 있어야 하는 열(예: ID, 값 등)에 NOT NULL 제약을 건다.

테이블 생성 시

sql
price INTEGER NOT NULL //....

테이블 생성 후 적용

sql
ALTER TABLE products ALTER COLUMN price SET NOT NULL;
  • 만약 이미 NULL 값이 있으면 에러가 뜬다.
  • 이럴 때는 기존 데이터를 먼저 수정한 후 적용한다.
sql
UPDATE products SET price = 9 WHERE price IS NULL -- WHERE price = NULL 아님!

2. 기본값 설정(DEFAULT)

값을 명시하지 않았을 때 자동으로 들어갈 기본값을 설정할 수 있다.

테이블 생성 시

sql
price INTEGER DEFAULT 9

테이블 생성 후 적용

sql
ALTER TABLE products ALTER COLUMN price SET DEFAULT 9;
  • 기본값은 INSERT 시 값을 생략하면 자동 적용된다.
  • 기본값은 언제든 변경 가능하다.

3. 고유값 제한(UNIQUE)

특정 컬럼에 중복된 값이 들어가지 않도록 한다.

테이블 생성 시

sql
name VARCHAR(50) UNIQUE

테이블 생성 후 적용

sql
ALTER TABLE products ADD UNIQUE(name);

4. 여러 컬럼 조합에 대한 UNIQUE

두 개 이상의 컬럼 조합이 고유해야 하는 경우도 있다.

sql
ALTER TABLE products ADD UNIQUE (name, department);

기존 제약 제거

기존의 constraint를 없애려면, Schemas/Public/Tables에서 만들어둔 테이블로 들어간다. Constraints 섹션에서 없애려는 constraint 이름을 찾아 우클릭을 해 Query Tool 메뉴를 눌러 쿼리 툴로 들어가, 아래의 쿼리를 입력한다.

sql
ALTER TABLE products DROP CONSTRAINT products_name_key;

5. 값에 유효성 검사 조건 걸기(CHECK)

아래와 같은 경우는 잘못된 데이터(음수 가격)이므로 추가되면 안된다.

sql
INSERT INTO products (name, department, price, weight) VALUES ('Belt', 'Clothes', -99, 1);

이런 경우 ADD CHECK로 체크 조건문을 추가하는데, 체크는 우리가 추가/업데이트 하는 row에만 적용된다.

sql
ALTER TABLE products ADD CHECK (price > 0);

6. 컬럼 간에 Validation Check 추가하기

아래와 같이 주문 내역을 관리하는 테이블을 만든다고 하자.

sql
CREATE TABLE orders ( id SERIAL PRIMARY KEY, name VARCHAR(40) NOT NULL, created_at TIMESTAMP NOT NULL, est_delivery TIMESTAMP NOT NULL, CHECK (created_at < est_delivery) );

이 테이블의 경우, 예상 배송 시간(estimated delivery)이 주문 생성 시간(created_at)보다 항상 뒤여야 함을 체크로 지정한 것이다. 이렇게 체크를 걸어두면, 아래의 예시는 에러를 발생시킨다.

sql
INSERT INTO orders (name, created_at, est_delivery) VALUES ('Shirt', '2000-NOV-20 01:00AM'::TIMESTAMP, '2000-NOV-10 01:00AM');

7. Validation은 어디에 적용해야할까?

유저 -> 웹 서버 -> 데이터베이스로 이어지는 플로우에서 어디에 Validation을 적용해야 할까?

웹 서버에서 유효성 검사

  • 장점:
    • 사용자마다 다른 조건 처리 가능
    • 복잡한 비즈니스 로직 처리 가능
    • 대부분의 프로그래밍 언어가 타입 기반 유효성 검사 지원
  • 단점:
    • 클라이언트가 우회하면 잘못된 데이터가 들어갈 수도 있음

데이터베이스에서 유효성 검사

  • 장점:
    • 모든 경로에서 데이터 무결성을 강제할 수 있음(API든 SQL이든)
    • 핵심적인 데이터 규칙을 데이터베이스에 직접 명시 가능
  • 단점:
    • 제약 추가가 어렵다(기존 모든 행이 조건을 만족해야 한다)
      • 예: NOT NULL을 추가하려면 기존 데이터의 모든 행이 NULL이 아니어야만 가능하다
    • 사용자마다 다른 조건을 넣는 등의 유연한 로직 구현은 어렵다

이런 차이 때문에 보통은 데이터베이스에 기본적이고 중요한 체크들(예: 가격은 0보다 커야한다)을 적용하고 다른 동적이고 복잡한 유효성 검사들(예: 회원 등급별 구매 제한, 유저별 조건 차등 처리)은 웹 서버에 적용한다.