Ver3.5 ๐ฅ๏ธ Qt & wxWidgets๋ก ๋ง๋๋C++ ํฌ๋ก์คํ๋ซํผ ์ฑ ๊ฐ๋ฐ

๐ฅ๏ธ Qt & wxWidgets๋ก ๋ง๋๋
C++ ํฌ๋ก์คํ๋ซํผ ์ฑ ๊ฐ๋ฐ
Windows, macOS, Linux ์ด๋์๋ ๋์๊ฐ๋ ์ฑ์ ๋ง๋ค์ด๋ณด์! ๐
์ผ, ์์งํ ๋งํด๋ด. ์ฑ ํ๋ ๋ง๋ค์๋๋ฐ Windows์์๋ง ๋์๊ฐ๋ฉด ์ผ๋ง๋ ์ต์ธํด? ๐ค
์น๊ตฌํํ
์๋ํ๋ ค๊ณ ๋ณด๋๋๋ "๋ Mac ์ฐ๋๋ฐ?"๋ผ๋ ๋ง ๋ค์ด๋ณธ ์ ์์ง?
๊ทธ๋์ ์ค๋์ C++๋ก ํฌ๋ก์คํ๋ซํผ ์ฑ์ ๋ง๋๋ ๋ ๊ฐ์ง ํต์ฌ ๋ฌด๊ธฐ, ๋ฐ๋ก Qt์ wxWidgets์ ๋ํด ์์ ๊น์ด ํํค์ณ๋ณผ ๊ฑฐ์ผ.
์ด ๋ ํ๋ ์์ํฌ๋ ์์ญ ๋
์ ์ญ์ฌ๋ฅผ ๊ฐ์ง ๊ฒ์ฆ๋ ๋๊ตฌ๋ค์ด๊ณ , ์ง๊ธ๋ ์ ์ธ๊ณ ์๋ง์ ๊ฐ๋ฐ์๋ค์ด ์ค๋ฌด์์ ์ฐ๊ณ ์์ด.
VLC ๋ฏธ๋์ด ํ๋ ์ด์ด, Autodesk ์ ํ๋ค, ์ฌ์ง์ด NASA ์ํํธ์จ์ด์๋ Qt๊ฐ ๋ค์ด๊ฐ ์๋ค๊ณ ํ๋ฉด ๋ฏฟ๊ฒจ? ๐ธ
์, ๊ทธ๋ผ ๋ณธ๊ฒฉ์ ์ผ๋ก ์์ํด๋ณด์!
๐ ํฌ๋ก์คํ๋ซํผ์ด ์ ์ค์ํ๊ฐ?
๋จผ์ ์ ํฌ๋ก์คํ๋ซํผ ๊ฐ๋ฐ์ด ์ด๋ ๊ฒ ์ค์ํ์ง๋ถํฐ ์ง๊ณ ๋์ด๊ฐ์.
2024๋
๊ธฐ์ค์ผ๋ก ๋ฐ์คํฌํ OS ์์ฅ์ ๋ณด๋ฉด Windows๊ฐ ์ฝ 72%, macOS๊ฐ ์ฝ 15%, Linux๊ฐ ์ฝ 4% ์ ๋๋ฅผ ์ฐจ์งํ๊ณ ์์ด.
์ฆ, Windows๋ง ์ง์ํ๋ฉด ์ ์ฒด ์ฌ์ฉ์์ 28%๋ฅผ ํฌ๊ธฐํ๋ ๊ฑฐ์ผ. ๊ทธ๊ฒ ์ผ๋ง๋ ํฐ ์์ค์ธ์ง ๋๊ปด์ง์ง? ๐ธ
๋ฌผ๋ก ์น ๊ธฐ๋ฐ ์ฑ(Electron ๊ฐ์)๋ ํฌ๋ก์คํ๋ซํผ์ ์ง์ํ์ง๋ง, C++ ๊ธฐ๋ฐ ๋ค์ดํฐ๋ธ ์ฑ์ ์ฑ๋ฅ ๋ฉด์์ ์๋์ ์ด์ผ.
ํนํ ๊ทธ๋ํฝ ์ฒ๋ฆฌ, ์ค์๊ฐ ๋ฐ์ดํฐ ์ฒ๋ฆฌ, ์๋ฒ ๋๋ ์์คํ
๊ฐ์ ๋ถ์ผ์์๋ C++ ํฌ๋ก์คํ๋ซํผ ํ๋ ์์ํฌ๊ฐ ์ง๊ฐ๋ฅผ ๋ฐํํด.
๊ทธ๋ฆฌ๊ณ ์์งํ ๋งํ๋ฉด, C++๋ก ํฌ๋ก์คํ๋ซํผ ์ฑ ๋ง๋๋ ๋ฅ๋ ฅ์ ์ทจ์
์์ฅ์์๋ ์์ฒญ๋ ๊ฒฝ์๋ ฅ์ด์ผ.
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์๋ Qt ๊ฐ๋ฐ์๋ C++ GUI ๊ฐ๋ฐ ๊ด๋ จ ์๋ขฐ๊ฐ ๊พธ์คํ ์ฌ๋ผ์ค๋ ๊ฑธ ๋ณด๋ฉด ์์๊ฐ ์ผ๋ง๋ ์๋์ง ์ ์ ์์ง. ๐
๐ต Qt ํ๋ ์์ํฌ ์์ ํด๋ถ
Qt์ ์ญ์ฌ์ ์ฒ ํ
Qt(ํํธ๋ผ๊ณ ์ฝ์ด, "ํํฐ"๊ฐ ์๋์ผ! ๐)๋ 1991๋
๋
ธ๋ฅด์จ์ด์ Trolltech ํ์ฌ์์ ๋ง๋ค์์ด.
์ดํ Nokia๊ฐ ์ธ์ํ๋ค๊ฐ, ์ง๊ธ์ The Qt Company๊ฐ ๊ด๋ฆฌํ๊ณ ์์ง.
30๋
์ด ๋๋ ์ญ์ฌ๋ฅผ ๊ฐ์ง ์ด ํ๋ ์์ํฌ๋ ๋จ์ํ GUI ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋์ด์ ์์ ํ ์ ํ๋ฆฌ์ผ์ด์
๊ฐ๋ฐ ํ๋ซํผ์ผ๋ก ์งํํ์ด.
Qt์ ํต์ฌ ์ฒ ํ์ "๋ชจ๋ ๊ฒ์ Qt ์์์"์ผ.
GUI๋ฟ๋ง ์๋๋ผ ๋คํธ์ํน, ๋ฐ์ดํฐ๋ฒ ์ด์ค, ๋ฉํฐ๋ฏธ๋์ด, 3D ๊ทธ๋ํฝ, ์น ์์ง๊น์ง Qt ํ๋๋ก ๋ค ํด๊ฒฐํ ์ ์์ด.
์ด๊ฒ Qt์ ๊ฐ์ฅ ํฐ ๊ฐ์ ์ด์, ๋๋ก๋ ๋จ์ ์ด ๋๊ธฐ๋ ํด(๋ฌด๊ฑฐ์์ง๊ฑฐ๋ ๐
).
Qt์ ํต์ฌ ๊ฐ๋ : Signal & Slot
Qt๋ฅผ ์ฒ์ ๋ฐฐ์ธ ๋ ๊ฐ์ฅ ๋จผ์ ๋ง๋๋ ๊ฐ๋
์ด ๋ฐ๋ก Signal & Slot ๋ฉ์ปค๋์ฆ์ด์ผ.
์ด๊ฑด Qt๊ฐ C++ ํ์ค์ ์๋ ์์ฒด์ ์ธ ์ด๋ฒคํธ ์ฒ๋ฆฌ ์์คํ
์ ๊ตฌํํ ๊ฑฐ์ผ.
์ฝ๊ฒ ๋งํ๋ฉด ์ด๋ ๊ฒ ์๊ฐํด๋ด:
๐ฏ Slot = "์๊ฒ ์ด, ๋ด๊ฐ ์ฒ๋ฆฌํ ๊ฒ!" ๋ผ๊ณ ์๋ตํ๋ ๊ฒ
๋ฒํผ์ ํด๋ฆญํ๋ฉด Signal์ด ๋ฐ์ํ๊ณ , ๊ทธ Signal์ ์ฐ๊ฒฐ๋ Slot ํจ์๊ฐ ์คํ๋๋ ๊ฑฐ์ผ.
์ด ๋ฐฉ์์ ์ฅ์ ์ ๋์จํ ๊ฒฐํฉ(Loose Coupling)์ด์ผ. ๋ฐ์ ์์ ์์ ์๊ฐ ์๋ก๋ฅผ ์ง์ ์ ํ์๊ฐ ์์ด.
// Qt Signal & Slot ๊ธฐ๋ณธ ์์
#include <QApplication>
#include <QPushButton>
#include <QLabel>
#include <QVBoxLayout>
#include <QWidget>
class MyWindow : public QWidget {
Q_OBJECT // Qt ๋ฉํ ์ค๋ธ์ ํธ ์์คํ
ํ์ฑํ (ํ์!)
public:
MyWindow(QWidget *parent = nullptr) : QWidget(parent) {
// ๋ ์ด์์ ์ค์
QVBoxLayout *layout = new QVBoxLayout(this);
// ์์ ฏ ์์ฑ
QPushButton *btn = new QPushButton("ํด๋ฆญํด๋ด!", this);
QLabel *label = new QLabel("์์ง ์ ๋๋ ธ์ด์", this);
layout->addWidget(btn);
layout->addWidget(label);
// Signal๊ณผ Slot ์ฐ๊ฒฐ (ํต์ฌ!)
connect(btn, &QPushButton::clicked, [label]() {
label->setText("๋ฒํผ์ด ๋๋ ธ์ด์! ๐");
});
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
MyWindow window;
window.setWindowTitle("Qt ํฌ๋ก์คํ๋ซํผ ์ฑ");
window.show();
return app.exec();
}
์ ์ฝ๋ ๋ณด๋ฉด ์ด๋? ์๊ฐ๋ณด๋ค ์ง๊ด์ ์ด์ง? ๐
connect() ํจ์ ํ๋๋ก ๋ฒํผ ํด๋ฆญ๊ณผ ๋ผ๋ฒจ ํ
์คํธ ๋ณ๊ฒฝ์ ์ฐ๊ฒฐํ์ด.
์ด ์ฝ๋๋ Windows, macOS, Linux ์ด๋์๋ ๋์ผํ๊ฒ ๋์ํด. ๊ทธ๊ฒ Qt์ ๋ง๋ฒ์ด์ผ!
Qt์ ์ฃผ์ ๋ชจ๋ ๊ตฌ์กฐ
Qt๋ ๋ชจ๋์ ๊ตฌ์กฐ๋ก ๋์ด ์์ด์ ํ์ํ ๊ฒ๋ง ๊ณจ๋ผ์ ์ธ ์ ์์ด.
์ฃผ์ ๋ชจ๋๋ค์ ์ดํด๋ณด์:
| ๋ชจ๋๋ช | ์ญํ | ์ฃผ์ ํด๋์ค |
|---|---|---|
| Qt Core | ํต์ฌ ๊ธฐ๋ฅ (๋ฌธ์์ด, ์ปจํ ์ด๋, ์ด๋ฒคํธ) | QString, QList, QTimer |
| Qt GUI | ๊ธฐ๋ณธ GUI ๊ธฐ๋ฅ | QWindow, QPainter, QFont |
| Qt Widgets | ๋ฐ์คํฌํ UI ์์ ฏ | QWidget, QDialog, QMainWindow |
| Qt Network | ๋คํธ์ํฌ ํต์ | QTcpSocket, QNetworkRequest |
| Qt SQL | ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๋ | QSqlDatabase, QSqlQuery |
| Qt Multimedia | ์ค๋์ค/๋น๋์ค | QMediaPlayer, QCamera |
| Qt Quick/QML | ๋ชจ๋ UI (์ ์ธํ) | QQuickView, QQmlEngine |
| Qt WebEngine | ์น ๋ธ๋ผ์ฐ์ ์์ง | QWebEngineView |
Qt์ ๋น๋ ์์คํ : qmake vs CMake
Qt ํ๋ก์ ํธ๋ฅผ ๋น๋ํ ๋ ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ด ์์ด.
Qt ์ ์ฉ ๋น๋ ์์คํ ์ด์ผ.
.pro ํ์ผ๋ก ํ๋ก์ ํธ๋ฅผ ์ค์ ํ๊ณ ,
Qt Creator์ ์๋ฒฝํ๊ฒ ํตํฉ๋ผ.
๊ฐ๋จํ ํ๋ก์ ํธ์ ์ฌ์ ํ ํธ๋ฆฌํด.
Qt 6๋ถํฐ ๊ณต์ ๊ถ์ฅ ๋น๋ ์์คํ .
๋ ๋์ ์ํ๊ณ์ ํธํ๋๊ณ ,
๋ณต์กํ ํ๋ก์ ํธ ๊ด๋ฆฌ์ ์ ๋ฆฌํด.
ํ์ ์์ ์ ์ ๋ ๋ง์ด ์ฐ์ฌ.
# CMakeLists.txt ์์ (Qt 6 ๊ธฐ์ค)
cmake_minimum_required(VERSION 3.16)
project(MyQtApp VERSION 1.0)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_AUTOMOC ON) # MOC ์๋ ์คํ
set(CMAKE_AUTOUIC ON) # UIC ์๋ ์คํ
set(CMAKE_AUTORCC ON) # RCC ์๋ ์คํ
find_package(Qt6 REQUIRED COMPONENTS Core Widgets Network)
add_executable(MyQtApp
main.cpp
mainwindow.cpp
mainwindow.h
)
target_link_libraries(MyQtApp PRIVATE
Qt6::Core
Qt6::Widgets
Qt6::Network
)
Qt์ MOC (Meta-Object Compiler)
Qt๋ฅผ ์ฒ์ ๋ฐฐ์ฐ๋ฉด MOC๋ผ๋ ๊ฐ๋
์์ ํ ๋ฒ์ฏค ๋ฉ๋ถ์ด ์ ๐ต
MOC๋ Qt์ Signal & Slot, Q_OBJECT ๋งคํฌ๋ก ๊ฐ์ ๊ธฐ๋ฅ์ C++ ํ์ค์ด ์ง์ํ์ง ์๊ธฐ ๋๋ฌธ์,
Qt๊ฐ ์์ฒด์ ์ผ๋ก ๋ง๋ ์ ์ฒ๋ฆฌ๊ธฐ(Pre-processor)์ผ.
๋น๋ ๊ณผ์ ์ ๋ณด๋ฉด:
์ด ๊ณผ์ ์ด ์๋์ผ๋ก ์ด๋ฃจ์ด์ง๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฐ์๋ ํฌ๊ฒ ์ ๊ฒฝ ์ธ ํ์ ์์ด.
CMake์์ CMAKE_AUTOMOC ON์ผ๋ก ์ค์ ํ๋ฉด ์์์ ์ฒ๋ฆฌํด์ค.
๋ค๋ง ์ด ๋๋ฌธ์ Qt ์ฝ๋๋ ์์ C++ ํ์ค๊ณผ 100% ํธํ๋์ง ์๋๋ค๋ ์ ์ ์์๋ฌ์ผ ํด.
Qt Designer์ Qt Creator
Qt๋ ๊ฐ๋ ฅํ ๊ฐ๋ฐ ๋๊ตฌ๋ค์ ์ ๊ณตํด:
.ui ํ์ผ์ ์์ฑํ๊ณ , ์ด๊ฑธ ์ฝ๋์ ์ฐ๊ฒฐํด์ ์ฌ์ฉํด.
๋ณต์กํ ๋ ์ด์์๋ ์ฝ๋ ํ ์ค ์์ด ๋๋ฑ ๋ง๋ค ์ ์์ด.
๐ ๏ธ Qt Creator: Qt ์ ์ฉ ํตํฉ ๊ฐ๋ฐ ํ๊ฒฝ(IDE)์ด์ผ.
์ฝ๋ ํธ์ง, ๋๋ฒ๊น , ํ๋กํ์ผ๋ง, Qt Designer ํตํฉ๊น์ง ๋ค ๋ค์ด์์ด.
๋ฌด๋ฃ๋ก ์ฌ์ฉํ ์ ์๊ณ , ํฌ๋ก์คํ๋ซํผ ๊ฐ๋ฐ์ ์ต์ ํ๋์ด ์์ด.
QML๊ณผ Qt Quick: ๋ชจ๋ UI์ ์ธ๊ณ
Qt 5๋ถํฐ ๋ณธ๊ฒฉ์ ์ผ๋ก ๋์
๋ QML(Qt Modeling Language)์ UI๋ฅผ ์ ์ธํ์ผ๋ก ์์ฑํ๋ ๋ฐฉ์์ด์ผ.
JavaScript์ ๋น์ทํ ๋ฌธ๋ฒ์ผ๋ก ์ ๋๋ฉ์ด์
, ํธ๋์ง์
, ๋ฐ์ํ UI๋ฅผ ์ฝ๊ฒ ๋ง๋ค ์ ์์ด.
// QML ์์ - ์ ๋๋ฉ์ด์
์ด ์๋ ๋ฒํผ
import QtQuick 2.15
import QtQuick.Controls 2.15
ApplicationWindow {
visible: true
width: 400
height: 300
title: "QML ๋ฐ๋ชจ"
Rectangle {
id: myRect
width: 100; height: 100
color: "#5b8fa8"
radius: 10
anchors.centerIn: parent
// ํด๋ฆญ ์ ์์ ๋ณ๊ฒฝ ์ ๋๋ฉ์ด์
MouseArea {
anchors.fill: parent
onClicked: {
colorAnimation.start()
}
}
ColorAnimation on color {
id: colorAnimation
to: "#7ab87a"
duration: 500
}
}
}
QML์ ํนํ ๋ชจ๋ฐ์ผ ์ฑ์ด๋ ์๋ฒ ๋๋ ์์คํ
UI์์ ๊ฐ๋ ฅํ ์ฑ๋ฅ์ ๋ฐํํด.
Qt for Android, Qt for iOS๋ ์ง์ํ๊ธฐ ๋๋ฌธ์ ๋ชจ๋ฐ์ผ๊น์ง ์ปค๋ฒํ ์ ์์ด. ์ง์ง ์ฌ์ธ์์ด์ง? ๐ฒ
๐ข wxWidgets ์์ ํด๋ถ
wxWidgets์ ์ญ์ฌ์ ์ฒ ํ
wxWidgets(์๋ ์ด๋ฆ์ wxWindows์์ด, ๋ง์ดํฌ๋ก์ํํธ์ ํญ์๋ก ์ด๋ฆ์ ๋ฐ๊ฟจ๋ค๋ ์ฐ์ด ์์ด ๐)๋
1992๋
Julian Smart๊ฐ ๋ง๋ ์คํ์์ค C++ GUI ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ผ.
wxWidgets์ ํต์ฌ ์ฒ ํ์ Qt์ ์์ ํ ๋ฌ๋ผ.
Qt๊ฐ "์ฐ๋ฆฌ๊ฐ ๋ค ๋ง๋ค์ด์ค๊ฒ"๋ผ๋ฉด, wxWidgets๋ "๊ฐ OS์ ๋ค์ดํฐ๋ธ ์์ ฏ์ ๊ทธ๋๋ก ์จ"์ผ.
์ด๊ฒ ๋ฌด์จ ๋ง์ด๋๋ฉด:
๐ macOS์์ ์คํํ๋ฉด: Cocoa ๋ค์ดํฐ๋ธ ์์ ฏ ์ฌ์ฉ
๐ง Linux์์ ์คํํ๋ฉด: GTK+ ๋ค์ดํฐ๋ธ ์์ ฏ ์ฌ์ฉ
์ด ๋ฐฉ์์ ์ฅ์ ์ ์๋ฒฝํ ๋ค์ดํฐ๋ธ ๋ฃฉ์คํ(Look & Feel)์ด์ผ.
Windows ์ฌ์ฉ์๊ฐ ๋ณด๋ฉด "์, ์ด๊ฑฐ Windows ์ฑ์ด๋ค"๋ผ๊ณ ๋๋ผ๊ณ ,
Mac ์ฌ์ฉ์๊ฐ ๋ณด๋ฉด "์ด๊ฑฐ ์์ Mac ์ฑ ๊ฐ์๋ฐ?"๋ผ๊ณ ๋๋ผ๋ ๊ฑฐ์ผ.
Qt๋ ์์ฒด ๋ ๋๋ง ์์ง์ ์ฐ๊ธฐ ๋๋ฌธ์ ์ด๋ฐ ์๋ฒฝํ ๋ค์ดํฐ๋ธ ๋๋์ ๋ด๊ธฐ๊ฐ ์ด๋ ค์.
wxWidgets ๊ธฐ๋ณธ ๊ตฌ์กฐ
wxWidgets ์ฑ์ ๊ธฐ๋ณธ ๊ตฌ์กฐ๋ฅผ ์ดํด๋ณด์:
// wxWidgets ๊ธฐ๋ณธ ์ฑ ๊ตฌ์กฐ
#include <wx/wx.h>
// 1. ์ฑ ํด๋์ค ์ ์ (wxApp ์์)
class MyApp : public wxApp {
public:
virtual bool OnInit() override;
};
// 2. wxIMPLEMENT_APP ๋งคํฌ๋ก๋ก ์ง์
์ ์ค์
wxIMPLEMENT_APP(MyApp);
// 3. ๋ฉ์ธ ํ๋ ์ ํด๋์ค (wxFrame ์์)
class MyFrame : public wxFrame {
public:
MyFrame();
private:
// ์ด๋ฒคํธ ํธ๋ค๋ฌ ์ ์ธ
void OnHello(wxCommandEvent& event);
void OnExit(wxCommandEvent& event);
// ์ด๋ฒคํธ ํ
์ด๋ธ ์ ์ธ
wxDECLARE_EVENT_TABLE();
};
// 4. ์ด๋ฒคํธ ํ
์ด๋ธ ์ ์
wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(wxID_EXIT, MyFrame::OnExit)
EVT_BUTTON(wxID_ANY, MyFrame::OnHello)
wxEND_EVENT_TABLE()
// 5. ์ฑ ์ด๊ธฐํ
bool MyApp::OnInit() {
MyFrame *frame = new MyFrame();
frame->Show(true);
return true;
}
// 6. ํ๋ ์ ์์ฑ์
MyFrame::MyFrame()
: wxFrame(nullptr, wxID_ANY, "wxWidgets ํฌ๋ก์คํ๋ซํผ ์ฑ",
wxDefaultPosition, wxSize(400, 300))
{
// ๋ฉ๋ด๋ฐ ์์ฑ
wxMenuBar *menuBar = new wxMenuBar();
wxMenu *fileMenu = new wxMenu();
fileMenu->Append(wxID_EXIT, "์ข
๋ฃ\tCtrl+Q");
menuBar->Append(fileMenu, "ํ์ผ");
SetMenuBar(menuBar);
// ํจ๋๊ณผ ๋ฒํผ ์์ฑ
wxPanel *panel = new wxPanel(this);
wxButton *btn = new wxButton(panel, wxID_ANY, "์๋
ํ์ธ์!",
wxPoint(150, 100));
// ์ํ๋ฐ
CreateStatusBar();
SetStatusText("์ค๋น๋จ");
}
void MyFrame::OnHello(wxCommandEvent& event) {
wxMessageBox("์๋
ํ์ธ์! wxWidgets ์ฑ์ด์์ ๐",
"์ธ์ฌ", wxOK | wxICON_INFORMATION);
}
void MyFrame::OnExit(wxCommandEvent& event) {
Close(true);
}
Qt์ ๋น๊ตํ๋ฉด ์ข ๋ ์ ํต์ ์ธ C++ ์คํ์ผ์ด์ง? ๐ค
์ด๋ฒคํธ ํ
์ด๋ธ ๋ฐฉ์์ MFC(Microsoft Foundation Classes)๋ฅผ ์จ๋ณธ ์ฌ๋์ด๋ผ๋ฉด ์ต์ํ ๊ฑฐ์ผ.
๋ฌผ๋ก wxWidgets๋ ๋๋ค ํจ์๋ฅผ ์ด์ฉํ ํ๋์ ์ธ ์ด๋ฒคํธ ๋ฐ์ธ๋ฉ์ ์ง์ํด:
// ํ๋์ ์ธ wxWidgets ์ด๋ฒคํธ ๋ฐ์ธ๋ฉ (Bind ๋ฐฉ์)
btn->Bind(wxEVT_BUTTON, [](wxCommandEvent& event) {
wxMessageBox("๋๋ค๋ก ์ฒ๋ฆฌํ์ด์! ๐");
});
wxWidgets์ ๋ ์ด์์ ์์คํ : Sizer
wxWidgets์์ UI ๋ ์ด์์์ ์ก์ ๋ Sizer๋ฅผ ์ฌ์ฉํด.
Sizer๋ ์์ ฏ๋ค์ ํฌ๊ธฐ์ ์์น๋ฅผ ์๋์ผ๋ก ๊ด๋ฆฌํด์ฃผ๋ ์์คํ
์ด์ผ.
// wxWidgets Sizer ์์
wxPanel *panel = new wxPanel(this);
// ์์ง ๋ฐ์ค Sizer ์์ฑ
wxBoxSizer *vSizer = new wxBoxSizer(wxVERTICAL);
wxStaticText *label = new wxStaticText(panel, wxID_ANY, "์ด๋ฆ์ ์
๋ ฅํ์ธ์:");
wxTextCtrl *textCtrl = new wxTextCtrl(panel, wxID_ANY);
wxButton *okBtn = new wxButton(panel, wxID_OK, "ํ์ธ");
// Sizer์ ์์ ฏ ์ถ๊ฐ
// (์์ ฏ, ๋น์จ, ํ๋๊ทธ, ์ฌ๋ฐฑ)
vSizer->Add(label, 0, wxALL | wxEXPAND, 10);
vSizer->Add(textCtrl, 0, wxALL | wxEXPAND, 10);
vSizer->Add(okBtn, 0, wxALL | wxCENTER, 10);
panel->SetSizer(vSizer);
vSizer->Fit(this); // ์ฐฝ ํฌ๊ธฐ๋ฅผ ๋ด์ฉ์ ๋ง๊ฒ ์กฐ์
wxWidgets์ ์ฃผ์ ์์ ฏ๋ค
| ์์ ฏ ํด๋์ค | ์ค๋ช | ๋์ Qt ํด๋์ค |
|---|---|---|
| wxButton | ์ผ๋ฐ ๋ฒํผ | QPushButton |
| wxTextCtrl | ํ ์คํธ ์ ๋ ฅ/ํธ์ง | QLineEdit / QTextEdit |
| wxListBox | ๋ชฉ๋ก ๋ฐ์ค | QListWidget |
| wxTreeCtrl | ํธ๋ฆฌ ๋ทฐ | QTreeWidget |
| wxGrid | ๊ทธ๋ฆฌ๋/ํ | QTableWidget |
| wxNotebook | ํญ ์ปจํธ๋กค | QTabWidget |
| wxDialog | ๋ค์ด์ผ๋ก๊ทธ ์ฐฝ | QDialog |
| wxFileDialog | ํ์ผ ์ ํ ๋ํ์์ | QFileDialog |
| wxSlider | ์ฌ๋ผ์ด๋ | QSlider |
| wxGauge | ์งํ ํ์์ค | QProgressBar |
wxWidgets ๋น๋ ์ค์
wxWidgets๋ CMake๋ฅผ ํตํด ์ฝ๊ฒ ์ค์ ํ ์ ์์ด:
# wxWidgets CMakeLists.txt ์์
cmake_minimum_required(VERSION 3.16)
project(MyWxApp)
set(CMAKE_CXX_STANDARD 17)
# wxWidgets ์ฐพ๊ธฐ
find_package(wxWidgets REQUIRED COMPONENTS core base)
include(${wxWidgets_USE_FILE})
add_executable(MyWxApp WIN32
main.cpp
myframe.cpp
myframe.h
)
target_link_libraries(MyWxApp ${wxWidgets_LIBRARIES})
vcpkg install wxwidgets ๋ช
๋ น์ด ํ๋๋ก ์ค์น ์๋ฃ!
โ๏ธ Qt vs wxWidgets: ์ง์ง ๋น๊ต
์, ์ด์ ๋ ํ๋ ์์ํฌ๋ฅผ ์ ๋๋ก ๋น๊ตํด๋ณผ ์๊ฐ์ด์ผ.
์ด๋ ๊ฒ ๋ ์ข๋ค๊ณ ๋จ์ ์ง๊ธฐ๋ณด๋ค๋, ์ด๋ค ์ํฉ์์ ์ด๋ค ๊ฑธ ์จ์ผ ํ๋์ง๋ฅผ ์์๋ณด์.
๋ผ์ด์ ์ค ๋น๊ต
LGPL v3 GPL v3 ์์ ์ฉ ๋ผ์ด์ ์ค
์คํ์์ค ํ๋ก์ ํธ: LGPL/GPL ๋ฌด๋ฃ
์์ ์ฉ ์ฑ: Qt Company ์์ ๋ผ์ด์ ์ค ํ์
(์ฐ๊ฐ ์๋ฐฑ๋ง ์ ์์ค)
โ ๏ธ ์์ ์ฉ์ผ๋ก ์ธ ๋ ๋ผ์ด์ ์ค ๋น์ฉ์ด ๋ฐ์ํ ์ ์์ด!
wxWindows ๋ผ์ด์ ์ค
LGPL ๊ธฐ๋ฐ์ด์ง๋ง ๋ ๊ด๋ํด.
์์ ์ฉ ์ฑ์๋ ๋ฌด๋ฃ๋ก ์ฌ์ฉ ๊ฐ๋ฅ!
์์ค ์ฝ๋ ๊ณต๊ฐ ์๋ฌด ์์.
โ ์์ ๋ฌด๋ฃ๋ก ์์ ์ฉ ์ฑ ๊ฐ๋ฐ ๊ฐ๋ฅ!
ํ์ง๋ง Qt์ ๋ชจ๋ ๊ธฐ๋ฅ(ํนํ Qt Quick/QML ์ผ๋ถ)์ ์ฐ๋ ค๋ฉด ์์ ๋ผ์ด์ ์ค๊ฐ ํ์ํ ๊ฒฝ์ฐ๊ฐ ์์ผ๋ ๊ผญ ํ์ธํด๋ด!
์ฑ๋ฅ ๋น๊ต
์ฑ๋ฅ ๋ฉด์์๋ ์ด๋จ๊น? ์์งํ๊ฒ ์๊ธฐํด์ค๊ฒ:
wxWidgets๊ฐ ์ผ๋ฐ์ ์ผ๋ก ๋ ๋น ๋ฅธ ํธ์ด์ผ. Qt๋ ์์ฒด ๋ฐํ์์ ์ด๊ธฐํํ๋ ์๊ฐ์ด ํ์ํ๊ฑฐ๋ .
์์ ์ ํธ๋ฆฌํฐ ์ฑ์ด๋ผ๋ฉด wxWidgets๊ฐ ๋ ๊ฐ๋ณ๊ฒ ๋๊ปด์ ธ.
๐พ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋:
wxWidgets๊ฐ ๋ ์ ์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฌ์ฉํด. Qt๋ ๋ชจ๋์ด ๋ง์์ ๊ธฐ๋ณธ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ด ๋์.
๐จ ๋ ๋๋ง ์ฑ๋ฅ:
๋ณต์กํ ์ปค์คํ ๊ทธ๋ํฝ์ด๋ ์ ๋๋ฉ์ด์ ์์๋ Qt๊ฐ ์๋์ ์ด์ผ.
Qt์ ํ๋์จ์ด ๊ฐ์ ๋ ๋๋ง ํ์ดํ๋ผ์ธ์ ์ ๋ง ๊ฐ๋ ฅํด.
๐ฆ ๋ฐฐํฌ ํ์ผ ํฌ๊ธฐ:
wxWidgets ์ฑ์ด ์ผ๋ฐ์ ์ผ๋ก ๋ ์์. Qt๋ ํ์ํ DLL/๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ง์์ ๋ฐฐํฌ ํจํค์ง๊ฐ ์ปค์ง ์ ์์ด.
ํ์ต ๊ณก์ ๋น๊ต
์ด๋ฐ์ Signal/Slot, MOC, Q_OBJECT ๊ฐ์
Qt ๊ณ ์ ๊ฐ๋ ์ ์ตํ์ผ ํด.
ํ์ง๋ง ๊ณต์ ๋ฌธ์๊ฐ ์ ๋ง ์ ๋์ด ์๊ณ ,
Qt Creator๊ฐ ๋ง์ ๊ฑธ ์๋ํํด์ค.
๐ ํ์ต ์๋ฃ: ๋งค์ฐ ํ๋ถ
๐ ์ปค๋ฎค๋ํฐ: ๋งค์ฐ ํ๋ฐ
์ ํต์ ์ธ C++ ์คํ์ผ์ ๊ฐ๊น์์
MFC ๊ฒฝํ์๋ผ๋ฉด ๋น ๋ฅด๊ฒ ์ ์ ๊ฐ๋ฅ.
ํ์ง๋ง ๊ณต์ ๋ฌธ์๊ฐ Qt๋ณด๋ค ๋ ์น์ ํ๊ณ ,
์์ ์ฝ๋๋ ์๋์ ์ผ๋ก ์ ์ด.
๐ ํ์ต ์๋ฃ: ๋ณดํต
๐ ์ปค๋ฎค๋ํฐ: ํ๋ฐํ์ง๋ง Qt๋ณด๋ค ์์
์ค์ ์ฌ์ฉ ์ฌ๋ก
์ด๋ค ์ ๋ช ํ ์ฑ๋ค์ด ์ด ํ๋ ์์ํฌ๋ฅผ ์ฐ๋์ง ์๋ฉด ์ ๋ขฐ๊ฐ ์๊ธฐ์ง? ๐
| ํ๋ ์์ํฌ | ์ ๋ช ์ฌ์ฉ ์ฌ๋ก |
|---|---|
| Qt | VLC ๋ฏธ๋์ด ํ๋ ์ด์ด, Autodesk Maya, KDE ๋ฐ์คํฌํ, Telegram Desktop, Qt Creator ์์ฒด, Wireshark(์ผ๋ถ), ๋ค์์ ์๋์ฐจ ์ธํฌํ ์ธ๋จผํธ ์์คํ |
| wxWidgets | Audacity(์ค๋์ค ํธ์ง๊ธฐ), FileZilla(FTP ํด๋ผ์ด์ธํธ), Code::Blocks(IDE), BitTorrent ํด๋ผ์ด์ธํธ๋ค, ๊ตฌ๋ฒ์ Tortoise SVN |
Audacity ์จ๋ดค์ด? ๊ทธ ์ ๋ช
ํ ๋ฌด๋ฃ ์ค๋์ค ํธ์ง๊ธฐ๊ฐ wxWidgets๋ก ๋ง๋ค์ด์ง ๊ฑฐ์ผ! ๐ต
FileZilla๋ ๋ง์ฐฌ๊ฐ์ง๊ณ . ์ด ์ ๋๋ฉด wxWidgets์ ์ค๋ ฅ์ด ์ฆ๋ช
๋ ๊ฑฐ์ง.
๐ ๏ธ ์ค์ ํ๋ก์ ํธ: ๊ฐ๋จํ ํ ์คํธ ์๋ํฐ ๋ง๋ค๊ธฐ
์ด๋ก ๋ง ๋ฐฐ์ฐ๋ฉด ์ฌ๋ฏธ์์์! ์ค์ ๋ก ๊ฐ๋จํ ํ ์คํธ ์๋ํฐ๋ฅผ ๋ ํ๋ ์์ํฌ๋ก ๋ง๋ค์ด๋ณด๋ฉด์ ์ฐจ์ด๋ฅผ ๋๊ปด๋ณด์. ๐
Qt๋ก ๋ง๋๋ ํ ์คํธ ์๋ํฐ
// mainwindow.h
#pragma once
#include <QMainWindow>
#include <QTextEdit>
#include <QMenuBar>
#include <QFileDialog>
#include <QMessageBox>
#include <QFile>
#include <QTextStream>
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
private slots:
void newFile();
void openFile();
void saveFile();
void about();
private:
QTextEdit *textEdit;
QString currentFile;
void createMenus();
void setCurrentFile(const QString &fileName);
};
// mainwindow.cpp
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
// ์ค์ ์์ ฏ์ผ๋ก ํ
์คํธ ์๋ํฐ ์ค์
textEdit = new QTextEdit(this);
setCentralWidget(textEdit);
// ํฐํธ ์ค์
QFont font("Consolas", 11);
textEdit->setFont(font);
createMenus();
setWindowTitle("Qt ํ
์คํธ ์๋ํฐ โ๏ธ");
resize(800, 600);
}
void MainWindow::createMenus() {
// ํ์ผ ๋ฉ๋ด
QMenu *fileMenu = menuBar()->addMenu("ํ์ผ(&F)");
fileMenu->addAction("์ ํ์ผ(&N)", this, &MainWindow::newFile,
QKeySequence::New);
fileMenu->addAction("์ด๊ธฐ(&O)...", this, &MainWindow::openFile,
QKeySequence::Open);
fileMenu->addAction("์ ์ฅ(&S)", this, &MainWindow::saveFile,
QKeySequence::Save);
fileMenu->addSeparator();
fileMenu->addAction("์ข
๋ฃ(&Q)", this, &QWidget::close,
QKeySequence::Quit);
// ๋์๋ง ๋ฉ๋ด
QMenu *helpMenu = menuBar()->addMenu("๋์๋ง(&H)");
helpMenu->addAction("์ ๋ณด(&A)", this, &MainWindow::about);
}
void MainWindow::newFile() {
if (textEdit->document()->isModified()) {
auto reply = QMessageBox::question(this, "์ ์ฅ",
"๋ณ๊ฒฝ์ฌํญ์ ์ ์ฅํ์๊ฒ ์ด์?",
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
if (reply == QMessageBox::Cancel) return;
if (reply == QMessageBox::Yes) saveFile();
}
textEdit->clear();
setCurrentFile("");
}
void MainWindow::openFile() {
QString fileName = QFileDialog::getOpenFileName(this,
"ํ์ผ ์ด๊ธฐ", "", "ํ
์คํธ ํ์ผ (*.txt);;๋ชจ๋ ํ์ผ (*)");
if (fileName.isEmpty()) return;
QFile file(fileName);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
QMessageBox::warning(this, "์ค๋ฅ", "ํ์ผ์ ์ด ์ ์์ด์!");
return;
}
QTextStream in(&file);
in.setEncoding(QStringConverter::Utf8);
textEdit->setPlainText(in.readAll());
setCurrentFile(fileName);
}
void MainWindow::saveFile() {
if (currentFile.isEmpty()) {
currentFile = QFileDialog::getSaveFileName(this,
"ํ์ผ ์ ์ฅ", "", "ํ
์คํธ ํ์ผ (*.txt)");
if (currentFile.isEmpty()) return;
}
QFile file(currentFile);
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
QMessageBox::warning(this, "์ค๋ฅ", "ํ์ผ์ ์ ์ฅํ ์ ์์ด์!");
return;
}
QTextStream out(&file);
out.setEncoding(QStringConverter::Utf8);
out << textEdit->toPlainText();
setCurrentFile(currentFile);
}
void MainWindow::setCurrentFile(const QString &fileName) {
currentFile = fileName;
textEdit->document()->setModified(false);
QString title = fileName.isEmpty() ? "์ ํ์ผ" :
QFileInfo(fileName).fileName();
setWindowTitle(title + " - Qt ํ
์คํธ ์๋ํฐ");
}
void MainWindow::about() {
QMessageBox::about(this, "์ ๋ณด",
"Qt๋ก ๋ง๋ ๊ฐ๋จํ ํ
์คํธ ์๋ํฐ\n"
"ํฌ๋ก์คํ๋ซํผ ์ฑ ๊ฐ๋ฐ ์์ ๐");
}
wxWidgets๋ก ๋ง๋๋ ํ ์คํธ ์๋ํฐ
// wxTextEditor.h
#pragma once
#include <wx/wx.h>
#include <wx/textctrl.h>
#include <wx/filedlg.h>
enum {
ID_NEW = wxID_HIGHEST + 1,
ID_OPEN,
ID_SAVE,
ID_ABOUT
};
class TextEditorFrame : public wxFrame {
public:
TextEditorFrame();
private:
wxTextCtrl *textCtrl;
wxString currentFile;
void OnNew(wxCommandEvent& event);
void OnOpen(wxCommandEvent& event);
void OnSave(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
void OnExit(wxCommandEvent& event);
void OnClose(wxCloseEvent& event);
wxDECLARE_EVENT_TABLE();
};
// wxTextEditor.cpp
#include "wxTextEditor.h"
wxBEGIN_EVENT_TABLE(TextEditorFrame, wxFrame)
EVT_MENU(ID_NEW, TextEditorFrame::OnNew)
EVT_MENU(ID_OPEN, TextEditorFrame::OnOpen)
EVT_MENU(ID_SAVE, TextEditorFrame::OnSave)
EVT_MENU(ID_ABOUT, TextEditorFrame::OnAbout)
EVT_MENU(wxID_EXIT, TextEditorFrame::OnExit)
EVT_CLOSE(TextEditorFrame::OnClose)
wxEND_EVENT_TABLE()
TextEditorFrame::TextEditorFrame()
: wxFrame(nullptr, wxID_ANY, "wxWidgets ํ
์คํธ ์๋ํฐ",
wxDefaultPosition, wxSize(800, 600))
{
// ํ
์คํธ ์ปจํธ๋กค ์์ฑ (๋ฉํฐ๋ผ์ธ, ์คํฌ๋กค๋ฐ ํฌํจ)
textCtrl = new wxTextCtrl(this, wxID_ANY, wxEmptyString,
wxDefaultPosition, wxDefaultSize,
wxTE_MULTILINE | wxTE_RICH2);
// ํฐํธ ์ค์
wxFont font(11, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL,
wxFONTWEIGHT_NORMAL);
textCtrl->SetFont(font);
// ๋ฉ๋ด๋ฐ ์์ฑ
wxMenuBar *menuBar = new wxMenuBar();
wxMenu *fileMenu = new wxMenu();
fileMenu->Append(ID_NEW, "์ ํ์ผ(&N)\tCtrl+N");
fileMenu->Append(ID_OPEN, "์ด๊ธฐ(&O)...\tCtrl+O");
fileMenu->Append(ID_SAVE, "์ ์ฅ(&S)\tCtrl+S");
fileMenu->AppendSeparator();
fileMenu->Append(wxID_EXIT, "์ข
๋ฃ(&Q)\tCtrl+Q");
wxMenu *helpMenu = new wxMenu();
helpMenu->Append(ID_ABOUT, "์ ๋ณด(&A)");
menuBar->Append(fileMenu, "ํ์ผ(&F)");
menuBar->Append(helpMenu, "๋์๋ง(&H)");
SetMenuBar(menuBar);
// ์ํ๋ฐ
CreateStatusBar();
SetStatusText("์ค๋น๋จ");
}
void TextEditorFrame::OnNew(wxCommandEvent& event) {
if (textCtrl->IsModified()) {
int result = wxMessageBox("๋ณ๊ฒฝ์ฌํญ์ ์ ์ฅํ์๊ฒ ์ด์?",
"์ ์ฅ",
wxYES_NO | wxCANCEL | wxICON_QUESTION);
if (result == wxCANCEL) return;
if (result == wxYES) OnSave(event);
}
textCtrl->Clear();
currentFile.clear();
SetTitle("์ ํ์ผ - wxWidgets ํ
์คํธ ์๋ํฐ");
}
void TextEditorFrame::OnOpen(wxCommandEvent& event) {
wxFileDialog openDialog(this, "ํ์ผ ์ด๊ธฐ", "", "",
"ํ
์คํธ ํ์ผ (*.txt)|*.txt|๋ชจ๋ ํ์ผ (*.*)|*.*",
wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (openDialog.ShowModal() == wxID_CANCEL) return;
currentFile = openDialog.GetPath();
textCtrl->LoadFile(currentFile);
SetTitle(openDialog.GetFilename() + " - wxWidgets ํ
์คํธ ์๋ํฐ");
SetStatusText("ํ์ผ ์ด๋ฆผ: " + currentFile);
}
void TextEditorFrame::OnSave(wxCommandEvent& event) {
if (currentFile.empty()) {
wxFileDialog saveDialog(this, "ํ์ผ ์ ์ฅ", "", "",
"ํ
์คํธ ํ์ผ (*.txt)|*.txt",
wxFD_SAVE | wxFD_OVERWRITE_PROMPT);
if (saveDialog.ShowModal() == wxID_CANCEL) return;
currentFile = saveDialog.GetPath();
}
textCtrl->SaveFile(currentFile);
SetStatusText("์ ์ฅ๋จ: " + currentFile);
}
void TextEditorFrame::OnAbout(wxCommandEvent& event) {
wxMessageBox("wxWidgets๋ก ๋ง๋ ๊ฐ๋จํ ํ
์คํธ ์๋ํฐ\n"
"ํฌ๋ก์คํ๋ซํผ ์ฑ ๊ฐ๋ฐ ์์ ๐",
"์ ๋ณด", wxOK | wxICON_INFORMATION);
}
void TextEditorFrame::OnExit(wxCommandEvent& event) {
Close(true);
}
void TextEditorFrame::OnClose(wxCloseEvent& event) {
if (textCtrl->IsModified()) {
int result = wxMessageBox("๋ณ๊ฒฝ์ฌํญ์ ์ ์ฅํ์๊ฒ ์ด์?",
"์ข
๋ฃ",
wxYES_NO | wxCANCEL | wxICON_QUESTION);
if (result == wxCANCEL) { event.Veto(); return; }
if (result == wxYES) {
wxCommandEvent dummy;
OnSave(dummy);
}
}
event.Skip();
}
๋ ์ฝ๋๋ฅผ ๋น๊ตํด๋ณด๋ฉด ์ด๋? ๐ค
Qt๋ ๋๋ค์ ํ๋์ ์ธ C++ ์คํ์ผ์ ๋ ๋ง์ด ํ์ฉํ๊ณ ,
wxWidgets๋ ์ด๋ฒคํธ ํ
์ด๋ธ๊ณผ ์ ํต์ ์ธ C++ ์คํ์ผ์ ์ฌ์ฉํด.
๊ธฐ๋ฅ์ ์ผ๋ก๋ ๊ฑฐ์ ๋์ผํ์ง๋ง ์ฝ๋ฉ ์คํ์ผ์ด ๊ฝค ๋ค๋ฅด์ง?
๐จ ์คํ์ผ๋ง๊ณผ ํ ๋ง ์ ์ฉ
Qt Style Sheets (QSS)
Qt์ ๊ฐ์ฅ ๊ฐ๋ ฅํ ๊ธฐ๋ฅ ์ค ํ๋๊ฐ ๋ฐ๋ก Qt Style Sheets(QSS)์ผ.
CSS์ ๊ฑฐ์ ๋์ผํ ๋ฌธ๋ฒ์ผ๋ก Qt ์์ ฏ์ ์ธ๊ด์ ์์ ํ ๋ฐ๊ฟ ์ ์์ด!
// QSS ์ ์ฉ ์์
QApplication app(argc, argv);
// ์ ์ฒด ์ฑ์ ์คํ์ผ ์ ์ฉ
app.setStyleSheet(R"(
QMainWindow {
background-color: #2b2b2b;
}
QPushButton {
background-color: #5b8fa8;
color: white;
border: none;
border-radius: 6px;
padding: 8px 16px;
font-size: 14px;
font-weight: bold;
}
QPushButton:hover {
background-color: #3d7a96;
}
QPushButton:pressed {
background-color: #2c5f7a;
}
QTextEdit {
background-color: #1e1e1e;
color: #d4d4d4;
border: 1px solid #3d3d3d;
border-radius: 4px;
font-family: 'Consolas', monospace;
font-size: 12px;
}
QMenuBar {
background-color: #2b2b2b;
color: #cccccc;
}
QMenuBar::item:selected {
background-color: #5b8fa8;
}
)");
์ด๋ ๊ฒ ํ๋ฉด ๋คํฌ ํ
๋ง ์ฑ์ ๋๋ฑ ๋ง๋ค ์ ์์ด! ๐
QSS๋ CSS์ ๊ฑฐ์ ๊ฐ์์ ์น ๊ฐ๋ฐ ๊ฒฝํ์ด ์์ผ๋ฉด ๊ธ๋ฐฉ ์ตํ ์ ์์ด.
wxWidgets ํ ๋ง
wxWidgets๋ ๋ค์ดํฐ๋ธ ์์ ฏ์ ์ฐ๊ธฐ ๋๋ฌธ์ OS ํ
๋ง๋ฅผ ์๋์ผ๋ก ๋ฐ๋ผ๊ฐ.
Windows 11์์ ์คํํ๋ฉด Windows 11 ์คํ์ผ, macOS์์ ์คํํ๋ฉด macOS ์คํ์ผ์ด ์๋ ์ ์ฉ๋ผ.
์ปค์คํ
์คํ์ผ๋ง์ Qt๋ณด๋ค ์ ํ์ ์ด์ง๋ง, ๋์ OS ์ ๊ทผ์ฑ ๊ธฐ๋ฅ(๊ณ ๋๋น ๋ชจ๋, ์คํฌ๋ฆฐ๋ฆฌ๋ ๋ฑ)์ด ์๋์ผ๋ก ์ง์๋ผ.
wxDC(Device Context)๋ฅผ ์ฌ์ฉํด.OnPaint ์ด๋ฒคํธ์์ ์ง์ ๊ทธ๋ํฝ์ ๊ทธ๋ฆด ์ ์์ด. Qt์ QPainter์ ๋น์ทํ ๊ฐ๋
์ด์ผ.
๐ฆ ๋ฐฐํฌ์ ํจํค์ง
์ฑ์ ๋ง๋ค์์ผ๋ฉด ๋ฐฐํฌํด์ผ์ง! ํฌ๋ก์คํ๋ซํผ ์ฑ ๋ฐฐํฌ๋ ์๊ฐ๋ณด๋ค ์ ๊ฒฝ ์ธ ๊ฒ ๋ง์. ๐
Qt ์ฑ ๋ฐฐํฌ
Qt๋ windeployqt, macdeployqt, linuxdeployqt ๊ฐ์ ๋ฐฐํฌ ๋๊ตฌ๋ฅผ ์ ๊ณตํด:
# Windows ๋ฐฐํฌ (ํ์ํ Qt DLL ์๋ ๋ณต์ฌ)
windeployqt.exe MyApp.exe
# macOS ๋ฐฐํฌ (์ฑ ๋ฒ๋ค ์์ฑ)
macdeployqt MyApp.app -dmg
# Linux (AppImage ์์ฑ - linuxdeployqt ์ฌ์ฉ)
linuxdeployqt MyApp -appimage
Qt6Core.dll, Qt6Gui.dll, Qt6Widgets.dll (๊ธฐ๋ณธ ๋ชจ๋)
platforms/ ํด๋ (ํ๋ซํผ ํ๋ฌ๊ทธ์ธ)
styles/ ํด๋ (์คํ์ผ ํ๋ฌ๊ทธ์ธ)
imageformats/ ํด๋ (์ด๋ฏธ์ง ํฌ๋งท ํ๋ฌ๊ทธ์ธ)
์ ์ฒด ๋ฐฐํฌ ํจํค์ง๊ฐ 20~50MB ์ ๋ ๋ ์ ์์ด.
wxWidgets ์ฑ ๋ฐฐํฌ
wxWidgets๋ ์ ์ ๋งํน(Static Linking)์ ์ฌ์ฉํ๋ฉด ๋จ์ผ ์คํ ํ์ผ๋ก ๋ฐฐํฌํ ์ ์์ด!
์ด๊ฒ wxWidgets์ ํฐ ์ฅ์ ์ค ํ๋์ผ:
# CMake์์ wxWidgets ์ ์ ๋งํน ์ค์
set(wxBUILD_SHARED OFF) # ์ ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ
find_package(wxWidgets REQUIRED COMPONENTS core base)
# ๊ฒฐ๊ณผ: ๋จ์ผ ์คํ ํ์ผ (Windows์์ ์ฝ 5~15MB)
USB์ ๋ด์์ ๋ฐ๋ก ์คํํ ์ ์๋ ํฌํฐ๋ธ ์ฑ ๋ง๋ค๊ธฐ์ ์ต์ ์ด์ผ.
ํฌ๋ก์ค ์ปดํ์ผ
Linux์์ Windows ์ฑ์ ๋น๋ํ๊ฑฐ๋, ๋ฐ๋๋ก ํ๊ณ ์ถ์ ๋ ํฌ๋ก์ค ์ปดํ์ผ์ ์ฌ์ฉํด.
Qt๋ ํฌ๋ก์ค ์ปดํ์ผ ์ง์์ด ์ ๋์ด ์์ด์ ์๋ฒ ๋๋ ์์คํ
๊ฐ๋ฐ์ ๋ง์ด ์ฐ์ฌ.
# Linux์์ Windows์ฉ Qt ์ฑ ํฌ๋ก์ค ์ปดํ์ผ (MXE ์ฌ์ฉ)
# MXE(M cross environment) ์ค์น ํ:
i686-w64-mingw32.static-cmake ..
make
# ๊ฒฐ๊ณผ: Windows์์ ์คํ ๊ฐ๋ฅํ .exe ํ์ผ ์์ฑ!
๐ง ๊ณ ๊ธ ๊ธฐ๋ฅ๋ค
Qt์ ๋ฉํฐ์ค๋ ๋ฉ
GUI ์ฑ์์ ๋ฌด๊ฑฐ์ด ์์
์ ํ ๋ UI๊ฐ ๋ฉ์ถ๋ฉด ์ ๋์์?
Qt๋ ๋ฉํฐ์ค๋ ๋ฉ์ ์ฐ์ํ๊ฒ ์ฒ๋ฆฌํ ์ ์์ด:
// Qt ๋ฉํฐ์ค๋ ๋ฉ ์์ (QThread + Signal/Slot)
#include <QThread>
#include <QProgressBar>
class WorkerThread : public QThread {
Q_OBJECT
signals:
void progressUpdated(int value);
void workFinished(const QString &result);
protected:
void run() override {
// ๋ฌด๊ฑฐ์ด ์์
(UI ์ค๋ ๋์ ๋ณ๊ฐ๋ก ์คํ)
for (int i = 0; i <= 100; i++) {
QThread::msleep(50); // ์์
์๋ฎฌ๋ ์ด์
emit progressUpdated(i); // UI ์ค๋ ๋์ ์งํ๋ฅ ์ ๋ฌ
}
emit workFinished("์์
์๋ฃ! ๐");
}
};
// ๋ฉ์ธ ์๋์ฐ์์ ์ฌ์ฉ
WorkerThread *worker = new WorkerThread();
connect(worker, &WorkerThread::progressUpdated,
progressBar, &QProgressBar::setValue);
connect(worker, &WorkerThread::workFinished,
[](const QString &msg) {
QMessageBox::information(nullptr, "์๋ฃ", msg);
});
worker->start(); // ๋ฐฑ๊ทธ๋ผ์ด๋ ์ค๋ ๋ ์์
Qt์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๋
// Qt SQL ์์ (SQLite ์ฌ์ฉ)
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QSqlError>
// ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("myapp.db");
if (!db.open()) {
qDebug() << "DB ์ฐ๊ฒฐ ์คํจ:" << db.lastError().text();
return;
}
// ํ
์ด๋ธ ์์ฑ
QSqlQuery query;
query.exec("CREATE TABLE IF NOT EXISTS users ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"name TEXT NOT NULL,"
"email TEXT UNIQUE)");
// ๋ฐ์ดํฐ ์ฝ์
query.prepare("INSERT INTO users (name, email) VALUES (:name, :email)");
query.bindValue(":name", "ํ๊ธธ๋");
query.bindValue(":email", "hong@example.com");
query.exec();
// ๋ฐ์ดํฐ ์กฐํ
query.exec("SELECT * FROM users");
while (query.next()) {
qDebug() << query.value("id").toInt()
<< query.value("name").toString()
<< query.value("email").toString();
}
Qt์ ๋คํธ์ํฌ ํต์
// Qt Network ์์ (HTTP GET ์์ฒญ)
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QJsonDocument>
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
QNetworkRequest request(QUrl("https://api.example.com/data"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QNetworkReply *reply = manager->get(request);
connect(reply, &QNetworkReply::finished, [reply]() {
if (reply->error() == QNetworkReply::NoError) {
QByteArray data = reply->readAll();
QJsonDocument doc = QJsonDocument::fromJson(data);
// JSON ํ์ฑ
QJsonObject obj = doc.object();
qDebug() << "์๋ต:" << obj["message"].toString();
} else {
qDebug() << "์ค๋ฅ:" << reply->errorString();
}
reply->deleteLater();
});
๐ก ์ด๋ค ๊ฑธ ์ ํํด์ผ ํ ๊น?
์, ์ด์ ์ง์ง ํต์ฌ ์ง๋ฌธ์ด์ผ. Qt๋ฅผ ์จ์ผ ํ ๊น, wxWidgets๋ฅผ ์จ์ผ ํ ๊น? ๐ค
์ํฉ๋ณ๋ก ๋ช
ํํ๊ฒ ์ ๋ฆฌํด์ค๊ฒ:
โ ๋ณต์กํ๊ณ ๊ธฐ๋ฅ์ด ๋ง์ ์ฑ์ ๋ง๋ค ๋ (Qt์ ํ๋ถํ ๋ชจ๋ ํ์ฉ)
โ ๋ชจ๋ฐ์ผ(Android/iOS)๊น์ง ์ง์ํด์ผ ํ ๋
โ ์๋ฒ ๋๋ ์์คํ UI ๊ฐ๋ฐ (์๋์ฐจ, ์ฐ์ ์ฉ ๊ธฐ๊ธฐ)
โ 3D ๊ทธ๋ํฝ, ์ ๋๋ฉ์ด์ ์ด ๋ง์ ์ฑ
โ QML๋ก ๋ชจ๋ํ๊ณ ํ๋ คํ UI๋ฅผ ๋ง๋ค๊ณ ์ถ์ ๋
โ ํ ๊ท๋ชจ๊ฐ ํฌ๊ณ ์ฅ๊ธฐ ํ๋ก์ ํธ์ผ ๋
โ ๊ฐ๋ ฅํ IDE(Qt Creator)์ ๋๋ฒ๊น ๋๊ตฌ๊ฐ ํ์ํ ๋
โ ์๋ฒฝํ ๋ค์ดํฐ๋ธ ๋ฃฉ์คํ์ด ์ค์ํ ๋
โ ์์ ์ฉ ์ฑ์ธ๋ฐ ๋ผ์ด์ ์ค ๋น์ฉ์ ํผํ๊ณ ์ถ์ ๋
โ ๊ฐ๋ณ๊ณ ๋น ๋ฅธ ์ ํธ๋ฆฌํฐ ์ฑ์ ๋ง๋ค ๋
โ ๋จ์ผ ์คํ ํ์ผ๋ก ๋ฐฐํฌํด์ผ ํ ๋ (์ ์ ๋งํน)
โ MFC ๊ฒฝํ์ด ์์ด์ ๋น์ทํ ์คํ์ผ์ ์ํ ๋
โ OS ์ ๊ทผ์ฑ ๊ธฐ๋ฅ์ด ์ค์ํ ์ฑ (์คํฌ๋ฆฐ๋ฆฌ๋ ๋ฑ)
โ ๊ธฐ์กด wxWidgets ์ฝ๋๋ฒ ์ด์ค๋ฅผ ์ ์ง๋ณด์ํ ๋
์ํ๊ณ, ๋ฌธ์, ๋๊ตฌ, ์ปค๋ฎค๋ํฐ ๋ชจ๋ ๋ฉด์์ Qt๊ฐ ์์ ์์ด.
๋จ, ๋ผ์ด์ ์ค ๋น์ฉ์ด ๋ถ๋ด๋๊ฑฐ๋ ๋ค์ดํฐ๋ธ ๋ฃฉ์คํ์ด ์ ๋์ ์ผ๋ก ์ค์ํ๋ค๋ฉด wxWidgets๋ฅผ ๊ณ ๋ คํด๋ด.
๊ทธ๋ฆฌ๊ณ ํน์ C++ GUI ๊ฐ๋ฐ ํ๋ฆฌ๋์๋ก ํ๋ํ๊ณ ์ถ๋ค๋ฉด,
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ Qt ๊ฐ๋ฐ ์๋น์ค๋ฅผ ์ ๊ณตํ๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ.
Qt ๊ธฐ๋ฐ ์ฐ์
์ฉ ์ํํธ์จ์ด๋ ์ปค์คํ
GUI ๊ฐ๋ฐ ์๋ขฐ๊ฐ ๊พธ์คํ ์๊ฑฐ๋ . ๐ผ
๐ ๊ฐ๋ฐ ํ๊ฒฝ ์ธํ ๊ฐ์ด๋
Qt ๊ฐ๋ฐ ํ๊ฒฝ ์ธํ
Step 2: Qt Creator + Qt 6.x (์ํ๋ ๋ฒ์ ) ์ ํ ์ค์น
Step 3: ์ปดํ์ผ๋ฌ ์ ํ (Windows: MSVC ๋๋ MinGW, Linux/Mac: GCC/Clang)
Step 4: Qt Creator ์คํ โ ์ ํ๋ก์ ํธ โ Qt Widgets Application ์ ํ
Step 5: ๋น๋ & ์คํ! ๐
wxWidgets ๊ฐ๋ฐ ํ๊ฒฝ ์ธํ
git clone https://github.com/microsoft/vcpkg./vcpkg/bootstrap-vcpkg.bat./vcpkg/vcpkg install wxwidgets:x64-windows./vcpkg/vcpkg integrate installLinux (Ubuntu/Debian)
sudo apt-get install libwxgtk3.2-devmacOS (Homebrew)
brew install wxwidgets
wxWidgets๋ Qt Creator๋ณด๋ค VS Code๋ CLion์์ ๋ ํธํ๊ฒ ๊ฐ๋ฐํ ์ ์์ด.
๐ ํ์ต ๋ก๋๋งต
์, ์ด์ ์ด๋ป๊ฒ ๊ณต๋ถํด์ผ ํ ์ง ๋ก๋๋งต์ ๊ทธ๋ ค์ค๊ฒ! ๐บ๏ธ
Qt ํ์ต ๋ก๋๋งต
C++ ๊ธฐ์ด (ํด๋์ค, ์์, ํฌ์ธํฐ, ์ค๋งํธ ํฌ์ธํฐ)
Qt ์ค์น ๋ฐ Qt Creator ์ฌ์ฉ๋ฒ
๊ธฐ๋ณธ ์์ ฏ (QPushButton, QLabel, QLineEdit, QTextEdit)
Signal & Slot ๊ฐ๋ ์ดํด
๋ ์ด์์ ๊ด๋ฆฌ (QVBoxLayout, QHBoxLayout, QGridLayout)
๐ฃ ์ด๊ธ ๋จ๊ณ (2~3๊ฐ์)
QMainWindow, QDialog, QMessageBox
ํ์ผ I/O (QFile, QTextStream)
Qt ์ปจํ ์ด๋ (QList, QMap, QVector)
QString ํ์ฉ๋ฒ
Qt Designer๋ก UI ๋ง๋ค๊ธฐ
๐ฅ ์ค๊ธ ๋จ๊ณ (3~6๊ฐ์)
Qt Network (HTTP ํต์ , WebSocket)
Qt SQL (๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๋)
QThread ๋ฉํฐ์ค๋ ๋ฉ
Model/View ์ํคํ ์ฒ (QAbstractItemModel)
QPainter ์ปค์คํ ๊ทธ๋ฆฌ๊ธฐ
QSS ์คํ์ผ๋ง
๐ฆ ๊ณ ๊ธ ๋จ๊ณ (6๊ฐ์+)
QML / Qt Quick
Qt 3D
Qt for Android/iOS
ํ๋ฌ๊ทธ์ธ ์์คํ
์ฑ๋ฅ ์ต์ ํ ๋ฐ ํ๋กํ์ผ๋ง
์ถ์ฒ ํ์ต ์๋ฃ
๐บ ์ ํ๋ธ: "Qt Tutorial" ๊ฒ์ํ๋ฉด ๋ฌด๋ฃ ๊ฐ์ ๋ง์
๐ ์ฑ : "Qt 6 Core Beginners with C++" (Packt ์ถํ์ฌ)
๐ป ์ค์ต: Qt ๊ณต์ ์์ ์ฝ๋ (Qt Creator์ ๋ด์ฅ๋์ด ์์ด)
๐ ์ปค๋ฎค๋ํฐ: Qt Forum (forum.qt.io), Stack Overflow
"๊ฐ๋จํ ๊ณ์ฐ๊ธฐ โ ๋ฉ๋ชจ์ฅ โ ์์ ํ๋ ์ด์ด" ์์๋ก ์ ์ ๋ณต์กํ ์ฑ์ ๋ง๋ค์ด๋ด.
๐ ๋ง๋ฌด๋ฆฌ: ํฌ๋ก์คํ๋ซํผ์ ๋ฏธ๋
Qt์ wxWidgets๋ 30๋
์ด ๋๋ ์ญ์ฌ๋ฅผ ๊ฐ์ง ๊ฒ์ฆ๋ ๋๊ตฌ๋ค์ด์ผ.
Electron(JavaScript ๊ธฐ๋ฐ), Flutter(Dart ๊ธฐ๋ฐ), Tauri(Rust ๊ธฐ๋ฐ) ๊ฐ์ ์๋ก์ด ํฌ๋ก์คํ๋ซํผ ํ๋ ์์ํฌ๋ค์ด ๋ฑ์ฅํ์ง๋ง,
C++ ๊ธฐ๋ฐ ๋ค์ดํฐ๋ธ ์ฑ๋ฅ์ด ํ์ํ ๋ถ์ผ์์๋ Qt์ wxWidgets์ ์๋ฆฌ๋ ๊ตณ๊ฑดํด.
ํนํ Qt๋ ์๋์ฐจ ์ธํฌํ
์ธ๋จผํธ, ์๋ฃ ๊ธฐ๊ธฐ, ์ฐ์
์ฉ ์ ์ด ์์คํ
, ํญ๊ณต์ฐ์ฃผ ์ํํธ์จ์ด ๊ฐ์
๊ณ ์ฑ๋ฅ, ๊ณ ์ ๋ขฐ์ฑ์ด ์๊ตฌ๋๋ ๋ถ์ผ์์ ๊ณ์ ๊ฐ์ธ๋ฅผ ๋ณด์ด๊ณ ์์ด.
wxWidgets๋ Audacity, FileZilla ๊ฐ์ ์ ๋ช
์คํ์์ค ํ๋ก์ ํธ๋ค์ด ๊ณ์ ์ฌ์ฉํ๋ฉด์
๊พธ์คํ ์
๋ฐ์ดํธ๋๊ณ ์๊ณ , ํนํ ๊ฐ๋ณ๊ณ ๋ค์ดํฐ๋ธํ ๋ฐ์คํฌํ ์ฑ์ด ํ์ํ ๊ฒฝ์ฐ์๋ ์ฌ์ ํ ์ต๊ณ ์ ์ ํ์ด์ผ.
Qt๋ wxWidgets ์ค ํ๋๋ฅผ ์ ํํด์ ๊พธ์คํ ๊ณต๋ถํ๋ฉด,
Windows, macOS, Linux ์ด๋์๋ ๋์๊ฐ๋ ๊ฐ๋ ฅํ ์ฑ์ ๋ง๋ค ์ ์์ด.
์ง๊ธ ๋น์ฅ Qt Creator๋ฅผ ์ค์นํ๊ณ "Hello World" ์ฑ๋ถํฐ ์์ํด๋ด! ๐ช
๋ง์ง๋ง์ผ๋ก, C++ GUI ๊ฐ๋ฐ ์ค๋ ฅ์ ์์๋ค๋ฉด ์ฌ๋ฅ๋ท์์ ํ๋ฆฌ๋์๋ก ํ๋ํ๋ ๊ฒ๋ ๊ณ ๋ คํด๋ด.
Qt ๊ธฐ๋ฐ ์ปค์คํ
์ํํธ์จ์ด ๊ฐ๋ฐ, wxWidgets ์ฑ ์ ์ง๋ณด์ ๊ฐ์ ์๋ขฐ๋ค์ด ๊พธ์คํ ์ฌ๋ผ์ค๊ฑฐ๋ .
๊ธฐ์ ์ ๋ฐฐ์ฐ๊ณ โ ์ค๋ ฅ์ ์๊ณ โ ์์ต๊น์ง ์ฐฝ์ถํ๋ ์ ์ํ์ ๋ง๋ค์ด๋ด! ๐ฏ
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.