MariaDB 강좌 / NULL·기본값·NOT NULL·CHECK로 입력 조건 정하기
NULL은 값이 없거나 알려지지 않았다는 상태이며 0이나 빈 문자열과 다릅니다. DEFAULT는 입력을 생략했을 때 사용할 값을 정하고, NOT NULL은 NULL을 막으며, CHECK는 입력값이 업무 조건을 만족하는지 검사하므로 각각의 역할을 구분해 조합해야 합니다.
NULL·빈 문자열·0·입력 생략 구분하기
| 입력 상태 | 뜻 | 이 실습의 예 |
|---|---|---|
| NULL | 값을 모르거나 아직 정하지 않음 | 상품 메모가 수집되지 않음 |
| 빈 문자열 '' | 길이가 0인 문자열 값 | 메모란을 비워 제출함 |
| 0 | 숫자 값이 0 | 재고가 없음 |
| 열 입력 생략 | INSERT의 열 목록에서 제외 | stock의 DEFAULT 0이 적용됨 |
업무에 따라 메모 미수집과 빈 메모를 같은 상태로 처리할 수도 있지만, 그 선택은 데이터 정책으로 정해야 합니다. NULL을 단순히 빈 문자열로 치환하면 정보를 수집하지 않은 경우와 사용자가 비워 둔 경우를 구분할 수 없게 됩니다. 이 글은 일반적인 MariaDB 모드에서의 차이를 다루며 EMPTY_STRING_IS_NULL 같은 호환 모드는 실습에 사용하지 않습니다.
NULL과 비교한 결과는 대체로 참·거짓이 아니라 NULL이라는 알 수 없는 결과가 됩니다. NULL 여부는 IS NULL로 확인하고 = NULL을 사용하지 않습니다. NULL 값과 비교 연산의 공식 설명에서 특별 처리되는 타입과 SQL 모드의 예외를 확인할 수 있습니다.
입력 조건은 서로 다른 문제를 해결하기
| 정의 | 하는 일 | 하지 않는 일 |
|---|---|---|
| DEFAULT 0 | 열을 생략하거나 DEFAULT를 지정할 때 0 사용 | 입력된 모든 NULL을 자동으로 0으로 바꾸는 규칙은 아님 |
| NOT NULL | 명시적인 NULL 입력을 제한 | 빈 문자열·0·음수를 모두 금지하는 것은 아님 |
| CHECK (stock >= 0) | 음수 재고를 거절 | nullable 열의 NULL까지 거절하는 것은 아님 |
| NOT NULL + CHECK | 값의 존재와 값의 조건을 함께 검사 | 사용자 화면의 안내와 데이터 검토를 대신하지 않음 |
기본값은 모르는 정보를 임의로 만드는 값으로 쓰지 않습니다. 재고가 아직 확인되지 않은 상태를 0으로 저장하면 실제 품절과 구별되지 않습니다. 이 예제에서는 새 상품의 초기 재고를 0으로 정한다는 정책을 선택한 것이며, 다른 업무에서는 NULL 또는 별도의 확인 상태가 더 적절할 수 있습니다.
입력 검사에는 데이터베이스 제약 조건과 애플리케이션 검사를 함께 사용합니다. 화면에서만 검사하면 다른 프로그램이나 SQL 파일을 통해 규칙을 어기는 데이터가 들어올 수 있습니다. 제약 조건은 최종 저장 지점에서 일관된 규칙을 적용하는 역할입니다.
기본값·NULL·CHECK를 한 테이블에서 실습하기
2~5강에서 준비한 course_user로 shopcourse 데이터베이스에 접속한 상태에서 실행합니다. 접속·SQL 파일 실행 방법은 명령줄 클라이언트 설명을 참고하세요. 서버 버전과 선택한 데이터베이스를 먼저 확인하고, 운영 데이터가 들어 있는 연결에서는 이 실습을 실행하지 않습니다.
SELECT VERSION() AS server_version, DATABASE() AS selected_database; USE shopcourse; SELECT DATABASE() AS selected_database;
정상 실행 파일은 조건을 만족하는 행만 넣습니다. 현재 연결의 SQL 모드와 CHECK 검사 설정을 변수에 보관하고 strict 모드·검사 ON 상태를 명시합니다. 오래된 서버에서는 CHECK 지원·강제 여부가 다를 수 있으므로 실제 버전의 문서와 검사 설정을 먼저 확인하세요.
USE shopcourse;
SET @saved_sql_mode = @@SESSION.sql_mode;
SET @saved_check_constraints = @@SESSION.check_constraint_checks;
SET SESSION sql_mode = 'STRICT_TRANS_TABLES';
SET SESSION check_constraint_checks = ON;
CREATE TABLE validation_lab (
item_id INT PRIMARY KEY,
item_name VARCHAR(100) NOT NULL,
note VARCHAR(100) NULL DEFAULT NULL,
stock INT NOT NULL DEFAULT 0,
CONSTRAINT ck_validation_stock CHECK (stock >= 0)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
INSERT INTO validation_lab (item_id, item_name, note) VALUES
(1, '노트', NULL),
(2, '머그컵', '');
SELECT item_id, note IS NULL AS note_is_null,
note = '' AS note_is_empty, stock
FROM validation_lab ORDER BY item_id;
CREATE TABLE nullable_check_lab (
item_id INT PRIMARY KEY,
stock INT NULL,
CONSTRAINT ck_nullable_stock CHECK (stock >= 0)
) ENGINE=InnoDB;
INSERT INTO nullable_check_lab VALUES (1, NULL);
SELECT item_id, stock IS NULL AS stock_is_null FROM nullable_check_lab;
| 행 | note_is_null | note_is_empty | stock |
|---|---|---|---|
| 1 | 1 | NULL | 0 |
| 2 | 0 | 1 | 0 |
note가 NULL인 첫 행에서 note = 빈 문자열의 결과도 NULL입니다. 둘째 행은 값 자체가 빈 문자열이므로 IS NULL은 0, 빈 문자열 비교는 1입니다. 두 행 모두 stock을 입력하지 않았으므로 DEFAULT 0이 적용됩니다.
추가 실습 테이블 nullable_check_lab에는 stock에 NULL을 넣었는데 행이 저장됩니다. CHECK(stock >= 0)의 결과가 NULL일 때 거짓으로 판정되는 것은 아니기 때문입니다. 재고 입력을 반드시 받으려면 CHECK만 붙이지 말고 stock NOT NULL도 함께 선언해야 합니다.
NOT NULL과 CHECK 오류를 따로 확인하기
같은 연결에서 아래 문장을 하나씩 실행합니다. 첫 문장은 필수 이름에 NULL을 넣어 Column cannot be null 오류가 예상됩니다. 둘째 문장은 이름은 있지만 재고가 음수여서 CHECK 제약 이름을 포함한 오류가 예상됩니다. 의도한 실패를 정상 파일 중간에 섞지 않습니다.
INSERT INTO validation_lab (item_id, item_name) VALUES (3, NULL);
INSERT INTO validation_lab (item_id, item_name, stock) VALUES (3, '책상 조명', -1);
각 오류 뒤 SELECT COUNT(*) FROM validation_lab;를 실행하면 정상 행 두 개가 유지되어야 합니다. 실패 문장의 3번 번호는 아직 저장되지 않았으므로 두 오류를 같은 번호로 시험할 수 있습니다. 오류 메시지의 세부 문구는 버전과 출력 언어에 따라 달라도 어떤 열·제약을 위반했는지는 확인합니다.
입력된 NULL을 허용할지, 거절할지, 기본값으로 대체할지는 선언과 입력 방식에 따라 달라집니다. 특히 AUTO_INCREMENT나 TIMESTAMP에는 특별한 NULL 처리 규칙이 있을 수 있으므로, 이 일반 메모·재고 예제를 모든 타입에 그대로 일반화하지 않습니다.
빈 값 검사와 제약 이름·설정 확인하기
NOT NULL인 문자열에도 빈 문자열은 들어갈 수 있습니다. 관련 조건은 문자열 저장과 길이에서 구체적으로 설명합니다. 이름에 일반 공백만 있는 경우까지 금지하려면 NOT NULL과 함께 CHECK(CHAR_LENGTH(TRIM(item_name)) > 0) 같은 조건을 설계할 수 있습니다. 다만 TRIM의 기본 처리가 모든 유니코드 공백이나 화면상의 빈 글자를 제거한다고 가정하지 않습니다.
SHOW CREATE TABLE validation_lab; SELECT @@SESSION.sql_mode, @@SESSION.check_constraint_checks;
이름이 붙은 ck_validation_stock은 어떤 규칙에서 실패했는지 확인할 때 도움이 됩니다. 제약 이름은 독자가 유지보수할 수 있도록 테이블·열·조건을 드러내고, 여러 서버 버전에서 허용되는 이름 범위 차이도 고려합니다. MariaDB 제약 조건 공식 문서는 CHECK 문법과 검사 설정을 설명합니다.
check_constraint_checks를 OFF로 바꾸면 CHECK 검사가 적용되지 않을 수 있습니다. 운영 데이터의 오류를 빨리 통과시키려고 검사를 끄지 말고 원래 조건과 입력값을 수정하세요. 검사 ON 설정만으로 과거에 잘못 들어간 모든 행이 자동 정리되는 것도 아닙니다.
설정 복원과 안전한 반복 실습
같은 연결에서 원래 설정을 복원하고, 보관할 데이터가 없는 실습 테이블만 삭제해 반복합니다. 새로운 연결에서는 사용자 변수가 사라졌으므로 저장 변수로 복원하지 말고 현재 설정을 조회합니다.
SET SESSION check_constraint_checks = @saved_check_constraints; SET SESSION sql_mode = @saved_sql_mode;
반복 실습 전에는 SELECT DATABASE();와 SHOW CREATE TABLE 테이블명;으로 대상이 이 강좌에서 만든 실습 테이블인지 확인합니다. 보관할 데이터가 있다면 먼저 백업합니다. 아래 명령은 지정한 테이블과 그 안의 데이터를 삭제하므로 확인한 학습용 테이블에만 실행하세요.
DROP TABLE shopcourse.validation_lab, shopcourse.nullable_check_lab;
삭제 후에는 본문의 전체 예제를 다시 실행할 수 있습니다. DROP TABLE 같은 데이터 정의 문장은 일반적인 트랜잭션의 ROLLBACK으로 복구할 수 있다고 기대하지 않습니다. 기존 products 테이블은 이 초기화 대상에 포함하지 않습니다.









