포스트

[Python 100일 챌린지] Day 73 - SQL 기본 (CREATE, INSERT)

[Python 100일 챌린지] Day 73 - SQL 기본 (CREATE, INSERT)

데이터베이스의 시작은 테이블 만들기부터! SQL의 CREATE와 INSERT는 데이터베이스 작업의 가장 기본이에요. 테이블 구조를 설계하고 데이터를 추가하는 방법을 배우면, 여러분만의 데이터베이스를 만들고 관리할 수 있습니다. 오늘은 Python의 sqlite3로 실제 데이터베이스를 만들어보며 SQL의 기초를 다져봅시다! 😊

(25분 완독 ⭐⭐)

🎯 오늘의 학습 목표

📚 사전 지식


🎯 학습 목표 1: CREATE TABLE로 테이블 만들기

한 줄 설명

CREATE TABLE = 데이터를 저장할 표 만들기

엑셀의 시트처럼 행(row)과 열(column)로 구성된 테이블을 생성합니다.

기본 문법

1
2
3
4
5
CREATE TABLE 테이블명 (
    컬럼명1 데이터타입,
    컬럼명2 데이터타입,
    컬럼명3 데이터타입
);

첫 번째 테이블 만들기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import sqlite3

# 데이터베이스 연결
conn = sqlite3.connect('mydb.db')
cursor = conn.cursor()

# 학생 테이블 생성
cursor.execute('''
    CREATE TABLE students (
        id INTEGER,
        name TEXT,
        age INTEGER,
        grade TEXT
    )
''')

conn.commit()
conn.close()

print("테이블 생성 완료!")

테이블 구조:

1
2
3
4
5
6
students 테이블
┌────┬──────┬─────┬───────┐
│ id │ name │ age │ grade │
├────┼──────┼─────┼───────┤
│    │      │     │       │ ← 데이터가 들어갈 공간
└────┴──────┴─────┴───────┘

테이블 존재 여부 확인

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import sqlite3

conn = sqlite3.connect('mydb.db')
cursor = conn.cursor()

# 테이블이 없을 때만 생성
cursor.execute('''
    CREATE TABLE IF NOT EXISTS students (
        id INTEGER,
        name TEXT,
        age INTEGER,
        grade TEXT
    )
''')

conn.commit()
conn.close()

💡 IF NOT EXISTS를 사용하면 이미 테이블이 있을 때 에러가 발생하지 않습니다!

🎯 학습 목표 2: SQL 데이터 타입 이해하기

SQLite의 주요 데이터 타입

SQLite는 5가지 주요 데이터 타입을 지원합니다:

1. INTEGER (정수)

1
2
3
4
5
CREATE TABLE users (
    id INTEGER,           # 1, 2, 3, 100, -50
    age INTEGER,
    score INTEGER
)

용도: 나이, 점수, 개수, ID 등

2. TEXT (문자열)

1
2
3
4
5
CREATE TABLE users (
    name TEXT,           # "홍길동", "Kim"
    email TEXT,          # "[email protected]"
    address TEXT
)

용도: 이름, 이메일, 주소, 설명 등

3. REAL (실수)

1
2
3
4
5
CREATE TABLE products (
    price REAL,          # 19.99, 3.14
    weight REAL,         # 1.5, 0.25
    rating REAL          # 4.5
)

용도: 가격, 무게, 평점, 온도 등

4. BLOB (바이너리 데이터)

1
2
3
CREATE TABLE files (
    file_data BLOB       # 이미지, 파일 등
)

용도: 이미지, 오디오, 파일 등 바이너리 데이터

5. NULL (값 없음)

1
2
# 값이 없는 상태
age = NULL  # 나이 정보 없음

실전 예제: 다양한 타입 사용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import sqlite3

conn = sqlite3.connect('shop.db')
cursor = conn.cursor()

cursor.execute('''
    CREATE TABLE IF NOT EXISTS products (
        id INTEGER,
        name TEXT,
        price REAL,
        stock INTEGER,
        description TEXT,
        image BLOB
    )
''')

conn.commit()
conn.close()

print("상품 테이블 생성 완료!")

🎯 학습 목표 3: 제약조건으로 데이터 품질 보장하기

PRIMARY KEY (기본 키)

역할: 각 행을 고유하게 식별하는 컬럼

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import sqlite3

conn = sqlite3.connect('mydb.db')
cursor = conn.cursor()

cursor.execute('''
    CREATE TABLE students (
        id INTEGER PRIMARY KEY,  # 중복 불가, NULL 불가
        name TEXT,
        age INTEGER
    )
''')

conn.commit()
conn.close()

특징:

  • 중복된 값 불가
  • NULL 값 불가
  • 자동으로 인덱스 생성 (검색 빠름)

AUTOINCREMENT (자동 증가)

1
2
3
4
5
6
7
cursor.execute('''
    CREATE TABLE students (
        id INTEGER PRIMARY KEY AUTOINCREMENT,  # 1, 2, 3... 자동 증가
        name TEXT,
        age INTEGER
    )
''')

장점:

  • ID를 직접 지정하지 않아도 자동으로 1씩 증가
  • 삭제된 ID 재사용 안 함

NOT NULL (필수 값)

1
2
3
4
5
6
7
8
cursor.execute('''
    CREATE TABLE students (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,      # 이름 필수
        age INTEGER NOT NULL,    # 나이 필수
        phone TEXT               # 선택 (NULL 가능)
    )
''')

DEFAULT (기본값)

1
2
3
4
5
6
7
8
9
cursor.execute('''
    CREATE TABLE posts (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        title TEXT NOT NULL,
        content TEXT,
        views INTEGER DEFAULT 0,           # 기본값 0
        created_at TEXT DEFAULT CURRENT_TIMESTAMP  # 현재 시간
    )
''')

UNIQUE (고유값)

1
2
3
4
5
6
7
8
cursor.execute('''
    CREATE TABLE users (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        username TEXT UNIQUE,    # 중복 불가
        email TEXT UNIQUE,       # 중복 불가
        name TEXT
    )
''')

종합 예제: 모든 제약조건 활용

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import sqlite3

conn = sqlite3.connect('company.db')
cursor = conn.cursor()

cursor.execute('''
    CREATE TABLE IF NOT EXISTS employees (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        employee_id TEXT UNIQUE NOT NULL,
        name TEXT NOT NULL,
        email TEXT UNIQUE NOT NULL,
        department TEXT NOT NULL,
        salary REAL DEFAULT 0,
        hire_date TEXT DEFAULT CURRENT_TIMESTAMP,
        is_active INTEGER DEFAULT 1
    )
''')

conn.commit()
conn.close()

print("직원 테이블 생성 완료!")

🎯 학습 목표 4: INSERT로 데이터 추가하기

기본 INSERT 문법

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import sqlite3

conn = sqlite3.connect('mydb.db')
cursor = conn.cursor()

# 단일 행 삽입
cursor.execute('''
    INSERT INTO students (id, name, age, grade)
    VALUES (1, '홍길동', 20, 'A')
''')

conn.commit()
conn.close()

print("데이터 추가 완료!")

컬럼 순서와 값 순서 일치

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 방법 1: 모든 컬럼 명시
cursor.execute('''
    INSERT INTO students (id, name, age, grade)
    VALUES (2, '김철수', 22, 'B')
''')

# 방법 2: 일부 컬럼만 (나머지는 NULL 또는 DEFAULT)
cursor.execute('''
    INSERT INTO students (name, age)
    VALUES ('이영희', 21)
''')

# 방법 3: 모든 컬럼 순서대로 (컬럼명 생략)
cursor.execute('''
    INSERT INTO students
    VALUES (3, '박민수', 23, 'A')
''')

여러 행 한 번에 삽입

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import sqlite3

conn = sqlite3.connect('mydb.db')
cursor = conn.cursor()

# 여러 데이터 준비
students_data = [
    (4, '정수진', 20, 'A'),
    (5, '최동욱', 22, 'B'),
    (6, '강지혜', 21, 'A'),
    (7, '윤서연', 23, 'C')
]

# executemany로 한 번에 삽입
cursor.executemany('''
    INSERT INTO students (id, name, age, grade)
    VALUES (?, ?, ?, ?)
''', students_data)

conn.commit()
print(f"{len(students_data)}개 데이터 추가 완료!")
conn.close()

출력:

1
4개 데이터 추가 완료!

Python 변수 사용하여 삽입

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import sqlite3

conn = sqlite3.connect('mydb.db')
cursor = conn.cursor()

# 변수로 데이터 준비
name = "안유진"
age = 24
grade = "A"

# ? 플레이스홀더 사용 (SQL Injection 방지)
cursor.execute('''
    INSERT INTO students (name, age, grade)
    VALUES (?, ?, ?)
''', (name, age, grade))

conn.commit()
conn.close()

print(f"{name} 학생 정보 추가 완료!")

⚠️ 보안 주의: 항상 ? 플레이스홀더를 사용하세요! 문자열 포맷팅(f-string, %)은 SQL Injection 공격에 취약합니다.

실전 예제: 상품 데이터 추가

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import sqlite3

conn = sqlite3.connect('shop.db')
cursor = conn.cursor()

# 테이블 생성
cursor.execute('''
    CREATE TABLE IF NOT EXISTS products (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        price REAL NOT NULL,
        stock INTEGER DEFAULT 0,
        category TEXT
    )
''')

# 상품 데이터
products = [
    ('노트북', 1200000, 15, '전자제품'),
    ('마우스', 35000, 50, '전자제품'),
    ('키보드', 89000, 30, '전자제품'),
    ('모니터', 250000, 20, '전자제품'),
    ('의자', 150000, 10, '가구')
]

# 데이터 삽입
cursor.executemany('''
    INSERT INTO products (name, price, stock, category)
    VALUES (?, ?, ?, ?)
''', products)

conn.commit()
print(f"{len(products)}개 상품 추가 완료!")

# 확인
cursor.execute('SELECT * FROM products')
rows = cursor.fetchall()

print("\n등록된 상품 목록:")
for row in rows:
    print(f"ID: {row[0]}, 상품명: {row[1]}, 가격: {row[2]:,}원, 재고: {row[3]}")

conn.close()

출력:

1
2
3
4
5
6
7
8
5개 상품 추가 완료!

등록된 상품 목록:
ID: 1, 상품명: 노트북, 가격: 1,200,000원, 재고: 15개
ID: 2, 상품명: 마우스, 가격: 35,000원, 재고: 50개
ID: 3, 상품명: 키보드, 가격: 89,000원, 재고: 30개
ID: 4, 상품명: 모니터, 가격: 250,000원, 재고: 20개
ID: 5, 상품명: 의자, 가격: 150,000원, 재고: 10개

💡 실전 팁 & 주의사항

💡 Tip 1: 테이블 설계는 신중하게

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# ❌ 나쁜 예 - 컬럼이 너무 많음
CREATE TABLE users (
    id, name, age, email, phone, address, city, country,
    zipcode, job, company, department, salary, ...
)

# ✅ 좋은 예 - 테이블 분리
CREATE TABLE users (
    id, name, email, phone
)

CREATE TABLE user_profiles (
    user_id, address, city, country, zipcode
)

CREATE TABLE user_employment (
    user_id, job, company, department, salary
)

💡 Tip 2: PRIMARY KEY는 항상 설정

1
2
3
4
5
6
7
8
9
10
11
12
# ❌ PRIMARY KEY 없음
CREATE TABLE users (
    name TEXT,
    email TEXT
)  # 중복 데이터 구분 불가!

# ✅ PRIMARY KEY 설정
CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT,
    email TEXT
)

💡 Tip 3: 필수 데이터는 NOT NULL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# ❌ 필수 정보가 NULL 가능
CREATE TABLE orders (
    id INTEGER PRIMARY KEY,
    product_name TEXT,      # NULL 가능
    customer_email TEXT     # NULL 가능
)

# ✅ 필수 정보는 NOT NULL
CREATE TABLE orders (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    product_name TEXT NOT NULL,
    customer_email TEXT NOT NULL,
    delivery_address TEXT  # 선택 사항
)

💡 Tip 4: commit() 잊지 않기

1
2
3
4
5
6
7
8
# ❌ commit() 없음 - 데이터 저장 안 됨!
cursor.execute('INSERT INTO users VALUES (1, "홍길동")')
conn.close()

# ✅ commit() 필수!
cursor.execute('INSERT INTO users VALUES (1, "홍길동")')
conn.commit()  # 반드시 커밋!
conn.close()

🧪 연습 문제

문제 1: 도서 관리 시스템 테이블 생성

과제: 도서관의 책 정보를 관리할 테이블을 만들고 데이터를 추가하세요.

요구사항:

  1. books 테이블 생성
    • id: 자동 증가 기본 키
    • title: 책 제목 (필수)
    • author: 저자 (필수)
    • isbn: ISBN (고유값, 필수)
    • price: 가격 (실수형)
    • stock: 재고 (기본값 0)
    • published_date: 출판일
  2. 다음 도서 데이터 추가:
    • (“파이썬 정복”, “홍길동”, “979-11-1234-567-8”, 25000, 10, “2024-01-15”)
    • (“데이터베이스 기초”, “김철수”, “979-11-1234-568-5”, 30000, 5, “2024-02-20”)
    • (“웹 개발 완벽 가이드”, “이영희”, “979-11-1234-569-2”, 35000, 8, “2024-03-10”)
  3. 전체 도서 목록 출력
💡 힌트
  • PRIMARY KEY AUTOINCREMENT로 id 자동 생성
  • NOT NULL로 필수 컬럼 지정
  • UNIQUE로 ISBN 중복 방지
  • DEFAULT로 재고 기본값 설정
  • executemany()로 여러 행 한 번에 삽입
✅ 정답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import sqlite3

# 데이터베이스 연결
conn = sqlite3.connect('library.db')
cursor = conn.cursor()

# 1. 테이블 생성
cursor.execute('''
    CREATE TABLE IF NOT EXISTS books (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        title TEXT NOT NULL,
        author TEXT NOT NULL,
        isbn TEXT UNIQUE NOT NULL,
        price REAL,
        stock INTEGER DEFAULT 0,
        published_date TEXT
    )
''')

print("도서 테이블 생성 완료!")

# 2. 도서 데이터 추가
books_data = [
    ("파이썬 정복", "홍길동", "979-11-1234-567-8", 25000, 10, "2024-01-15"),
    ("데이터베이스 기초", "김철수", "979-11-1234-568-5", 30000, 5, "2024-02-20"),
    ("웹 개발 완벽 가이드", "이영희", "979-11-1234-569-2", 35000, 8, "2024-03-10")
]

cursor.executemany('''
    INSERT INTO books (title, author, isbn, price, stock, published_date)
    VALUES (?, ?, ?, ?, ?, ?)
''', books_data)

conn.commit()
print(f"\n{len(books_data)}권의 도서 추가 완료!")

# 3. 전체 도서 목록 출력
cursor.execute('SELECT * FROM books')
books = cursor.fetchall()

print("\n=== 도서 목록 ===")
print(f"{'ID':<5} {'제목':<20} {'저자':<10} {'가격':<10} {'재고':<5}")
print("-" * 60)

for book in books:
    book_id, title, author, isbn, price, stock, pub_date = book
    print(f"{book_id:<5} {title:<20} {author:<10} {price:>8,}{stock:>3}")

print(f"\n{len(books)}권의 도서가 등록되어 있습니다.")

conn.close()

출력:

1
2
3
4
5
6
7
8
9
10
11
12
도서 테이블 생성 완료!

3권의 도서 추가 완료!

=== 도서 목록 ===
ID    제목                   저자         가격        재고
------------------------------------------------------------
1     파이썬 정복             홍길동        25,000원  10권
2     데이터베이스 기초        김철수        30,000원   5권
3     웹 개발 완벽 가이드      이영희        35,000원   8권

총 3권의 도서가 등록되어 있습니다.

문제 2: 회원 가입 시스템

과제: 사용자 회원 정보를 저장하는 테이블을 만들고 회원 가입 기능을 구현하세요.

요구사항:

  1. members 테이블 생성
    • id: 자동 증가 기본 키
    • username: 사용자명 (고유값, 필수)
    • email: 이메일 (고유값, 필수)
    • password: 비밀번호 (필수)
    • join_date: 가입일 (자동으로 현재 시간)
    • is_active: 활성 상태 (기본값 1)
  2. 회원 가입 함수 작성:
    • 사용자 입력받기 (username, email, password)
    • 데이터 검증 (빈 값 체크)
    • DB에 저장
    • 성공/실패 메시지 출력
  3. 3명 이상 회원 가입 후 전체 회원 목록 출력
💡 힌트
  • UNIQUE로 username과 email 중복 방지
  • DEFAULT CURRENT_TIMESTAMP로 가입일 자동 설정
  • try-except로 중복 회원 에러 처리
  • 입력값 검증: if not username or not email
✅ 정답
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import sqlite3

def create_members_table():
    """회원 테이블 생성"""
    conn = sqlite3.connect('users.db')
    cursor = conn.cursor()

    cursor.execute('''
        CREATE TABLE IF NOT EXISTS members (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            username TEXT UNIQUE NOT NULL,
            email TEXT UNIQUE NOT NULL,
            password TEXT NOT NULL,
            join_date TEXT DEFAULT CURRENT_TIMESTAMP,
            is_active INTEGER DEFAULT 1
        )
    ''')

    conn.commit()
    conn.close()
    print("회원 테이블 생성 완료!\n")

def register_member(username, email, password):
    """회원 가입 함수"""
    # 입력값 검증
    if not username or not email or not password:
        print("❌ 모든 정보를 입력해주세요!")
        return False

    try:
        conn = sqlite3.connect('users.db')
        cursor = conn.cursor()

        cursor.execute('''
            INSERT INTO members (username, email, password)
            VALUES (?, ?, ?)
        ''', (username, email, password))

        conn.commit()
        conn.close()

        print(f"{username}님, 회원가입 완료!")
        return True

    except sqlite3.IntegrityError:
        print(f"❌ 이미 사용 중인 사용자명 또는 이메일입니다!")
        return False

def show_all_members():
    """전체 회원 목록 출력"""
    conn = sqlite3.connect('users.db')
    cursor = conn.cursor()

    cursor.execute('SELECT id, username, email, join_date, is_active FROM members')
    members = cursor.fetchall()

    print("\n=== 전체 회원 목록 ===")
    print(f"{'ID':<5} {'사용자명':<15} {'이메일':<25} {'가입일':<20} {'상태':<5}")
    print("-" * 80)

    for member in members:
        member_id, username, email, join_date, is_active = member
        status = "활성" if is_active else "비활성"
        print(f"{member_id:<5} {username:<15} {email:<25} {join_date:<20} {status:<5}")

    print(f"\n{len(members)}명의 회원이 가입했습니다.")

    conn.close()

# 메인 실행
if __name__ == "__main__":
    # 1. 테이블 생성
    create_members_table()

    # 2. 회원 가입
    print("=== 회원 가입 ===")
    register_member("hong_gil_dong", "[email protected]", "pass1234")
    register_member("kim_cheol_su", "[email protected]", "secure5678")
    register_member("lee_young_hee", "[email protected]", "mypass9012")

    # 중복 회원 테스트
    print("\n=== 중복 회원 가입 시도 ===")
    register_member("hong_gil_dong", "[email protected]", "pass0000")

    # 3. 전체 회원 목록 출력
    show_all_members()

출력:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
회원 테이블 생성 완료!

=== 회원 가입 ===
✅ hong_gil_dong님, 회원가입 완료!
✅ kim_cheol_su님, 회원가입 완료!
✅ lee_young_hee님, 회원가입 완료!

=== 중복 회원 가입 시도 ===
❌ 이미 사용 중인 사용자명 또는 이메일입니다!

=== 전체 회원 목록 ===
ID    사용자명          이메일                      가입일                 상태
--------------------------------------------------------------------------------
1     hong_gil_dong   [email protected]          2025-05-12 09:15:30   활성
2     kim_cheol_su    [email protected]           2025-05-12 09:15:30   활성
3     lee_young_hee   [email protected]           2025-05-12 09:15:30   활성

총 3명의 회원이 가입했습니다.

📝 오늘 배운 내용 정리

  1. CREATE TABLE: 테이블 구조 정의하고 생성
  2. 데이터 타입: INTEGER, TEXT, REAL, BLOB, NULL
  3. 제약조건: PRIMARY KEY, AUTOINCREMENT, NOT NULL, DEFAULT, UNIQUE
  4. INSERT: 데이터 추가 (단일/다중)
  5. 보안: ? 플레이스홀더로 SQL Injection 방지
  6. 트랜잭션: commit()으로 변경사항 저장

🔗 관련 자료


📚 이전 학습

Day 72: SQLite 시작하기 ⭐⭐

어제는 SQLite 데이터베이스 연결과 기본 사용법을 배웠습니다!

📚 다음 학습

Day 74: SQL 기본 (SELECT, WHERE) ⭐⭐

내일은 데이터를 조회하고 필터링하는 SELECT와 WHERE를 배웁니다!


“늦었다고 생각할 때가 가장 빠른 시기입니다!” 🚀

Day 73/100 Phase 8: 데이터베이스와 SQL #100DaysOfPython
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.