1. 목표

더보기
  • 데이터베이스와 테이블의 차이를 구분할 수 있다.
  • MySQL 클라이언트에서 USE, CREATE TABLE, SHOW TABLES, DESCRIBE 기본 명령을 사용할 수 있다.
  • users 테이블 구조를 설계한다.
    id, username, password 컬럼과 타입 의미를 이해한다.
  • PySide6 애플리케이션에서 버튼 클릭으로 users 테이블을 생성하는 코드를 작성하고,
    MySQL 수업에서 사용한 모델링과 비교하여 학습한다.
  • QFile, QTextStream으로 테이블 생성 로그를 남겨 보고, MySQL 클라이언트로 테이블이 생성되었는지 확인한다.
  • 이후 단계인 데이터 INSERT, SELECT에서 이 users 테이블을 재사용할 수 있는 기반을 만든다.

 

2. 전체 로직

더보기

1단계: 연결 테스트

2단계: 데이터베이스 생성

3단계: users 테이블 생성

 

import sys

from PySide6.QtWidgets import (
    QApplication,
    QWidget,
    QLabel,
    QLineEdit,
    QPushButton,
    QVBoxLayout,
    QFormLayout,
    QMessageBox,
    QGroupBox,
)
from PySide6.QtCore import QFile, QIODevice, QTextStream

import mysql.connector as mc


class MySqlDemoWindow(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.setWindowTitle("PySide6 MySQL 연동 기초 데모")
        self.resize(520, 420)

        self.host_edit = QLineEdit()
        self.user_edit = QLineEdit()
        self.password_edit = QLineEdit()
        self.password_edit.setEchoMode(QLineEdit.Password)
        self.db_edit = QLineEdit()

        self.host_edit.setText("localhost")
        self.user_edit.setText("root")

        conn_form = QFormLayout()
        conn_form.addRow("호스트", self.host_edit)
        conn_form.addRow("사용자", self.user_edit)
        conn_form.addRow("비밀번호", self.password_edit)
        conn_form.addRow("데이터베이스 이름", self.db_edit)

        conn_group = QGroupBox("1·2단계 연결 설정 및 데이터베이스 생성")
        conn_group.setLayout(conn_form)

        self.btn_connect = QPushButton("연결 테스트")
        self.btn_create_db = QPushButton("데이터베이스 생성 (users)")

        table_info_label = QLabel(
            "이 단계에서 생성할 테이블\n"
            "테이블 이름: users\n"
            "컬럼: id(INT, 자동 증가, 기본키), username(VARCHAR(150)), password(VARCHAR(150))"
        )
        table_info_label.setWordWrap(True)

        self.btn_create_table = QPushButton("users 테이블 생성")

        table_layout = QVBoxLayout()
        table_layout.addWidget(table_info_label)
        table_layout.addWidget(self.btn_create_table)

        table_group = QGroupBox("3단계 데이터베이스 선택 후 users 테이블 만들기")
        table_group.setLayout(table_layout)

        self.result_label = QLabel("결과 메시지가 여기에 표시됩니다.")

        main_layout = QVBoxLayout(self)
        main_layout.addWidget(conn_group)
        main_layout.addWidget(self.btn_connect)
        main_layout.addWidget(self.btn_create_db)
        main_layout.addWidget(table_group)
        main_layout.addWidget(self.result_label)

        self.btn_connect.clicked.connect(self.connect_to_database)
        self.btn_create_db.clicked.connect(self.create_database)
        self.btn_create_table.clicked.connect(self.create_users_table)

    def connect_to_database(self):
        host = self.host_edit.text().strip()
        user = self.user_edit.text().strip()
        password = self.password_edit.text()
        db_name = self.db_edit.text().strip()

        if not host or not user:
            QMessageBox.warning(self, "입력 오류", "호스트와 사용자 이름은 반드시 입력해야 합니다.")
            return

        try:
            if db_name:
                conn = mc.connect(
                    host=host,
                    user=user,
                    password=password,
                    database=db_name,
                )
            else:
                conn = mc.connect(
                    host=host,
                    user=user,
                    password=password,
                )

            if conn.is_connected():
                if db_name:
                    msg = f"MySQL 서버에 연결되었습니다.\n사용 중인 데이터베이스: {db_name}"
                else:
                    msg = "MySQL 서버에 연결되었습니다.\n데이터베이스 이름은 지정되지 않았습니다."
                self.result_label.setText(msg)
                QMessageBox.information(self, "연결 성공", msg)
            else:
                self.result_label.setText("MySQL 서버에 연결하지 못했습니다.")
                QMessageBox.warning(self, "연결 실패", "MySQL 서버에 연결하지 못했습니다.")

        except mc.Error as e:
            self.result_label.setText("연결 중 오류가 발생했습니다.")
            QMessageBox.critical(
                self,
                "연결 오류",
                f"MySQL 연결에 실패했습니다.\n오류 내용: {e}",
            )
            self.write_log_with_qt(f"연결 실패, 오류: {e}")
        finally:
            try:
                if conn.is_connected():
                    conn.close()
            except Exception:
                pass

    def create_database(self):
        host = self.host_edit.text().strip()
        user = self.user_edit.text().strip()
        password = self.password_edit.text()
        db_name = self.db_edit.text().strip()

        if not host or not user or not db_name:
            QMessageBox.warning(self, "입력 오류", "호스트, 사용자, 데이터베이스 이름을 모두 입력해야 합니다.")
            return

        try:
            conn = mc.connect(
                host=host,
                user=user,
                password=password,
            )

            cursor = conn.cursor()
            cursor.execute(f"CREATE DATABASE {db_name}")
            conn.commit()

            msg = f"{db_name} 데이터베이스가 생성되었습니다."
            self.result_label.setText(msg)
            QMessageBox.information(self, "생성 완료", msg)
            self.write_log_with_qt(f"데이터베이스 생성: {db_name}")

        except mc.Error as e:
            self.result_label.setText("데이터베이스 생성에 실패했습니다.")
            QMessageBox.critical(
                self,
                "생성 오류",
                f"데이터베이스 생성에 실패했습니다.\n오류 내용: {e}",
            )
            self.write_log_with_qt(f"데이터베이스 생성 실패: {db_name}, 오류: {e}")
        finally:
            try:
                if conn.is_connected():
                    conn.close()
            except Exception:
                pass

    def create_users_table(self):
        host = self.host_edit.text().strip()
        user = self.user_edit.text().strip()
        password = self.password_edit.text()
        db_name = self.db_edit.text().strip()

        if not host or not user or not db_name:
            QMessageBox.warning(self, "입력 오류", "먼저 데이터베이스 이름까지 포함한 연결 정보를 모두 입력해야 합니다.")
            return

        create_table_sql = """
        CREATE TABLE users (
            id INT AUTO_INCREMENT PRIMARY KEY,
            username VARCHAR(150) NOT NULL,
            password VARCHAR(150) NOT NULL
        )
        """

        try:
            conn = mc.connect(
                host=host,
                user=user,
                password=password,
                database=db_name,
            )

            cursor = conn.cursor()
            cursor.execute(create_table_sql)
            conn.commit()

            msg = f"{db_name} 데이터베이스에 users 테이블이 생성되었습니다."
            self.result_label.setText(msg)
            QMessageBox.information(self, "테이블 생성 완료", msg)
            self.write_log_with_qt(f"테이블 생성: {db_name}.users")

        except mc.Error as e:
            self.result_label.setText("테이블 생성 중 오류가 발생했습니다.")
            QMessageBox.critical(
                self,
                "테이블 생성 오류",
                f"users 테이블 생성에 실패했습니다.\n오류 내용: {e}",
            )
            self.write_log_with_qt(f"테이블 생성 실패: {db_name}.users, 오류: {e}")
        finally:
            try:
                if conn.is_connected():
                    conn.close()
            except Exception:
                pass

    def write_log_with_qt(self, message):
        file = QFile("db_steps_log.txt")
        if not file.open(QIODevice.Append | QIODevice.Text):
            return

        stream = QTextStream(file)
        stream << message << "\n"
        file.close()


if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MySqlDemoWindow()
    window.show()
    sys.exit(app.exec())
# main.py

import sys
from PySide6.QtWidgets import QApplication

from mainwindow import DbConnectWindow

if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = DbConnectWindow()
    w.show()
    sys.exit(app.exec())


3. 필요한 import + 주석 설명

더보기
from PySide6.QtCore import (
    QFile,          # 파일을 열고 닫는 등 입출력을 담당하는 Qt 파일 클래스이다.
    QIODevice,      # 파일 열기 모드(읽기, 쓰기, 추가 등)를 지정하기 위한 기반 클래스이다.
    QTextStream,    # 텍스트를 파일에 쓰고 읽기 위한 스트림 클래스이다.
)

로그를 남기기 위해 Qt의 QFile, QTextStream를 가져왔다.


 

4. GUI 구현부 소스코드와 주석

더보기
class MySqlDemoWindow(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)

        # 윈도우 제목과 크기를 설정한다.
        self.setWindowTitle("PySide6 MySQL 연동 기초 데모")
        self.resize(520, 420)

        # 이전 단계에서 사용한 MySQL 접속 정보 입력 필드들이다.
        self.host_edit = QLineEdit()
        self.user_edit = QLineEdit()
        self.password_edit = QLineEdit()
        self.password_edit.setEchoMode(QLineEdit.Password)  # 비밀번호 입력값을 숨김 모드로 표시한다.
        self.db_edit = QLineEdit()

        # 학습 편의를 위해 기본값을 미리 채워 둔다.
        self.host_edit.setText("localhost")
        self.user_edit.setText("root")

        # 접속 정보를 한 줄씩 라벨과 함께 배치하는 폼 레이아웃을 만든다.
        conn_form = QFormLayout()
        conn_form.addRow("호스트", self.host_edit)
        conn_form.addRow("사용자", self.user_edit)
        conn_form.addRow("비밀번호", self.password_edit)
        conn_form.addRow("데이터베이스 이름", self.db_edit)

        # 1단계, 2단계 관련 입력 필드를 묶는 그룹 박스이다.
        conn_group = QGroupBox("1·2단계 연결 설정 및 데이터베이스 생성")
        conn_group.setLayout(conn_form)

        # 1단계에서 구현한 연결 테스트 버튼이다.
        self.btn_connect = QPushButton("연결 테스트")

        # 2단계에서 구현한 데이터베이스 생성 버튼이다.
        self.btn_create_db = QPushButton("데이터베이스 생성 (users)")

        # 3단계에서 만들 테이블 구조를 눈으로 확인하기 위한 테이블 속성 안내다.
        table_info_label = QLabel(
            "이 단계에서 생성할 테이블\n"
            "테이블 이름: users\n"
            "컬럼: id(INT, 자동 증가, 기본키), username(VARCHAR(150)), password(VARCHAR(150))"
        )
        table_info_label.setWordWrap(True)  # 긴 텍스트가 줄바꿈되도록 설정한다.

        # users 테이블을 실제로 생성할 버튼이다.
        self.btn_create_table = QPushButton("users 테이블 생성")

        # 안내 라벨과 버튼을 세로로 배치하는 레이아웃이다.
        table_layout = QVBoxLayout()
        table_layout.addWidget(table_info_label)
        table_layout.addWidget(self.btn_create_table)

        # 3단계 영역을 묶는 그룹 박스이다.
        table_group = QGroupBox("3단계 데이터베이스 선택 후 users 테이블 만들기")
        table_group.setLayout(table_layout)

        # 전체 동작 결과를 간단히 표시할 라벨이다.
        self.result_label = QLabel("결과 메시지가 여기에 표시됩니다.")

        # 메인 레이아웃을 세로 방향으로 구성한다.
        main_layout = QVBoxLayout(self)
        main_layout.addWidget(conn_group)          # 접속 정보 그룹
        main_layout.addWidget(self.btn_connect)    # 연결 테스트 버튼
        main_layout.addWidget(self.btn_create_db)  # 데이터베이스 생성 버튼
        main_layout.addWidget(table_group)         # 테이블 생성 그룹
        main_layout.addWidget(self.result_label)   # 결과 표시 라벨

        # 시그널 연결은 다음 단계에서 설명한다.
        self.btn_connect.clicked.connect(self.connect_to_database)
        self.btn_create_db.clicked.connect(self.create_database)
        self.btn_create_table.clicked.connect(self.create_users_table)

 

  • 접속 정보 입력 UI는 1·2단계와 동일하게 유지한다.
  • 3단계용 QGroupBox를 추가하고, 그 안에
    테이블 구조 안내 라벨 + 테이블 생성 버튼을 배치한다.
  • 일반적이지 않지만,
    이번 단계 목표가 users 테이블 생성임을 직관적으로 이해할 수 있도록 구현다.

 


 

5. 시그널과 슬롯 연결 부분

더보기
        # users 테이블 생성 버튼 클릭 시, CREATE TABLE을 실행하는 함수와 연결한다.
        self.btn_create_table.clicked.connect(self.create_users_table)

동일한 패턴


 

6. <Table 생성> 주요 구조

더보기

6.1 <Table 생성> 핵심 요약

# 1. 연결
conn = mc.connect(서버_및_DB_접속_정보)

# 2. 확인
if conn.is_connected():
    성공_처리()

# 3. SQL 실행
cursor = conn.cursor()
cursor.execute(CREATE_TABLE_SQL)
conn.commit()

# 4. 종료
conn.close()

 

 

6.2 <Table 생성> 흐름

import mysql.connector as mc


def users_table_create_flow():
    # 1. DB 서버 및 데이터베이스 접속 정보 준비
    host = "localhost"
    user = "root"
    password = "password"
    database = "test_db"

    # 2. users 테이블 생성을 위한 SQL 준비
    create_table_sql = """
    CREATE TABLE users (
        id INT AUTO_INCREMENT PRIMARY KEY,
        username VARCHAR(150) NOT NULL,
        password VARCHAR(150) NOT NULL
    )
    """

    # 3. 지정된 데이터베이스에 연결
    conn = mc.connect(
        host=host,
        user=user,
        password=password,
        database=database
    )

    # 4. 서버와의 연결 상태 확인
    if conn.is_connected():
        print("DB 연결 성공")

        # 5. SQL 실행을 위한 커서 생성
        cursor = conn.cursor()

        # 6. CREATE TABLE SQL 실행
        cursor.execute(create_table_sql)

        # 7. 테이블 생성은 DB 구조 변경이므로 커밋 필요
        conn.commit()

    # 8. 모든 DB 작업이 끝난 후 연결 종료
    conn.close()


# users 테이블 생성 흐름 실행
users_table_create_flow()
  1. DB 접속 정보 준비
  2. CREATE TABLE SQL 작성
  3. 데이터베이스까지 포함하여 연결
  4. 연결 상태 확인
  5. 커서 생성
  6. CREATE TABLE 실행
  7. commit으로 변경 사항 반영
  8. 연결 종료

 

 

7. 기능 구현 소스코드

더보기

7.1 데이터베이스 선택 후 users 테이블 생성 함수

    def create_users_table(self):
        # 1단계: 접속 정보 읽기
        host = self.host_edit.text().strip()
        user = self.user_edit.text().strip()
        password = self.password_edit.text()
        db_name = self.db_edit.text().strip()

        # 데이터베이스 이름까지 지정돼 있어야 테이블을 만들 수 있다.
        if not host or not user or not db_name:
            QMessageBox.warning(self, "입력 오류", "먼저 데이터베이스 이름까지 포함한 연결 정보를 모두 입력해야 합니다.")
            return

        # 2단계: 생성할 테이블 구조를 정의하는 SQL을 문자열로 준비한다.
        # 이 SQL이 바로 3.3에서 말한 CREATE TABLE users 직접 작성해 보기의 예시이다.
        create_table_sql = """
        CREATE TABLE users (
            id INT AUTO_INCREMENT PRIMARY KEY,
            username VARCHAR(150) NOT NULL,
            password VARCHAR(150) NOT NULL
        )
        """

        try:
            # 3단계: 지정된 데이터베이스에 연결한다.
            # mysql.connector에서는 database 인자에 db_name을 넘기는 것이
            # USE db_name 명령과 같은 효과를 낸다고 이해하면 된다.
            conn = mc.connect(
                host=host,
                user=user,
                password=password,
                database=db_name,
            )

            cursor = conn.cursor()

            # 4단계: CREATE TABLE 문을 실행한다.
            cursor.execute(create_table_sql)
            conn.commit()

            # 5단계: 성공 메시지 출력
            msg = f"{db_name} 데이터베이스에 users 테이블이 생성되었습니다."
            self.result_label.setText(msg)
            QMessageBox.information(self, "테이블 생성 완료", msg)

            # 6단계: Qt 기반 파일 입출력으로 로그 기록
            self.write_log_with_qt(f"테이블 생성: {db_name}.users")

        except mc.Error as e:
            # 이미 같은 이름의 테이블이 있는 경우 등에서 예외가 발생한다.
            self.result_label.setText("테이블 생성 중 오류가 발생했습니다.")
            QMessageBox.critical(
                self,
                "테이블 생성 오류",
                f"users 테이블 생성에 실패했습니다.\n오류 내용: {e}",
            )
            self.write_log_with_qt(f"테이블 생성 실패: {db_name}.users, 오류: {e}")
        finally:
            # 7단계: 연결 자원 정리
            try:
                if conn.is_connected():
                    conn.close()
            except Exception:
                pass

 

  • 3.1 MySQL 클라이언트에서 생성된 DB 선택하기
    코드에서는 mc.connect(database=db_name)으로 구현된다.
    MySQL 클라이언트에서는 USE db_name과 동일한 의미를 갖는다.

  • 3.2 사용자 테이블 구조 설계하기
    create_table_sql 문자열 안에 id, username, password 컬럼과 타입을 정의했다.

 

 

 

 

7.2 동작 로그 기록 함수

    def write_log_with_qt(self, message):
        # QFile을 사용해 db_steps_log.txt 파일을 연다.
        # Append 모드와 Text 모드를 함께 사용하여, 기존 내용 뒤에 텍스트를 추가한다.
        file = QFile("db_steps_log.txt")
        if not file.open(QIODevice.Append | QIODevice.Text):
            # 파일을 열 수 없는 경우에는 조용히 반환한다.
            return

        # QTextStream을 사용해 파일에 문자열을 기록한다.
        stream = QTextStream(file)
        stream << message << "\n"

        # 작업이 끝났으면 파일을 닫는다.
        file.close()

 

  • 파이썬 open 대신 Qt의 QFile, QTextStream을 사용했다.
  • 데이터베이스 생성, 테이블 생성, 연결 실패 등 중요한 단계마다
    이 함수를 호출해 어떤 작업이 수행되었는지 한 줄씩 기록한다.
  • 일반적으로 이와 같이 모든 동작의 로그가 기록된다.


 

 

8. 실행

더보기

(1) 데이터베이스 생성

  • 호스트: localhost
  • 사용자: root
  • 비밀번호: 본인 환경 비밀번호
  • 데이터베이스 이름: pyqtdb
  • 데이터베이스 생성 버튼 클릭
    • 생성 완료 메시지 확인
    • db_steps_log.txt에 기록 확인

 

(2) 테이블 생성

  • 데이터베이스 이름이 pyqtdb로 설정돼 있는 상태에서
  • users 테이블 생성 버튼 클릭
    • pyqtdb 안에 users 테이블이 생성되었다는 메시지가 표시된다.
    • db_steps_log.txt에 테이블 생성 로그가 기록된다.

 

(3) MySQL 클라이언트에서 확인

USE pyqtdb;          -- 3.1 데이터베이스 선택
SHOW TABLES;         -- 3.4 테이블 목록 확인
DESCRIBE users;      -- 3.4 users 테이블 구조 확인


9.학습 주요 포인트

더보기
  • 데이터베이스와 테이블의 관계를 명확히 이해해야 한다.

  • MySQL 클라이언트에서의 USE dbname 명령은
    PySide6 코드에서는 mc.connect에 database=db_name을 넘기는 것과 같은 의미이다.

  • users 테이블 구조 설계는 이후 모든 단계의 기반이 된다.
    id 컬럼은 AUTO_INCREMENT PRIMARY KEY로 두어 자동 번호를 사용하고,
    username과 password는 NOT NULL로 설정해 비어 있는 값이 들어가지 않도록 한다.

  • CREATE TABLE, SHOW TABLES, DESCRIBE 명령을
    코드 안의 문자열로만 보지 말고,
    실제 MySQL 클라이언트에서 직접 실행해 보는 경험이 중요하다.

  • PySide6 애플리케이션 안에서
    데이터베이스 생성 → 테이블 생성까지 버튼 단위로 단계적으로 실행할 수 있게 구현하면서
    전체 흐름을 눈으로 따라간다.

  • QFile과 QTextStream을 활용한 로그 기록을 실습한다.

  • 이 단계까지 완료되면,
    다음 단계에서 INSERT 쿼리를 안전하게 실습할 수 있다.

 

단계별 완성 파일