제약 조건
데이터베이스에 잘못된 데이터가 들어가면 시스템 전체가 혼란에 빠집니다. 나이가 -5인 회원, 존재하지 않는 부서에 배정된 직원, 같은 이메일로 가입한 두 명의 회원. 이런 문제를 애플리케이션 코드에서만 검증하면 실수나 버그로 잘못된 데이터가 들어갈 수 있습니다. 제약 조건(Constraint)은 DBMS가 데이터의 무결성을 자동으로 보장하는 규칙입니다. 어떤 경로로 데이터가 들어오든(SQL 직접 실행, 애플리케이션, 배치 프로그램) 제약 조건을 만족하지 않으면 DBMS가 거부합니다.
제약 조건은 데이터 품질의 마지막 방어선입니다. 애플리케이션에서 검증하더라도 DBMS에 제약 조건을 설정해야 하고, 반대로 DBMS에 제약 조건이 있더라도 불필요한 에러를 줄이기 위해 애플리케이션에서도 검증해야 합니다. 이 이중 검증이 실무의 표준입니다.
이 절에서는 각 제약 조건의 개념과 문법, DBMS별 차이, 복합 키, 외래키의 참조 옵션, 제약 조건 관리, 그리고 실무에서의 설계 원칙까지 깊이 있게 다룹니다.
제약 조건의 종류
| 제약 조건 | 역할 | 허용 | 예시 |
|---|---|---|---|
| PRIMARY KEY | 행을 유일하게 식별 | NULL 불가, 중복 불가 | 사원번호, 주문번호 |
| FOREIGN KEY | 다른 테이블과의 관계 보장 | 참조 테이블에 있는 값만 허용 | 주문의 고객ID |
| UNIQUE | 중복 불가 | NULL 허용 (DBMS마다 다름) | 이메일, 주민번호 |
| NOT NULL | NULL 불가 | 빈 값 금지 | 이름, 비밀번호 |
| CHECK | 조건식 검사 | 조건을 만족하는 값만 허용 | 나이 > 0, 성별 IN ('M','F') |
| DEFAULT | 값 미지정 시 기본값 | 제약보다는 편의 기능 | 가입일 = 오늘, 상태 = 'ACTIVE' |
PRIMARY KEY — 기본키
기본키는 테이블에서 각 행을 유일하게 식별하는 컬럼(또는 컬럼 조합)입니다. 기본키의 핵심 특성은 두 가지입니다. 값이 유일해야 하고(UNIQUE), NULL이어서는 안 됩니다(NOT NULL). 즉 PRIMARY KEY = UNIQUE + NOT NULL입니다.
-- 컬럼 레벨 정의
CREATE TABLE employees (
emp_id INT PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
-- 테이블 레벨 정의 (이름 지정)
CREATE TABLE employees (
emp_id INT,
name VARCHAR(100) NOT NULL,
CONSTRAINT pk_emp PRIMARY KEY (emp_id)
);복합 기본키
두 개 이상의 컬럼을 조합하여 기본키로 사용할 수 있습니다. 다대다(M:N) 관계의 연결 테이블에서 흔히 사용됩니다.
-- 학생-과목 수강 테이블 (학생ID + 과목ID로 식별)
CREATE TABLE enrollment (
student_id INT,
course_id INT,
grade VARCHAR(2),
CONSTRAINT pk_enrollment PRIMARY KEY (student_id, course_id)
);
-- (1, 101, 'A')와 (1, 102, 'B')는 허용
-- (1, 101, 'A')와 (1, 101, 'B')는 중복 — 같은 학생이 같은 과목을 두 번 수강복합키에서 유의할 점은, 개별 컬럼은 중복될 수 있지만 조합이 유일해야 한다는 것입니다. student_id=1이 여러 번 나올 수 있고, course_id=101이 여러 번 나올 수 있지만, (student_id=1, course_id=101) 조합은 한 번만 나올 수 있습니다.
자연키 vs 대리키
기본키를 선택할 때 두 가지 접근이 있습니다.
자연키 (Natural Key):
비즈니스 의미가 있는 컬럼을 기본키로 사용
예: 주민등록번호, 이메일, ISBN
장점: 별도의 ID 컬럼 불필요
단점: 비즈니스 규칙 변경 시 키도 변경해야 함
(예: 주민번호 수집 금지법)
대리키 (Surrogate Key):
비즈니스 의미 없이 순수하게 식별만을 위한 컬럼
예: AUTO_INCREMENT, SEQUENCE, UUID
장점: 비즈니스 변화에 영향 없음, 조인 성능 우수
단점: 별도의 ID 컬럼 필요, 비즈니스 의미 없음실무에서는 대리키를 기본키로 사용하고, 자연키는 UNIQUE 제약으로 관리하는 것이 일반적입니다. 비즈니스 규칙은 변할 수 있지만, 대리키는 변하지 않기 때문입니다.
기본키와 인덱스
기본키를 정의하면 DBMS가 자동으로 인덱스를 생성합니다. Oracle은 기본키에 유니크 인덱스를, MySQL InnoDB는 클러스터드 인덱스를 생성합니다. 이 인덱스 덕분에 기본키로 조회하는 쿼리가 매우 빠릅니다.
Oracle: UNIQUE INDEX (비클러스터드)
MySQL: CLUSTERED INDEX (행 데이터가 PK 순서로 저장됨)
PostgreSQL: UNIQUE INDEX (비클러스터드)
SQL Server: CLUSTERED INDEX (기본, 변경 가능)MySQL InnoDB에서 클러스터드 인덱스는 테이블 데이터의 물리적 저장 순서를 결정합니다. 따라서 기본키가 정수형 AUTO_INCREMENT이면 새 행이 항상 마지막에 추가되어 효율적이지만, UUID처럼 무작위 값이면 데이터 페이지가 조각나서 성능이 저하될 수 있습니다.
FOREIGN KEY — 외래키
외래키는 두 테이블 간의 관계를 정의하고, 참조 무결성을 보장합니다. 자식 테이블의 외래키 값은 부모 테이블의 참조된 컬럼에 반드시 존재해야 합니다(또는 NULL).
-- 컬럼 레벨
CREATE TABLE orders (
order_id INT PRIMARY KEY,
user_id INT REFERENCES users(user_id),
amount INT NOT NULL
);
-- 테이블 레벨 (이름 지정)
CREATE TABLE orders (
order_id INT PRIMARY KEY,
user_id INT NOT NULL,
amount INT NOT NULL,
CONSTRAINT fk_orders_user FOREIGN KEY (user_id)
REFERENCES users(user_id)
);외래키가 참조하는 부모 테이블의 컬럼은 반드시 PRIMARY KEY이거나 UNIQUE 제약이 있어야 합니다. 유일하지 않은 컬럼을 참조하면 어떤 행을 가리키는가가 모호해지기 때문입니다.
복합 외래키
부모 테이블의 기본키가 복합키이면 외래키도 같은 컬럼 조합을 참조해야 합니다.
CREATE TABLE exam_scores (
score_id INT PRIMARY KEY,
student_id INT NOT NULL,
course_id INT NOT NULL,
score INT,
CONSTRAINT fk_scores_enrollment
FOREIGN KEY (student_id, course_id)
REFERENCES enrollment(student_id, course_id)
);자기 참조 외래키 (Self-Referencing FK)
같은 테이블 내에서 외래키를 참조하는 패턴입니다. 조직도(상사-부하 관계), 카테고리 계층 구조 등에서 사용됩니다.
CREATE TABLE employees (
emp_id INT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
manager_id INT,
CONSTRAINT fk_emp_manager FOREIGN KEY (manager_id)
REFERENCES employees(emp_id)
);
-- 최상위 관리자 (사장)
INSERT INTO employees VALUES (1, '사장', NULL);
-- 부장 (사장의 부하)
INSERT INTO employees VALUES (2, '부장', 1);
-- 과장 (부장의 부하)
INSERT INTO employees VALUES (3, '과장', 2);ON DELETE / ON UPDATE 옵션
부모 테이블의 행이 삭제되거나 기본키가 변경되었을 때, 자식 테이블의 외래키를 어떻게 처리할지 결정합니다.
departments 테이블
┌────┬───────┐
│ id │ name │
├────┼───────┤
│ 1 │ 개발팀│
│ 2 │ 기획팀│
└────┴───────┘
employees 테이블
┌────┬──────┬─────────┐
│ id │ name │ dept_id │
├────┼──────┼─────────┤
│ 1 │김철수│ 1 │
│ 2 │이영희│ 1 │
│ 3 │박민수│ 2 │
└────┴──────┴─────────┘
employees.dept_id → departments.id
DELETE FROM departments WHERE id = 1; → 어떻게 되나?| 옵션 | 부모 삭제 시 동작 | 사용 시나리오 |
|---|---|---|
| RESTRICT (기본) | 에러! 삭제 거부 | 안전이 최우선인 경우 |
| NO ACTION | RESTRICT와 유사 (체크 시점이 다름) | SQL 표준 기본값 |
| CASCADE | 자식 행도 함께 삭제 | 주문 삭제 → 주문상세도 삭제 |
| SET NULL | 자식의 FK를 NULL로 변경 | 부서 삭제 → 직원의 부서를 NULL로 |
| SET DEFAULT | 자식의 FK를 DEFAULT 값으로 변경 | 지원 DBMS 제한적 |
CREATE TABLE employees (
emp_id INT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
dept_id INT,
CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id)
REFERENCES departments(dept_id)
ON DELETE SET NULL
ON UPDATE CASCADE
);ON DELETE SET NULL은 부서가 삭제되면 해당 부서 직원의 dept_id를 NULL로 변경합니다. ON UPDATE CASCADE는 부서 ID가 변경되면 직원의 dept_id도 자동으로 같은 값으로 변경됩니다.
users → orders → order_items → shipments
users에 ON DELETE CASCADE가 설정되어 있다면:
DELETE FROM users WHERE user_id = 1;
→ user_id=1의 주문 삭제
→ 해당 주문의 주문상세 삭제
→ 해당 주문상세의 배송정보 삭제
한 명의 회원 삭제로 수백 건의 데이터가 연쇄 삭제!
→ 실무에서는 CASCADE를 신중하게 사용해야 합니다UNIQUE — 유일성 제약
UNIQUE 제약은 컬럼의 값이 테이블 내에서 유일해야 함을 보장합니다. PRIMARY KEY와의 차이는 NULL을 허용한다는 점과, 한 테이블에 여러 개의 UNIQUE 제약을 둘 수 있다는 점입니다.
CREATE TABLE users (
user_id INT PRIMARY KEY,
email VARCHAR(100) NOT NULL,
phone VARCHAR(20),
CONSTRAINT uq_email UNIQUE (email),
CONSTRAINT uq_phone UNIQUE (phone)
);NULL과 UNIQUE의 관계
UNIQUE 컬럼에 NULL이 여러 개 들어갈 수 있는지는 DBMS마다 다릅니다.
| DBMS | UNIQUE 컬럼에 NULL 여러 개 허용? |
|---|---|
| Oracle | 허용 |
| MySQL | 허용 |
| PostgreSQL | 허용 |
| SQL Server | 불허 (NULL도 하나만 가능) |
SQL Server는 NULL도 하나의 값으로 취급하여 UNIQUE 제약을 적용합니다. 이 차이를 모르면 DBMS를 바꿀 때 예상치 못한 에러가 발생할 수 있습니다.
복합 UNIQUE
여러 컬럼의 조합에 유일성을 적용할 수 있습니다. 개별 컬럼은 중복 가능하지만, 조합이 유일해야 합니다.
-- 같은 부서에 같은 직급은 하나만 (부서-직급 조합 유일)
CREATE TABLE positions (
dept_id INT,
title VARCHAR(50),
emp_id INT,
CONSTRAINT uq_dept_title UNIQUE (dept_id, title)
);NOT NULL
NOT NULL은 해당 컬럼에 NULL 값을 허용하지 않는 제약입니다. 반드시 값이 있어야 하는 컬럼에 설정합니다.
CREATE TABLE users (
user_id INT PRIMARY KEY,
username VARCHAR(50) NOT NULL, -- 반드시 입력
email VARCHAR(100) NOT NULL, -- 반드시 입력
phone VARCHAR(20) -- NULL 허용
);NOT NULL은 다른 제약 조건과 달리 컬럼 레벨에서만 정의할 수 있습니다. 테이블 레벨에서 CONSTRAINT ... NOT NULL 형태로 정의할 수 없습니다(Oracle에서는 가능하지만 비표준).
NULL을 허용할지 결정하는 기준
반드시 NOT NULL:
* 기본키 (PK에 자동 포함)
* 로그인에 필수인 컬럼 (username, email, password)
* 비즈니스 로직에 반드시 필요한 값 (주문의 상품ID, 수량)
* 상태값 (status, is_active 등)
NULL 허용 가능:
* 선택적 입력 정보 (phone, address, bio)
* 나중에 결정되는 값 (배송일, 결제일)
* 외래키 (관계가 선택적인 경우)
* 자기 참조 FK (최상위 노드는 부모가 없음)CHECK — 조건 검사
CHECK 제약은 컬럼에 입력되는 값이 특정 조건을 만족하는지 검사합니다.
CREATE TABLE employees (
emp_id INT PRIMARY KEY,
name VARCHAR(100) NOT NULL,
age INT CHECK (age >= 0 AND age <= 150),
salary INT,
gender CHAR(1),
CONSTRAINT ck_salary CHECK (salary >= 0),
CONSTRAINT ck_gender CHECK (gender IN ('M', 'F'))
);CHECK 제약의 한계
CHECK 제약은 단일 행의 단일 테이블에 대해서만 검사할 수 있습니다. 다른 테이블을 참조하는 조건이나 서브쿼리는 사용할 수 없습니다.
-- 가능
CHECK (price >= 0)
CHECK (status IN ('ACTIVE', 'INACTIVE', 'DELETED'))
CHECK (start_date < end_date) -- 같은 행의 다른 컬럼 참조 가능
-- 불가능
CHECK (salary < (SELECT max_salary FROM pay_grades WHERE ...)) -- 서브쿼리
CHECK (email NOT IN (SELECT email FROM blocked_emails)) -- 다른 테이블다른 테이블을 참조하는 복잡한 검증은 트리거나 애플리케이션 로직으로 구현해야 합니다.
MySQL의 CHECK 제약 주의사항
MySQL 8.0.16 미만 버전에서는 CHECK 제약을 구문적으로 허용하지만 실제로 검사하지 않습니다. 즉, CHECK 제약을 정의해도 제약 위반 데이터가 들어갑니다. MySQL 8.0.16부터 정상적으로 작동합니다.
SELECT VERSION();
-- 8.0.16 이상이어야 CHECK가 실제로 동작DEFAULT — 기본값
DEFAULT는 INSERT 시 값을 명시하지 않으면 자동으로 설정되는 값입니다. 엄밀히 말하면 제약 조건이 아니라 편의 기능이지만, 제약 조건과 함께 다루는 것이 일반적입니다.
CREATE TABLE orders (
order_id INT PRIMARY KEY,
status VARCHAR(20) DEFAULT 'PENDING',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_deleted BOOLEAN DEFAULT FALSE
);
-- status, created_at, is_deleted를 생략하면 DEFAULT 값 적용
INSERT INTO orders (order_id) VALUES (1);
-- 결과: order_id=1, status='PENDING', created_at=현재시각, is_deleted=FALSEDEFAULT에는 상수뿐 아니라 함수도 사용할 수 있습니다. CURRENT_TIMESTAMP, CURRENT_DATE, SYSDATE(Oracle) 등이 흔히 사용됩니다.
제약 조건 이름 지정
모든 제약 조건에는 이름을 지정하는 것이 좋습니다. 이름이 없으면 DBMS가 자동으로 이름을 생성하는데(SYS_C007234 같은 형태), 에러 발생 시 어떤 제약을 위반했는지 알기 어렵습니다.
CREATE TABLE employees (
id NUMBER,
name VARCHAR2(100),
email VARCHAR2(200),
dept_id NUMBER,
salary NUMBER,
CONSTRAINT pk_emp PRIMARY KEY (id),
CONSTRAINT uq_emp_email UNIQUE (email),
CONSTRAINT fk_emp_dept FOREIGN KEY (dept_id)
REFERENCES departments(dept_id)
ON DELETE SET NULL,
CONSTRAINT ck_emp_salary CHECK (salary >= 0),
CONSTRAINT nn_emp_name CHECK (name IS NOT NULL)
);이름 없을 때 에러
ORA-02290: check constraint (HR.SYS_C007234) violated
→ "SYS_C007234가 뭐지?" → 데이터 딕셔너리 조회 필요
이름 있을 때 에러
ORA-02290: check constraint (HR.CK_EMP_SALARY) violated
→ "아, 급여가 음수구나!" → 즉시 원인 파악제약 조건 이름 규칙
| 접두어 | 의미 | 예시 |
|---|---|---|
| pk_ | PRIMARY KEY | pk_employees |
| fk_ | FOREIGN KEY | fk_orders_user |
| uq_ | UNIQUE | uq_users_email |
| ck_ | CHECK | ck_age_positive |
| nn_ | NOT NULL | nn_users_name |
SEQUENCE와 자동 증가
기본키에 자동으로 증가하는 숫자를 할당하는 방법은 DBMS마다 다릅니다.
Oracle — SEQUENCE
-- 시퀀스 생성
CREATE SEQUENCE emp_seq
START WITH 1
INCREMENT BY 1
MAXVALUE 9999999
NOCACHE; -- 캐시 없이 순차 생성
-- 사용
INSERT INTO employees (id, name)
VALUES (emp_seq.NEXTVAL, '김철수');
-- 현재 값 확인
SELECT emp_seq.CURRVAL FROM DUAL;
-- 시퀀스 수정
ALTER SEQUENCE emp_seq INCREMENT BY 10;
-- 시퀀스 삭제
DROP SEQUENCE emp_seq;SEQUENCE는 테이블과 독립적인 객체입니다. 하나의 시퀀스를 여러 테이블에서 공유할 수 있고, 하나의 테이블에 여러 시퀀스를 사용할 수도 있습니다.
CACHE 옵션은 성능에 영향을 줍니다. CACHE 20이면 메모리에 20개의 시퀀스 값을 미리 생성해두어 디스크 접근을 줄입니다. 하지만 DBMS 비정상 종료 시 캐시된 값이 손실되어 번호가 건너뛸 수 있습니다.
MySQL — AUTO_INCREMENT
CREATE TABLE employees (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
-- id를 지정하지 않으면 자동 증가
INSERT INTO employees (name) VALUES ('김철수');
INSERT INTO employees (name) VALUES ('이영희');
-- 마지막 생성된 ID 확인
SELECT LAST_INSERT_ID();
-- AUTO_INCREMENT 값 재설정
ALTER TABLE employees AUTO_INCREMENT = 100;AUTO_INCREMENT의 특징은 테이블당 하나만 사용할 수 있고, 반드시 인덱스(PK 또는 UNIQUE)가 있어야 한다는 것입니다.
PostgreSQL — SERIAL / IDENTITY
-- SERIAL (레거시)
CREATE TABLE employees (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
-- 내부적으로 시퀀스 자동 생성
-- IDENTITY (SQL 표준, PostgreSQL 10+)
CREATE TABLE employees (
id INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
-- GENERATED BY DEFAULT: 명시적 값 지정 허용
CREATE TABLE employees (
id INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
name VARCHAR(100) NOT NULL
);GENERATED ALWAYS는 ID를 직접 지정할 수 없고, GENERATED BY DEFAULT는 직접 지정도 허용합니다. 데이터 마이그레이션 시에는 BY DEFAULT가 편리합니다.
Oracle 12c+ — IDENTITY
CREATE TABLE employees (
id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name VARCHAR2(100) NOT NULL
);Oracle 12c부터는 SEQUENCE를 직접 만들지 않아도 MySQL과 유사한 자동 증가 컬럼을 사용할 수 있습니다.
| DBMS | 방식 | 테이블당 제한 | 여러 테이블 공유 |
|---|---|---|---|
| Oracle | SEQUENCE | 제한 없음 | 가능 |
| Oracle 12c+ | IDENTITY | 1개 | 불가 |
| MySQL | AUTO_INCREMENT | 1개 | 불가 |
| PostgreSQL | SERIAL/IDENTITY | 제한 없음 | 불가 (SERIAL은 내부 시퀀스) |
| SQL Server | IDENTITY | 1개 | 불가 |
제약 조건 관리
제약 조건 조회
-- 테이블의 제약 조건 목록
SELECT constraint_name, constraint_type, status
FROM user_constraints
WHERE table_name = 'EMPLOYEES';
-- 제약 조건의 컬럼 정보
SELECT constraint_name, column_name
FROM user_cons_columns
WHERE table_name = 'EMPLOYEES';-- 테이블 구조와 제약 조건 확인
SHOW CREATE TABLE employees;
-- information_schema에서 조회
SELECT constraint_name, constraint_type
FROM information_schema.table_constraints
WHERE table_name = 'employees';-- 테이블의 제약 조건
SELECT conname, contype, pg_get_constraintdef(oid)
FROM pg_constraint
WHERE conrelid = 'employees'::regclass;제약 조건 추가와 삭제
이미 데이터가 있는 테이블에 제약 조건을 추가할 때는 기존 데이터가 제약을 만족하는지 확인해야 합니다.
-- NOT NULL 추가 전: NULL 데이터 확인
SELECT COUNT(*) FROM employees WHERE phone IS NULL;
-- CHECK 추가 전: 조건 위반 데이터 확인
SELECT * FROM employees WHERE salary < 0;
-- UNIQUE 추가 전: 중복 데이터 확인
SELECT email, COUNT(*) FROM employees GROUP BY email HAVING COUNT(*) > 1;
-- FK 추가 전: 참조 무결성 위반 데이터 확인
SELECT e.dept_id FROM employees e
WHERE e.dept_id NOT IN (SELECT dept_id FROM departments);이런 확인 없이 제약 조건을 추가하면 기존 데이터가 제약을 위반합니다라는 에러가 발생합니다.
제약 조건 설계 원칙
1. 기본키는 대리키(INT AUTO_INCREMENT)를 사용하라
→ 비즈니스 키(이메일, 주민번호)는 UNIQUE로 관리
2. 외래키는 반드시 설정하라
→ 애플리케이션만으로는 참조 무결성을 보장할 수 없음
→ 성능 이유로 FK를 생략하는 팀도 있지만, 데이터 정합성 위험
3. NOT NULL을 적극 사용하라
→ "이 컬럼이 NULL일 수 있는가?"를 항상 고민
→ NULL을 허용하면 모든 쿼리에서 NULL 처리가 필요해짐
4. CHECK로 비즈니스 규칙을 DBMS 레벨에서 보장하라
→ price >= 0, quantity > 0, status IN (...) 등
5. 제약 조건에는 반드시 이름을 붙여라
→ 에러 추적과 유지보수가 쉬워짐
6. CASCADE는 신중하게 사용하라
→ 연쇄 삭제의 범위를 정확히 이해하고 적용
→ 대부분의 경우 RESTRICT + 소프트 삭제가 안전요약
| 제약 | 역할 | NULL | 중복 | 테이블당 개수 |
|---|---|---|---|---|
| PRIMARY KEY | 행 식별 | 불가 | 불가 | 1개 |
| FOREIGN KEY | 관계 보장 | 가능 | 가능 | 여러 개 |
| UNIQUE | 중복 방지 | 가능 (DBMS마다) | 불가 | 여러 개 |
| NOT NULL | 빈값 방지 | 불가 | 가능 | 여러 개 |
| CHECK | 조건 검사 | — | — | 여러 개 |
| DEFAULT | 기본값 | — | — | 여러 개 |
제약 조건은 데이터 품질의 마지막 방어선입니다. 애플리케이션의 버그, 잘못된 SQL, 다른 팀의 직접 데이터 수정 등 어떤 경로를 통하더라도 DBMS가 제약 조건을 검사하여 잘못된 데이터의 입력을 거부합니다. 성능 최적화를 위해 제약 조건을 제거하는 것은 단기적으로는 빠르지만, 장기적으로는 데이터 품질 저하와 디버깅 비용 증가로 이어집니다.
다음 절에서는 테이블을 수정하고 삭제하는 방법을 다루겠습니다.