MySQL CREATE TABLE 연습 문제 20선

핵심 요약

  • MySQL CREATE TABLE 연습 문제는 “직접 설계 → 타이핑 → 에러 해결”을 반복하며 SQL 근육을 만드는 드릴입니다.
  • 이 글은 MySQL 8.4 LTS 기준으로 테스트했으며, MySQL 9.x(Innovation)에서도 대부분 동일하게 동작합니다.
  • 기초 테이블 생성부터 제약 조건, 외래키, 복합키까지 단계별로 난이도가 올라갑니다.
  • FK 에러의 대표 원인은 “부모/자식 컬럼 정의 불일치(특히 UNSIGNED, 길이, collation 등)”입니다.

초보자도 따라 할 수 있는 단계별 SQL CREATE TABLE 드릴 (Drill)

이론은 이해했는데 막상 하얀 에디터 창을 보면 막막하신가요? 오늘 소개할 MySQL CREATE TABLE 연습 문제는 여러분의 지식을 손가락 끝의 ‘SQL 근육’으로 만들어 드립니다.

데이터베이스 학습의 시작은 테이블을 만드는 DDL에서 출발합니다. 문법을 외우는 것과, 원하는 구조를 스스로 설계하는 것은 완전히 다른 영역입니다. 오늘은 MySQL 8.4 LTS 기준으로 기초부터 외래키, 복합키까지 다루는 SQL 실습 문제 20선을 직접 타이핑하며 풀어봅니다.

목차

섹션 1: 워밍업 – 코딩 전 필수 체크리스트

본격적인 SQL CREATE TABLE 드릴을 시작하기 전에, 실수하기 쉬운 기본 문법과 데이터 타입 전략을 점검해봅시다. 집을 짓기 전에 설계도와 자재를 확인하는 과정과 같습니다.

0) 실습 환경 추천 (처음 1번만)

연습용 DB를 따로 만들어두면 실수해도 안전합니다. (utf8mb4 기본 세팅)

CREATE DATABASE IF NOT EXISTS sql_drill
  DEFAULT CHARACTER SET utf8mb4;

USE sql_drill;

1) MySQL 버전 확인 (권장)

글의 예시는 MySQL 8.4 LTS 기준으로 테스트했습니다.

SELECT VERSION();

2) 기본 구문 구조도

CREATE TABLE 테이블명 (
  컬럼명 데이터타입 제약조건,
  컬럼명 데이터타입 제약조건,
  ...
  PRIMARY KEY (기본키_컬럼명)
)

3) 2026년 기준 “자주 쓰는 타입” 3대장

데이터 타입 용도 및 특징 실무 팁
BIGINT ID(식별자), 큰 숫자 서비스가 커질 가능성이 있으면 PK를 BIGINT로 시작하는 편이 안전합니다.
VARCHAR(n) 이름, 이메일 등 가변 길이 문자열 고정 길이 CHAR보다 일반적으로 저장 효율이 좋습니다. (고정 길이 데이터는 CHAR도 고려)
DATETIME 날짜+시간 미래 날짜까지 길게 보관해야 하면 DATETIME이 편합니다.

4) 네이밍 컨벤션 (Naming Convention)

MySQL에서는 snake_case가 널리 쓰입니다.

  • Bad: UserEmail, userId
  • Good: user_email, user_id

백틱(Backtick) 팁
만약 컬럼명/테이블명이 예약어(order, group, rank 등)와 겹쳐서 에러가 나면, 임시 해결책으로 이름을 백틱(`)으로 감싸보세요. 예: `order`, `group`
(다만 실무에서는 예약어를 피하는 네이밍을 추천합니다.)

섹션 2: [Level 1] 기초 튼튼! 단일 테이블 생성하기 (문제 1~5)

첫 번째 단계는 “단일 테이블”을 문법 에러 없이 만드는 연습입니다. 실무에서 흔한 id(PK) + 나머지 컬럼 구조를 손에 익혀봅시다. (이 레벨은 일부 컬럼을 아직 NOT NULL로 강제하지 않습니다. 다음 레벨에서 본격적으로 다룹니다.)

문제 1: 회원 기본 정보 테이블 (users)

의도: “가장 흔한 사용자 테이블 뼈대(id PK + 문자열 컬럼)”를 손에 익히기

  • 테이블: users
  • 컬럼:
    • id: BIGINT UNSIGNED, NOT NULL, AUTO_INCREMENT, PK
    • username: VARCHAR(50)
    • email: VARCHAR(100)
  • 엔진: InnoDB
CREATE TABLE users (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  username VARCHAR(50),
  email VARCHAR(100),
  PRIMARY KEY (id)
)

문제 2: 상품 정보 테이블 (products)

의도: “TEXT(긴 글) 컬럼을 포함한 테이블”에 익숙해지기

  • 테이블: products
  • 컬럼:
    • id: BIGINT UNSIGNED, NOT NULL, AUTO_INCREMENT, PK
    • name: VARCHAR(100)
    • price: INT
    • description: TEXT
CREATE TABLE products (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  name VARCHAR(100),
  price INT,
  description TEXT,
  PRIMARY KEY (id)
)

문제 3: 게시판 테이블 (posts)

의도: “DATETIME(날짜+시간) 컬럼”을 포함한 테이블 만들기

  • 테이블: posts
  • 컬럼:
    • id: BIGINT UNSIGNED, NOT NULL, AUTO_INCREMENT, PK
    • title: VARCHAR(200)
    • content: TEXT
    • created_at: DATETIME
CREATE TABLE posts (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  title VARCHAR(200),
  content TEXT,
  created_at DATETIME,
  PRIMARY KEY (id)
)

문제 4: 직원 급여 테이블 (salaries)

의도: “DECIMAL(정확한 소수) 타입”을 익히기 (돈/정밀수는 FLOAT보다 DECIMAL 권장)

  • 테이블: salaries
  • 컬럼:
    • id: BIGINT UNSIGNED, NOT NULL, AUTO_INCREMENT, PK
    • employee_name: VARCHAR(50)
    • amount: DECIMAL(10,2)
CREATE TABLE salaries (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  employee_name VARCHAR(50),
  amount DECIMAL(10, 2),
  PRIMARY KEY (id)
)

문제 5: 접속 로그 테이블 (access_logs)

의도: “긴 문자열(VARCHAR 큰 길이) + IPv6 고려(45)” 같은 실무 디테일 연습

  • 테이블: access_logs
  • 컬럼:
    • id: BIGINT UNSIGNED, NOT NULL, AUTO_INCREMENT, PK
    • access_time: DATETIME
    • url_path: VARCHAR(2048)
    • ip_address: VARCHAR(45) (IPv6까지 고려)
CREATE TABLE access_logs (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  access_time DATETIME,
  url_path VARCHAR(2048),
  ip_address VARCHAR(45),
  PRIMARY KEY (id)
)

섹션 3: [Level 2] 무결성을 위한 제약 조건 마스터 (문제 6~12)

이제부터는 “쓰레기 데이터가 들어오는 걸 막는 문지기”를 붙입니다. NOT NULL, DEFAULT, UNIQUE, CHECK 같은 제약 조건은 데이터 품질을 결정합니다.

문제 6: 필수값 관리 (NOT NULL & DEFAULT) – orders

의도: “필수값 + 기본값” 패턴을 몸에 익히기

  • 테이블: orders
  • 규칙: product_name은 필수, status는 기본값 ‘접수대기’
CREATE TABLE orders (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  product_name VARCHAR(100) NOT NULL,
  status VARCHAR(20) NOT NULL DEFAULT '접수대기',
  PRIMARY KEY (id)
)

문제 7: 중복 방지 (UNIQUE) – members

의도: “이메일 중복 가입 방지” 같은 실무 요구를 UNIQUE로 구현하기

  • 테이블: members
  • 규칙: email은 중복 불가(UNIQUE), password는 필수
CREATE TABLE members (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  email VARCHAR(100) NOT NULL,
  password VARCHAR(255) NOT NULL,
  PRIMARY KEY (id),
  UNIQUE KEY uk_members_email (email)
)

문제 8: 자동 증가 (AUTO_INCREMENT) – items

의도: “PK 자동 증가”를 가장 표준적인 형태로 익히기

  • 테이블: items
  • 규칙: id는 자동 증가 PK
CREATE TABLE items (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  item_name VARCHAR(50),
  PRIMARY KEY (id)
)

문제 9: 데이터 검증 (CHECK) – adult_users

의도: “값의 범위를 DB 레벨에서 강제”하는 연습 (CHECK는 8.0.16+에서 실질적으로 동작) :contentReference[oaicite:3]{index=3}

  • 테이블: adult_users
  • 규칙: age는 19 이상만 허용
CREATE TABLE adult_users (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  username VARCHAR(50) NOT NULL,
  age INT NOT NULL,
  PRIMARY KEY (id),
  CONSTRAINT chk_adult_users_age CHECK (age >= 19)
)

문제 10: 복합 제약 (NOT NULL + UNIQUE) – inventory

의도: “상품 코드(sku)는 필수 + 중복 불가” 같은 실무 무결성 연습

  • 테이블: inventory
  • 규칙: sku_code는 NOT NULL + UNIQUE, quantity는 기본값 0
CREATE TABLE inventory (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  sku_code VARCHAR(20) NOT NULL,
  quantity INT NOT NULL DEFAULT 0,
  PRIMARY KEY (id),
  UNIQUE KEY uk_inventory_sku (sku_code)
)

문제 11: 기본키 설정 습관 – 복합키를 위한 준비운동 (students)

의도: PK를 “마지막 줄(테이블 레벨)로 빼는 습관”을 고정하기
이렇게 해두면 나중에 복합키(문제 19처럼 2개 컬럼 PK)로 확장할 때 사고가 덜 납니다.

  • 테이블: students
  • 규칙: id는 AUTO_INCREMENT PK, name은 필수
CREATE TABLE students (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  name VARCHAR(50) NOT NULL,
  PRIMARY KEY (id)
)

문제 12: ENUM 대체 (CHECK로 상태 제한) – users_status

의도: “허용 가능한 값의 집합”을 CHECK로 강제하기

  • 테이블: users_status
  • 규칙: is_active는 ‘Y’ 또는 ‘N’만 허용
  • 포인트: CHAR(1) 같은 고정 길이는 CHAR가 자연스러운 케이스
CREATE TABLE users_status (
  user_id BIGINT UNSIGNED NOT NULL,
  is_active CHAR(1) NOT NULL,
  PRIMARY KEY (user_id),
  CONSTRAINT chk_users_status_is_active CHECK (is_active IN ('Y', 'N'))
)

섹션 4: [Level 3] 관계 설정하기 – 외래키(FK) 심화 (문제 13~17)

⚠️ 절대 주의: 부모가 UNSIGNED면 자식도 UNSIGNED!
FK는 “양쪽 컬럼 정의가 완전히 동일”해야 안정적으로 붙습니다.
예: 부모 PK가 BIGINT UNSIGNED인데 자식 FK를 BIGINT로 만들면 실패할 수 있습니다.

문제 13: 1:N 관계 기초 (departments → employees)

의도: “부서 1개에 직원 여러 명” 같은 전형적인 1:N 모델 만들기

  • 부모 테이블: departments (PK: id)
  • 자식 테이블: employees (FK: dept_id → departments.id)
CREATE TABLE departments (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  dept_name VARCHAR(50) NOT NULL,
  PRIMARY KEY (id)
) 

CREATE TABLE employees (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  name VARCHAR(50) NOT NULL,
  dept_id BIGINT UNSIGNED,
  PRIMARY KEY (id),
  CONSTRAINT fk_employees_dept
    FOREIGN KEY (dept_id) REFERENCES departments(id)
)

문제 14: 삭제 연쇄 (ON DELETE CASCADE) – customers → customer_orders

의도: “부모 삭제 시 자식도 같이 정리”되는 패턴 익히기

  • 규칙: 고객이 삭제되면 해당 고객의 주문도 자동 삭제
CREATE TABLE customers (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  name VARCHAR(50) NOT NULL,
  PRIMARY KEY (id)
) 

CREATE TABLE customer_orders (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  customer_id BIGINT UNSIGNED NOT NULL,
  order_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  CONSTRAINT fk_customer_orders_customer
    FOREIGN KEY (customer_id) REFERENCES customers(id)
    ON DELETE CASCADE
)

문제 15: 카테고리 삭제 시 상품은 남기기 (ON DELETE SET NULL)

의도: “부모가 없어져도 자식 데이터는 유지 + 참조만 끊기” 패턴 익히기

  • 규칙: 카테고리가 삭제되면 상품은 남되, category_id만 NULL 처리
CREATE TABLE categories (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  name VARCHAR(50) NOT NULL,
  PRIMARY KEY (id)
) 

CREATE TABLE catalog_products (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  name VARCHAR(50) NOT NULL,
  category_id BIGINT UNSIGNED NULL,
  PRIMARY KEY (id),
  CONSTRAINT fk_catalog_products_category
    FOREIGN KEY (category_id) REFERENCES categories(id)
    ON DELETE SET NULL
)

문제 16: 1:1 관계 구현 (users ↔ user_details)

의도: “유저 1명당 상세정보 1개”를 PK=FK로 구현하는 패턴 익히기

  • 규칙: user_details.user_id는 PK이자 FK (users.id 참조)
CREATE TABLE user_details (
  user_id BIGINT UNSIGNED NOT NULL,
  address VARCHAR(100),
  phone VARCHAR(20),
  PRIMARY KEY (user_id),
  CONSTRAINT fk_user_details_user
    FOREIGN KEY (user_id) REFERENCES users(id)
)

문제 17: 자기 참조 (대댓글) – comments

의도: “부모 댓글을 가리키는 parent_id”로 계층 구조 만들기

  • 규칙: parent_id는 같은 테이블의 id를 참조
  • 주의: 최상위 댓글은 parent_id가 NULL
CREATE TABLE comments (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  content TEXT NOT NULL,
  parent_id BIGINT UNSIGNED NULL,
  PRIMARY KEY (id),
  CONSTRAINT fk_comments_parent
    FOREIGN KEY (parent_id) REFERENCES comments(id)
)

섹션 5: [Level 4] 실전 종합 시나리오 (문제 18~20)

문제 18: N:M 관계 해소 (수강신청) – enrollments

의도: “다대다(N:M)”는 반드시 중간 테이블로 푼다는 감각 익히기

  • 전제: students 테이블(문제 11)이 이미 존재
  • 테이블: classes, enrollments
  • 규칙: 중복 수강 방지를 위해 (student_id, class_id) 복합 PK
CREATE TABLE classes (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  title VARCHAR(100) NOT NULL,
  PRIMARY KEY (id)
) 

CREATE TABLE enrollments (
  student_id BIGINT UNSIGNED NOT NULL,
  class_id BIGINT UNSIGNED NOT NULL,
  enrolled_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (student_id, class_id),
  CONSTRAINT fk_enrollments_student
    FOREIGN KEY (student_id) REFERENCES students(id),
  CONSTRAINT fk_enrollments_class
    FOREIGN KEY (class_id) REFERENCES classes(id)
)

문제 19: 복합 기본키 (좋아요 기능) – post_likes

의도: “유저 1명이 게시글 1개에 좋아요 1번만”을 복합 PK로 강제하기

  • 전제: users(문제 1), posts(문제 3) 존재
  • 규칙: (user_id, post_id) 복합 PK
CREATE TABLE post_likes (
  user_id BIGINT UNSIGNED NOT NULL,
  post_id BIGINT UNSIGNED NOT NULL,
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (user_id, post_id),
  CONSTRAINT fk_post_likes_user
    FOREIGN KEY (user_id) REFERENCES users(id),
  CONSTRAINT fk_post_likes_post
    FOREIGN KEY (post_id) REFERENCES posts(id)
)

문제 20: 미니 프로젝트 – 쇼핑몰 전체 구조

의도: “부모 → 자식 → 중간테이블” 생성 순서 감각을 완성하기

  • 생성 순서: users → products → orders → order_items
  • 핵심 포인트: mini_order_items가 N:M 해소(주문-상품)
CREATE TABLE mini_users (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  username VARCHAR(50) NOT NULL,
  PRIMARY KEY (id)
) 

CREATE TABLE mini_products (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  name VARCHAR(100) NOT NULL,
  price INT NOT NULL,
  PRIMARY KEY (id)
) 

CREATE TABLE mini_orders (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
  user_id BIGINT UNSIGNED NOT NULL,
  order_date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  CONSTRAINT fk_mini_orders_user
    FOREIGN KEY (user_id) REFERENCES mini_users(id)
) 

CREATE TABLE mini_order_items (
  order_id BIGINT UNSIGNED NOT NULL,
  product_id BIGINT UNSIGNED NOT NULL,
  count INT NOT NULL DEFAULT 1,
  PRIMARY KEY (order_id, product_id),
  CONSTRAINT fk_mini_order_items_order
    FOREIGN KEY (order_id) REFERENCES mini_orders(id),
  CONSTRAINT fk_mini_order_items_product
    FOREIGN KEY (product_id) REFERENCES mini_products(id)
)

테이블을 지울 때는 “생성의 역순(자식 → 부모)”
실습을 반복하려면 DROP TABLE도 알아야 합니다.
예를 들어 문제 20의 구조에서 mini_users를 먼저 지우면, 이를 참조하는 mini_orders 때문에 에러가 납니다.
아래처럼 “자식 테이블부터” 지우세요.

DROP TABLE IF EXISTS mini_order_items;
DROP TABLE IF EXISTS mini_orders;
DROP TABLE IF EXISTS mini_products;
DROP TABLE IF EXISTS mini_users;

결론 및 마무리

여러분은 CREATE TABLE 드릴 20문제를 통해 단일 테이블 → 제약 조건 → 외래키 → 복합키까지 테이블 설계의 핵심 패턴을 완주했습니다.

  • FK 에러는 “정의 불일치”를 의심: 타입(UNSIGNED), 길이, NULL 허용 여부 등을 맞추면 해결 확률이 높습니다.
  • 다음 단계: 이제 INSERT로 데이터를 넣고 SELECT로 조회하는 연습으로 넘어가세요.

자주 묻는 질문 (FAQ)

Q: 외래키 생성 시 Error 1215가 발생합니다. 해결 방법은 무엇인가요?

A: (1) 부모 테이블이 먼저 만들어졌는지 확인하세요. (2) 부모 PK와 자식 FK의 정의가 완전히 같은지(UNSIGNED 포함) 확인하세요. (3) InnoDB인지 확인하세요.

Q: 실습을 반복하려면 테이블을 어떻게 지워야 하나요?

A: 생성의 역순(자식 → 부모)으로 DROP TABLE 하세요. 외래키로 물려 있으면 부모부터 지울 수 없습니다.

Q: 예약어 때문에 CREATE TABLE이 실패하는 것 같아요.

A: 예약어(order, group 등)를 이름으로 쓰면 에러가 날 수 있습니다. 가능하면 이름을 바꾸고, 급하면 백틱(`)으로 감싸서 회피할 수 있습니다.

이 글은 어떠셨나요? 자유롭게 의견을 남겨주세요! 💬