RDMS/PostgreSQL

[PostgreSQL] 제약조건 설정시 주의해야할 부분을 고려하면서(PRIMARY KEY, NOT NULL, UNIQUE, CHECK) - 컴도리돌이

컴도리돌이 2024. 8. 30. 09:00
728x90

이전 포스팅에서 제약조건을 간단하게 설정하는 방법과 제약조건을 설정할 때 어느 부분을 고려해야 할지 짧게 확인해 보았어요. 이번에는 조금 더 세부적으로 설정하는 방법과 언제 사용하는지에 대해 스터디를 해보려고 해요. 

 

 

[PostgreSQL] 제약조건에 대해서(PRIMARY KEY, UNIQUE, NOT NULL, CHECK) - 컴도리돌이

PostgreSQL에서 제약조건에 대해 글을 남겨보려고 해요. 제약조건은 데이터베이스에서 우리가 입력하는 데이터가 올바르고 일관되도록 도와주는 규칙이라 생각하면 돼요. 예를 들어, 고객의 이메

comdolidol-i.tistory.com

 

적절하게 제약조건을 걸자!

제약조건을 설정할 때 적재적소하게 사용하는 게 중요하다고 했죠? 모든 필드에 제약조건을 걸고 싶은 유혹이 있을 수 있지만, 정말 필요한 곳에만 정확이 적용하는 것이 중요해요. 예를 들어, 사용자 테이블에서 필수 정보와 선택 정보를 구분해 본다 다음과 같아요.

CREATE TABLE users (
    user_id SERIAL PRIMARY KEY,  -- 각 사용자를 고유하게 식별하는 값
    username VARCHAR(50) NOT NULL,  -- 사용자 이름은 필수
    email VARCHAR(100) UNIQUE NOT NULL,  -- 이메일은 필수이면서 중복이 안 됨
    phone_number VARCHAR(15),  -- 전화번호는 선택 (NULL 허용)
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP  -- 계정 생성 시각, 기본값 설정
);

 

이 코드에서 볼 수 있듯이, 필수 정보 (username, email)에만 NOT NULL 제약조건을 설정했어요. 선택 정보인 phone_numberNULL을 허용해 유연성을 높였죠. 또 이메일 필드에는 UNIQUE 제약 조건을 사용해 중복된 이메일이 입력되지 않도록 했습니다. 

이처럼, 필요한 제약조건만 적절히 사용하면 데이터 베이스의 무결성을 유지하면서도 유연성을 확보할 수 있죠. 

 

 

5.4. Constraints

5.4. Constraints # 5.4.1. Check Constraints 5.4.2. Not-Null Constraints 5.4.3. Unique Constraints 5.4.4. Primary Keys 5.4.5. Foreign Keys 5.4.6. Exclusion Constraints …

www.postgresql.org


확장성을 고려해서 제약조건을 걸자!

지금 설계한 테이블 구조가 어떻게 변화할지 모르는 일이에요. 그래서 미래의 변화에 대비해 제약 조건을 설정하는 것도 중요해요. 🔥

현재는 한 사용자에게 하나의 이메일만 할당되도록 설정했지만, 나중에 여러 이메일을 허용할 가능성을 고려할 수 있어요. 이를 위해, 이메일 테이블을 별도로 분리하고 제약조건을 설정해 보는 방법이 있습니다. 

CREATE TABLE user_emails (
    email_id SERIAL PRIMARY KEY,
    user_id INT NOT NULL,
    email VARCHAR(100) NOT NULL,
    is_primary BOOLEAN DEFAULT FALSE,
    CONSTRAINT fk_user
        FOREIGN KEY(user_id) 
        REFERENCES users(user_id) 
        ON DELETE CASCADE,
    CONSTRAINT unique_user_email UNIQUE(user_id, email)
);

 

user_emails라는 테이블을 만들어 사용자의 여러 이메일을 관리할 수 있도록 했어요. UNIQUE(user_id, email) 제약 조건을 통해, 한 사용자가 동일한 이메일을 여러 번 등록하지 못하도록 방지했죠. 이렇게 하면, 나중에 요구사항이 변경되더라도 구조를 쉽게 확장할 수 있어요.

 

 

Best Practices for Database Design in PostgreSQL

In the realm of database management systems, PostgreSQL stands tall as a robust and versatile option. Its rich feature set, combined with…

medium.com


협업을 고려해서 제약조건을 걸자!

지금 설정한 제약 조건을 다른 개발자들은 모를 수가 있어요 🙄 그럴 때를 대비해서 제약 조건의 의도를 쉽게 이해할 수 있도록, 명확한 코드와 함께 주석을 달아두는 것이 좋아요.

CREATE TABLE orders (
    order_id SERIAL PRIMARY KEY,
    user_id INT NOT NULL,
    product_id INT NOT NULL,
    quantity INT CHECK (quantity > 0),  -- 수량은 0보다 커야 한다는 제약조건
    order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT fk_user
        FOREIGN KEY(user_id) 
        REFERENCES users(user_id)
        ON DELETE CASCADE,  -- 사용자가 삭제되면 해당 주문도 삭제
    CONSTRAINT fk_product
        FOREIGN KEY(product_id) 
        REFERENCES products(product_id)
);

 

각 제약 조건이 어떤 역할을 하는지 주석으로 설명이 되어 있죠. 예를 들어, quantity 필드에는 CHECK (quantity > 0) 제약 조건을 설정해 잘못된 수량이 입력되지 않도록 했고, FOREIGN KEY 제약 조건을 통해 사용자가 삭제되면 해당 주문도 자동으로 삭제되도록 했어요. 주석을 통해, 이 제약조건이 왜 필요한지 명확히 설명하면, 다른 개발자들이 코드를 이해하는 데 큰 도움이 될 겁니다 ✅


 

성능을 고려해서 제약조건을 걸자!

제약 조건을 설정할 때 데이터베이스의 성능에 미치는 영향을 고려하는 것이 매우 중요하죠. 제약 조건은 데이터 무결성을 유지하는 데 매우 유용하지만, 특히 대용량 데이터를 다루는 경우 성능에 영향을 줄 수 있어요. 🙅‍♂️

예를 들어 CHECK 제약조건이나 복합 UNIQUE 제약 조건을 설정하기 전에 성능 테스트를 진행하거나, 필요에 따라 인덱스를 추가하여 성능을 최적화할 수 있는 방법을 고민해봐야 해요 🤔

CREATE TABLE transactions (
    transaction_id SERIAL PRIMARY KEY,
    account_id INT NOT NULL,
    amount DECIMAL(10, 2) NOT NULL,
    transaction_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT chk_amount CHECK (amount > 0),    -- 트랜잭션 금액이 양수여야 함
    CONSTRAINT fk_account
        FOREIGN KEY (account_id) 
        REFERENCES accounts(account_id)
        ON DELETE CASCADE
);
CREATE INDEX idx_account_transaction_date ON transactions (account_id, transaction_date);

 

transactions 테이블을 생성하고, CHECK 제약조건을 사용해 amount 필드가 양수만 허용되도록 설정했어요. 또한 account_idtransaction_date에 인덱스를 추가하여 성능을 최적화할 수 있습니다. CHECK 제약조건이 있는 경우 특히 대용량 데이터에서 성능에 영향을 줄 수 있으므로, 인덱스를 통해 성능 저하를 방지할 수 있어요. 👍


유지보수를 고려해서 제약조건을 걸자!

마지막으로, 유지보수를 고려한 제약조건 설정도 중요해요. 데이터베이스는 시간이 지남에 따라 변경되고 확장될 가능성이 큽니다. 제약조건을 설정할 때, 나중에 이 제약조건이 유지보수 하기 쉬운지, 변경이 용이한지를 생각해야 해요. 예를 들어, 너무 복잡한 제약조건은 향후 요구사항이 변경될 때  수정이 어렵고, 오류를 일으킬 가능성이 있어요. 그렇기에 유지보수를 염두에 두고 최대한 간결하고 이해하기 쉬운 제약 조건을 설정해줘야 해요 🤔

CREATE TABLE promotions (
    promotion_id SERIAL PRIMARY KEY,
    promotion_name VARCHAR(100) NOT NULL,
    discount_rate DECIMAL(5, 2) CHECK (discount_rate BETWEEN 0 AND 100), -- 할인율이 0~100% 사이여야 함
    start_date DATE NOT NULL,
    end_date DATE NOT NULL,
    CHECK (end_date >= start_date)  -- 종료일이 시작일보다 늦어야 함
);

 

promotions 테이블을 생성하고, discount_rate가 0에서 100 사이의 값인지 확인하는 CHECK 제약조건과 end_datestart_date보다 크거나 같아야 한다는 조건을 설정했어요. 이 제약조건은 논리적으로 간결하면서도 명확하여, 나중에 유지보수할 때 수정이 용이합니다. 복잡하지 않으면서도 핵심적인 비즈니스 로직을 간결하게 표현한 제약조건은 유지보수를 쉽게 만들어줍니다.