Qt 프레임워크란


Qt는 컴퓨터 프로그래밍에서 GUI 프로그램 개발에 널리 쓰이는 오픈 소스 C++ 개발 프레임워크이다. C++와 QML이라는 자체 스크립트 언어를 기반으로 한다. 회사 내부에서는 Qt를 "cute"로 발음하고 있으며 비공식적으로는 "큐티"로 발음한다. Qt는 KDE, Qtopia, OPIE에 이용되고 있다.

 

"Write once, compile anywhere"를 표방하는 크로스 플랫폼 프레임워크이며, 현존하는 주요 플랫폼(Windows, Linux, OSX, Android, iOS 등)을 거의 대부분 지원한다. 초기에는 크로스플랫폼 GUI 라이브러리로 시작하였으나, 점점 기능이 확대되어 각종 UI 개발 도구, 번역 작업을 위한 도구와 C++ 개발에 필요한 온갖 기능(UI, XML, 네트워크, SVG, Animation, Container Class 등등)을 제공하는 종합 프레임워크가 되었다

 

Qt는 C++를 주로 사용하지만, 파이썬, 루비, C, , 파스칼과도 연동된다. 수많은 플랫폼에서 동작하며, 상당히 좋은 국제화를 지원한다. SQL 데이터베이스 접근, XML 처리, 스레드 관리, 단일 크로스 플랫폼 파일 관리 API를 제공한다.

 

Qt 6 지원 플랫폼


데스크탑 플랫폼 : Linux, macOC, Windows

모바일 플랫폼 : Android, iOS

임베디드 플랫폼

지원되는 플랫폼 자세히 보기 : https://doc.qt.io/qt-6/supported-platforms.html

 

라이선스


오픈소스 라이선스(무료)상용 라이선스(유료) 중 선택 가능하다. 

모듈별로 지원되는 라이선스가 다르니 확인이 필요하다. (모듈에 따라서는 Third-party License가 적용되기도 한다.)

오픈소스 라이선스 중에서도 GPL과 LGPL, BSD 라이센스 등이 섞여있으므로.. 잘 확인해야 한다. GPL 라이선스가 적용되는 모듈을 쓰면 전체 소스코드 공개 의무를 지게 된다.

 

라이선스 확인하기 :

Qt 5 : https://doc.qt.io/qt-5/licenses-used-in-qt.html

 

Licenses Used in Qt | Qt 5.15

Licenses Used in Qt Qt contains some code that is not provided under the GNU Lesser General Public License (LGPL) or the Qt Commercial License, but rather under specific licenses from the original authors. The Qt Company gratefully acknowledges these and o

doc.qt.io

Qt 6 : https://doc.qt.io/qt-6/licenses-used-in-qt.html

 

Licenses Used in Qt | Qt 6.2

Licenses Used in Qt Qt contains some code that is not provided under the GNU Lesser General Public License (LGPL) or the Qt Commercial License, but rather under specific licenses from the original authors. The Qt Company gratefully acknowledges these and o

doc.qt.io

 

참고:

https://ko.wikipedia.org/wiki/Qt_(%EC%86%8C%ED%94%84%ED%8A%B8%EC%9B%A8%EC%96%B4) 

https://namu.wiki/w/Qt(%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC)

방법 1. toStdString().c_str() 사용

QString text = "AAA";
const char* p = text.toStdString().c_str();
qDebug("text : %s", p);

이는 아래 코드와 동일하다.

QString text = "AAA";
std::string str = text.toStdString();
const char* p = str.c_str();
qDebug("text : %s", p);

방법 2. qPrintable() 사용

QString text = "AAA";
qDebug("text : %s", qPrintable(text));

QTreeWidget의 기본 동작을 살펴보는 예제이다.

- QTreeWidget에 데이터를 추가/수정/삭제하고,

- 현재 선택된 데이터의 값을 보여주고,

- 선택된 데이터의 행 위치를 옮긴다.(위로 올리거나 내림)

※ 데이터를 외부 데이터 소스(파일 등)에서 읽어오거나, 저장하는 기능은 생략하였다.

부분 소스코드 살펴보기

UI는 QtDesigner로 만들고, 각 버튼 클릭 시 slot을 연결해 주었다.

 

헤더 설정

    QStringList headers;
    headers << tr("Col 1") << tr("Col 2");
    ui->treeWidget->setHeaderLabels(headers);

 

최상위 데이터 추가

col 1, col 2의 lineEdit에 입력한 값을 최상위 레벨에 추가해 준다.

    QTreeWidgetItem *parent = new QTreeWidgetItem(ui->treeWidget);
    parent->setText(0, ui->lineEditCol1->text());
    parent->setText(1, ui->lineEditCol2->text());

 

하위 데이터 추가

col 1, col 2의 lineEdit에 입력한 값을, 선택된 부모의 자식으로 추가해 준다.

    // QTreeWidget은 SingleSelection만 허용하도록 설정해놓았음
    QList<QTreeWidgetItem*> parent = ui->treeWidget->selectedItems();
    if (parent.size() > 0) {
        QTreeWidgetItem *child = new QTreeWidgetItem(parent[0]);
        child->setText(0, ui->lineEditCol1->text());
        child->setText(1, ui->lineEditCol2->text());
        child->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
        parent[0]->setExpanded(true);
    }
    else {
        QMessageBox::information(this, tr("Info"), tr("Please select the parent item"), QMessageBox::Ok);
    }

 

데이터 삭제

선택된 아이템을 삭제한다. (선택된 아이템 하위에 자식이 있는 경우, 자식도 모두 삭제됨)

    QTreeWidgetItem *item = ui->treeWidget->currentItem();
    int index;
    if (item) {
        QTreeWidgetItem *parent = item->parent();
        if (parent) { // 하위 아이템 삭제
            index = parent->indexOfChild(item);
            delete parent->takeChild(index);
        }
        else {  // 최상위 레벨 아이템 삭제
            index = ui->treeWidget->indexOfTopLevelItem(item);
            delete ui->treeWidget->takeTopLevelItem(index);
        }
    }
    else {
        QMessageBox::information(this, tr("Info"), tr("Please select the item"), QMessageBox::Ok);
    }

 

데이터 수정

아이템에 Qt::ItemIsEditable 플래그를 설정해주면, 해당 아이템을 더블 클릭 시 수정 가능하게 된다.

child->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);

 

클릭한 데이터 정보 보기

itemClicked 시그널이 발생하면, 클릭한 아이템 정보를 보여주는 slot 함수를 호출하도록 한다.

connect(ui->treeWidget, &QTreeWidget::itemClicked, this, &TreeDialog::on_itemClicked);

on_itemClicked 함수

void TreeDialog::on_itemClicked(QTreeWidgetItem *item, int column)
{
    displayCurrentItem(item, column);
}

displayCurrentItem 함수

void TreeDialog::displayCurrentItem(QTreeWidgetItem *item, int column)
{
    if (item) {
        ui->labelCurrentItem->setText(item->data(column, Qt::DisplayRole).toString());
    }
}

 

클릭한 데이터 행을 위/아래로 옮기기

아이템 클릭 후, Up 또는 Down 버튼을 누르면, 해당 아이템을 위로 올리거나 아래로 내린다.

enum Direction {
    Up = -1,
    Down = 1
};

void TreeDialog::on_pushButtonUp_clicked()
{
    moveItem(Direction::Up);
}

void TreeDialog::on_pushButtonDown_clicked()
{
    moveItem(Direction::Down);
}

void TreeDialog::moveItem(Direction direction)
{
    QTreeWidgetItem *item = ui->treeWidget->currentItem();
    if (!item)
        return;

    int row = ui->treeWidget->currentIndex().row();
    if ((direction == Direction::Up) && (row <= 0)) // 더 이상 올라갈 곳이 없음
        return;

    int lastRow = 0;
    QTreeWidgetItem *parent = item->parent();
    if (parent)
        lastRow = parent->childCount() -1;
    else
        lastRow = ui->treeWidget->topLevelItemCount() - 1;
    if ((direction == Direction::Down) && (row == lastRow)) // 더 이상 내려갈 곳이 없음
        return;

    int upDownIdx = static_cast<int>(direction);
    int index;
    if (parent) { // 하위 아이템의 경우
        index = parent->indexOfChild(item);
        QTreeWidgetItem *child = parent->takeChild(index); // 선택된 아이템을 부모에서 분리하고
        parent->insertChild(index + upDownIdx, child);  // 옮긴 위치에 다시 insert 한다
        ui->treeWidget->setCurrentItem(child);
        parent->setExpanded(true);
        child->setExpanded(true);
    }
    else { // 최상위 레벨 아이템의 경우
        index = ui->treeWidget->indexOfTopLevelItem(item);
        QTreeWidgetItem *child = ui->treeWidget->takeTopLevelItem(index); // 선택된 아이템을 부모에서 분리하고
        ui->treeWidget->insertTopLevelItem(index + upDownIdx, child); // 옮긴 위치에 다시 insert 한다
        ui->treeWidget->setCurrentItem(child);
        child->setExpanded(true);
    }
}

 

전체 소스코드

treedialog.h

#ifndef TREEDIALOG_H
#define TREEDIALOG_H

#include <QDialog>
#include <QTreeWidgetItem>


enum Direction {
    Up = -1,
    Down = 1
};

namespace Ui {
class TreeDialog;
}

class TreeDialog : public QDialog
{
    Q_OBJECT

public:
    explicit TreeDialog(QWidget *parent = nullptr);
    ~TreeDialog();
    void moveItem(Direction direction);
    void displayCurrentItem(QTreeWidgetItem *item, int column);

private slots:
    void on_pushButtonAddParent_clicked();
    void on_pushButtonAddChild_clicked();
    void on_pushButtonDel_clicked();
    void on_pushButtonUp_clicked();
    void on_pushButtonDown_clicked();
    void on_itemClicked(QTreeWidgetItem *item, int column);

private:
    Ui::TreeDialog *ui;
};

#endif // TREEDIALOG_H

treedialog.cpp

#include "treedialog.h"
#include "ui_treedialog.h"
#include <QMessageBox>

TreeDialog::TreeDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::TreeDialog)
{
    ui->setupUi(this);

    ui->treeWidget->setColumnCount(2);  // QTreeWidget을 열을 2개로 설정

    // 헤더 설정
    QStringList headers;
    headers << tr("Col 1") << tr("Col 2");
    ui->treeWidget->setHeaderLabels(headers);

    // 데이터(아이템) 추가
    QTreeWidgetItem *cities = new QTreeWidgetItem(ui->treeWidget);  // 첫번째 부모
    cities->setText(0, tr("Cities"));
    cities->setText(1, tr(" - "));

    QTreeWidgetItem *osloItem = new QTreeWidgetItem(cities);        // 첫번째 부모의 첫번째 자식(row 1)
    osloItem->setText(0, tr("Oslo"));
    osloItem->setText(1, tr("No"));
    QTreeWidgetItem *seoulItem = new QTreeWidgetItem(cities);       // 첫번째 부모의 두번째 자식(row 2)
    seoulItem->setText(0, tr("Seoul"));
    seoulItem->setText(1, tr("Yes"));
    cities->setExpanded(true);

    QTreeWidgetItem *colors = new QTreeWidgetItem(ui->treeWidget);  // 두번째 부모
    colors->setText(0, tr("Colors"));
    // 아이템을 수정 가능하게 함 - ItemIsEditable 플래그를 설정해주면 아이템을 더블 클릭시 수정 가능해짐
    colors->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
    QTreeWidgetItem *redItem = new QTreeWidgetItem(colors);         // 두번째 부모의 첫번째 자식(row 1)
    redItem->setText(0, tr("Red"));
    redItem->setText(1, tr("1"));
    redItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
    QTreeWidgetItem *yellowItem = new QTreeWidgetItem(colors);      // 두번째 부모의 두번째 자식(row 1)
    yellowItem->setText(0, tr("Yellow"));
    yellowItem->setText(1, tr("2"));
    yellowItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
    yellowItem->setHidden(true);    // 아이템을 숨김(화면에 표시 안함)
    QTreeWidgetItem *greenItem = new QTreeWidgetItem(colors);         // 두번째 부모의 첫번째 자식(row 1)
    greenItem->setText(0, tr("Green"));
    greenItem->setText(1, tr("1"));
    greenItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
    colors->setExpanded(true);

    connect(ui->treeWidget, &QTreeWidget::itemClicked, this, &TreeDialog::on_itemClicked);
//    ui->treeWidget->setSortingEnabled(true);    // 헤더 클릭 시 오름/내림차순 정렬되게 함

    displayCurrentItem(ui->treeWidget->currentItem(), 0);

}

TreeDialog::~TreeDialog()
{
    delete ui;
}

void TreeDialog::on_pushButtonAddParent_clicked()
{
    // 최상위 아이템을 추가함
    QTreeWidgetItem *parent = new QTreeWidgetItem(ui->treeWidget);
    parent->setText(0, ui->lineEditCol1->text());
    parent->setText(1, ui->lineEditCol2->text());
}

void TreeDialog::on_pushButtonAddChild_clicked()
{
    // 선택된 아이템 하위에 데이터를 추가함
    // QTreeWidget은 SingleSelection만 허용하도록 설정해놓았음
    QList<QTreeWidgetItem*> parent = ui->treeWidget->selectedItems();
    if (parent.size() > 0) {
        QTreeWidgetItem *child = new QTreeWidgetItem(parent[0]);
        child->setText(0, ui->lineEditCol1->text());
        child->setText(1, ui->lineEditCol2->text());
        child->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable | Qt::ItemIsSelectable);
        parent[0]->setExpanded(true);
    }
    else {
        QMessageBox::information(this, tr("Info"), tr("Please select the parent item"), QMessageBox::Ok);
    }
}

void TreeDialog::on_pushButtonDel_clicked()
{
    QTreeWidgetItem *item = ui->treeWidget->currentItem();
    int index;
    if (item) {
        QTreeWidgetItem *parent = item->parent();
        if (parent) { // 하위 아이템 삭제
            index = parent->indexOfChild(item);
            delete parent->takeChild(index);
        }
        else {  // 최상위 레벨 아이템 삭제
            index = ui->treeWidget->indexOfTopLevelItem(item);
            delete ui->treeWidget->takeTopLevelItem(index);
        }
    }
    else {
        QMessageBox::information(this, tr("Info"), tr("Please select the item"), QMessageBox::Ok);
    }
}

void TreeDialog::on_pushButtonUp_clicked()
{
    moveItem(Direction::Up);
}

void TreeDialog::on_pushButtonDown_clicked()
{
    moveItem(Direction::Down);
}

void TreeDialog::on_itemClicked(QTreeWidgetItem *item, int column)
{
    displayCurrentItem(item, column);
}

void TreeDialog::moveItem(Direction direction)
{
    QTreeWidgetItem *item = ui->treeWidget->currentItem();
    if (!item)
        return;

    int row = ui->treeWidget->currentIndex().row();
    if ((direction == Direction::Up) && (row <= 0)) // 더 이상 올라갈 곳이 없음
        return;

    int lastRow = 0;
    QTreeWidgetItem *parent = item->parent();
    if (parent)
        lastRow = parent->childCount() -1;
    else
        lastRow = ui->treeWidget->topLevelItemCount() - 1;
    if ((direction == Direction::Down) && (row == lastRow)) // 더 이상 내려갈 곳이 없음
        return;

    int upDownIdx = static_cast<int>(direction);
    int index;
    if (parent) { // 하위 아이템의 경우
        index = parent->indexOfChild(item);
        QTreeWidgetItem *child = parent->takeChild(index); // 선택된 아이템을 부모에서 분리하고
        parent->insertChild(index + upDownIdx, child);  // 옮긴 위치에 다시 insert 한다
        ui->treeWidget->setCurrentItem(child);
        parent->setExpanded(true);
        child->setExpanded(true);
    }
    else { // 최상위 레벨 아이템의 경우
        index = ui->treeWidget->indexOfTopLevelItem(item);
        QTreeWidgetItem *child = ui->treeWidget->takeTopLevelItem(index); // 선택된 아이템을 부모에서 분리하고
        ui->treeWidget->insertTopLevelItem(index + upDownIdx, child); // 옮긴 위치에 다시 insert 한다
        ui->treeWidget->setCurrentItem(child);
        child->setExpanded(true);
    }
}

void TreeDialog::displayCurrentItem(QTreeWidgetItem *item, int column)
{
    if (item) {
        ui->labelCurrentItem->setText(item->data(column, Qt::DisplayRole).toString());
    }
}

main.cpp

#include "treedialog.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    TreeDialog w;
    w.show();
    return a.exec();
}

 

소스 코드 첨부

main.cpp
0.00MB
treedialog.cpp
0.01MB
treedialog.h
0.00MB
treedialog.ui
0.00MB

어플리케이션을 개발하다 보면 Tree 구조 형태의 데이터를 보여주어야 하는 일들이 있다. (예 : 파일 탐색기 등)

Qt에서는 이러한 Tree 구조 데이터를 보여줄 수 있는 수단으로 QTreeWidgetQTreeView라는 클래스를 제공한다.

그럼 이 둘의 차이점은 무엇이고, 언제 어떤 클래스를 사용해야 할까?

 

QTreeWidget과 QTreeView의 차이점

가장 큰 차이는, 데이터가 어디에 저장되는가데이터와 뷰의 분리가 가능한가 이다.

QTreeWidget은,

Qt 초기 버전에서 사용되던 클래스로, 데이터가 위젯 내에 존재한다. 사용자는 위젯 내에 존재하는 데이터에 대해 검색과 편집을 수행하고, 어느 시점엔가 변경된 내용을 원본 데이터에 기록하곤 했다.

  • 장점: 이 방식은 이해와 사용이 간단하다.
  • 단점: 크기가 큰 데이터 집합은 위젯 내에 데이터를 모두 보유하기 부담스러워지기 시작했고, 동일한 데이터를 둘 이상의 서로 다른 위젯에 출력하고자 하는 상황에는 적합하지 않았다. (같은 데이터를 동시에 두 화면 - 트리 형태로 보여주는 화면과, 테이블 형태로 보여주는 화면 - 에 보여주려고 하면 힘들었다. 데이터가 위젯에 각각 저장되어 있기 때문에 어느 한쪽에서 변경 시 두 화면 사이의 싱크를 맞춰주기가 힘들었음)

QTreeView는,

이러한 QTreeWidget의 단점을 보완하기 위해 만들어진 클래스로, MVC 패턴에서 영감을 받은 모델/뷰 아키텍처의 '뷰' 부분이다. 쉽게 말하면 데이터를 다루는 모델 부분과, 데이터를 화면에 출력하는 뷰 부분을 분리하여 운영하는 것이다.

데이터는 '모델 클래스' 한 곳에서 관리하고, 이 모델에 연결된 여러개의 '뷰 클래스'를 만듦으로써 동일한 데이터를 여러 화면에서 보여주는 것을 가능하게 했다. 따라서 '모델'에서만 데이터를 변경 하면, 여러개의 '뷰'에 동시에 반영이 될 수 있게 된 것이다.

 

또한, 모델에 아무리 큰 집합의 데이터가 있어도, 각 뷰에서는 자신이 보여줄 부분의 데이터만 가져와서 보여주면 되어 뷰의 성능이 획기적으로 향상되게 된다.

 

이러한 개념은 QListWidgetQListView, QTableWidgetQTableView에도 동일하게 적용된다. (각각 보여지는 형태가 리스트 형태인지, 테이블 형태인지만 달라질 뿐이다.)

QListWidget과 QTableWidget은 데이터를 위젯 내에 저장하고 / QListView와 QTableView는 데이터가 저장된 모델에 연동되어 화면을 보여줄 수 있는 뷰 클래스이다.

 

언제 어떤 클래스를 사용해야 할까?

QTreeWidget (또는 QListWidget, QTableWidget) 을 사용하는 경우

데이터 집합이 작고, 동시에 여러군데서 보여질 필요 없이, 간단히 구현이 필요할 때 사용한다.

데이터 저장/변경/화면에 표시가 하나의 위젯에서 이루어지므로 QTreeView에 비해 상대적으로 구현이 간단하다.

QTreeView (또는 QListView, QTableView) 를 사용하는 경우

QTreeWidget과 반대로, 데이터 집합이 큰 경우, 같은 데이터를 동시에 여러 뷰에서 보여주어야 하는 경우에 적합하다.

(개발 시 데이터를 다루는 '모델' 클래스를 만들고, 뷰와 연동시켜야 한다.)

델리게이트라는 개념을 통해 데이터가 View에서 보여지는 형태를 조금 더 세밀히 조정할 수도 있다.

 

어느 쪽이든 본인이 개발하는 소프트웨어에 가장 적합한 것을 선택하는 것이 현명한 방법이다.

다음번 포스팅에서는 QTreeWidget을 사용하는 방법에 대해 알아보도록 하겠다.

 

 

참고 : Qt4를 이용한 C++GUI 프로그래밍 제2판

 

해상도, DPI, PPI 개념

해상도란

해상도(解像度, 문화어: 도형표시, 화면해상도, display resolution) 또는 선명도(鮮明度) 또는 화질(畵質, resolution)은 종이나 스크린 등에 표현된 그림이나 글씨 따위가 표현된 섬세함의 정도를 나타내는 말이다.

 

보통 1인치(25.4mm) 안에 표현되는 화소(Pixel)나 점(Dot)의 수로 해상도를 표현한다. 널리 쓰이는 단위로는 DPI PPI가 있다.

DPI(dots per inch)는 주로 인쇄 출력물에, PPI(pixels per inch)는 화상 출력물에 각각 쓰인다.

 

한편 텔레비전, 디지털 카메라와 컴퓨터, 스마트폰 등의 모니터의 경우에는 전체 픽셀의 수가 관용적으로 해상도의 의미로 쓰인다.

텔레비전에서의 해상도는 화소라는 단위(CRT에서는 본(line)이라고도 함)를 쓰고 있으며 화면에 흑백 라인이 총 몇개가 표시되는 지를 기준으로 수평x수직 화소(Symbol:px)로 표기한다. 예) 1920 * 1080 모니터

 

DPI란 (Dots per Inch)

1인치(inch) 안에 들어가는 점의 갯수를 의미한다. 주로 인쇄물에서 사용되는 용어이다. DPI가 높을 수록, 이미지가 선명해진다.(같은 공간 안에 더 많은 점이 들어가므로)

예) 72 DPI란, 1인치 안에 점이 72개 있다는 의미이다.

예) 6x4인치 사진을 인화하는데 300DPI로 인화하려면 어느 정도 픽셀 수가 필요할까?

가로 : 6 inch x 300 DPI = 1800 px,

세로 : 4 inch x 300 DPI = 1200 px

결국, 1800x1200 픽셀 이상의 사진이면 된다. 

 

PPI란 (Pixels Per Inch)

1인치(inch) 안에 들어가는 픽셀의 개수로, 디지털 이미지 파일나 컴퓨터 모니터, 텔레비전 등에서 사용된다.

예) 360 PPI란, 1인치에 픽셀이 360개 들어가있다는 의미이다.

 

DPI와 PPI는 엄밀히는 다른 용어이나, 실제로는 혼용해서 쓰는 듯 하다. (화상 출력물에 DPI 단위를 쓰는 등..)

 

Pixel(픽셀) 이란

픽셀(Pixel) = Picture(그림) + Element(원소)의 줄인 말.

픽셀(화소)은 사각형의 점으로, 디스플레이(화면)의 이미지를 구성하는 최소 단위를 의미한다.

스마트폰이나 모니터, TV 화면에 나타나는 이미지는 수많은 픽셀들이 모자이크처럼 집합해 하나의 큰 이미지를 형성하여 표현된 것으로, 픽셀 수가 많을 수록 더 디테일한 이미지 표현이 가능하다.

 

하나의 픽셀은 빛의 3원색인 R, G, B 값을 표현하는 서브 픽셀(부분화소)들로 이루어져 있다. 픽셀은 각 서브 픽셀들이 표현하는 빛의 양과 색의 조합을 통해 다양한 색을 표현한다.

계산

모니터에서의 DPI or PPI

모니터의 가로가 1920px이고, 길이는 20inch인 경우, 해상도는 96 PPI가 된다.

이미지 파일에서의 DPI or PPI

예를 들어, 1600 × 900 픽셀의 이미지가 있다고 해보자. (가로 1600 픽셀 * 세로 900 픽셀 = 전체 1,440,000픽셀)

- 해상도 100 DPI를 적용하면, 가로 16인치, 세로 9인치인 이미지 파일이 된다.

- 해상도를 200 DPI 로 바꾸면, 가로 8인치, 세로 4.5인치로 작아진다.

 

따라서, 같은 1600 * 900 픽셀의 이미지라도 픽셀 정보만으로는 해상도를 알 수 없고, 물리적인 크기가 같이 주어져야 해상도의 높/낮음을 알 수 있다.

(동일한 이미지를 크게 인쇄할수록 화질(해상도)이 떨어지고, 작게 인쇄할수록 화질이 높아진다.)

 

 

참고 : 

https://ko.wikipedia.org/wiki/%ED%95%B4%EC%83%81%EB%8F%84

https://en.wikipedia.org/wiki/Pixel_density

https://news.samsungdisplay.com/17578/

https://namu.wiki/w/%ED%94%BD%EC%85%80

HTTP, HTTPS, SSL, TLS 등 개념 

쉽게 풀어 말하자면,

웹에서 서버와 클라이언트가 정보를 주고받을 수 있도록 하는 전송 규약 HTTP이다.

그런데, HTTP로 통신 시 데이터를 중간에 감청하거나 변조하는 보안 문제가 발생하게 되었다. 이에 서버-클라이언트 간의 통신을 암호화하여 송수신 함으로써 보안 문제를 해결하기 위해 나온 전송 규약 HTTPS이다(HTTP의 암호화된 버전). 

 

HTTPS 통신을 위해서는 디지털 인증서 TLS라는 기술이 필요하다.

 

디지털 인증서란, 암호화 키쌍을 저장한 파일이다. 공개키가 담긴 인증서와, 개인키 파일로 구성되며, 이를 사용하여 통신 내용을 암호화/복호화 할 수 있다.

디지털 인증서는 개인이 만들 수도 있지만, 이로는 통신 내용을 암호화 할 수 있을 뿐, 내가 나임을 입증할 수가 없다.(예를 들면, 내가 진짜 '우리은행' 사이트가 맞고 가짜 우리은행이 아니라는 것을 입증을 할 수가 없음)

 

이에, 진짜 내가 맞다는 것을 입증하기 위해 공인된 인증기관(CA; Certificate Authority)로부터 인증서를 발급받는다.(제 3자가 확인/보장하는 것. 비용을 지불하고..) 이것이 SSL 인증서이다.

SSL 인증서에는 암호화 통신을 위한 공개키와, 사이트 정보 및 소유자 정보, 인증서의 만료일, 인증서 발행 기관(CA)의 정보가 들어 있기 때문에 SSL 인증서가 설치된 사이트를 믿고 통신을 할 수 있게 된다. 한국에서는 개인정보를 취급하는 사이트에는 SSL 인증서를 필수로 적용하도록 법으로 강제되어 있다.

 

또한 SSL 인증서로 암호화 통신을 할 수 있게 해주는 기술 TLS이다(SSL은 TLS의 구 명칭). 일단은 이런게 있구나, 정도만 알고 넘어가자. 

SSL 인증서

브라우저 또는 사용자의 컴퓨터와 서버 또는 웹사이트 간에 암호화된 연결을 수립하는 데 사용되는 디지털 인증서

인증서에 포함되는 내용

암호화 통신을 위한 공개키, 사이트 정보 및 소유자 정보, 인증서의 만료일, 인증서 발행 기관(CA)의 정보, 암호화 통신을 복호화 하기 위한 개인키

기능

1. 정보 유출 방지

통신 내용을 암호화하여, 통신 내용이 공격자에게 노출되는 것을 방지한다.

 

2. 정보 위변조 방지

통신 내용을 암호화하여, 내용의 악의적인 변경을 방지할 수 있다.

 

3. 위조사이트 방지(Phishing 방지)

인증서 발급기관(CA)에서 사이트 소유자를 확인하기에, 위조 사이트가 아닌 정상적인 사이트임을 신뢰할 수 있다.

서버에 SSL 인증서 설치하기

1. SSL 인증서 구매

SSL 인증서를 판매하는 곳은 많이 있다.(Sectigo, Symantec, Digicert 등) 국내에서는 대표적으로 "Cafe24의 보안서버 SSL 인증서" 구매를 통해 Sectigo SSL 인증서를 구매할 수 있다.

2. 서버에 SSL 인증서 설치

이하는 Cafe24에서 보안서버 SSL 인증서를 구매한 경우 + Apache 웹서버를 운영하는 경우를 가정하여 설명한다.

Case 1. Cafe24에서 웹호스팅을 받고 있는 경우

: 무료 설치가 된다.

Case 2. 웹 호스팅이 아닌 다른 호스팅 서비스를 이용하거나, 타사에서 호스팅을 하는 경우

인증서를 다운받아서 본인의 서버에 직접 올려주어야 한다. 과정은 아래와 같다.

 

1) 인증서 다운로드

나의 서비스 관리 > 인증서 관리 > 인증서 받기 메뉴에서 인증서 설치에 필요한 파일들을 다운로드 한다.

설치에 필요한 파일은 개인키, 인증서, 중개자 인증서(인증서 발행 기관의 인증서)이다.

  • ssl.key : 개인키
  • ssl.crt : 인증서
  • chain_ssl.crt : 중개자 인증서

2) 다운로드한 인증서를 서버에 복사

서버에 폴더를 만들어, 다운로드한 인증서를 해당 위치에 복사한다.

예) etc/pki/tls

 

Tip! 개인키 비밀번호 제거

개인키에 비밀번호가 걸려 있는 경우 Apache 웹 서버를 실행할 때마다 비밀번호를 입력해 주어야 한다. 자동으로 서버를 재시작해야 하는 경우가 있다면 그때마다 수동으로 비밀번호를 입력해 줄 수 없으니.. 개인키에 걸린 비밀번호를 제거한 키파일을 만들어 올려 놓으면 편하다.

다만, 개인키 파일에 비밀번호가 사라진 만큼 키파일이 노출되지 않도록 보안에 신경써야 한다. 

[root@www]# openssl rsa -in ssl.key -out ssl_nopass.key
Enter pass phrase for ssl.key: [비밀번호 입력]
writing RSA key
[root@www]# [완료]

Tip! 인증서 만료 날짜 확인

openssl x509 -in server.crt -noout -dates

 

3) httpd.conf 파일을 아래와 같이 수정

NameVirtualHost 127.0.0.1:443

<VirtualHost 127.0.0.1:443>
    SSLEngine on
    # 본인의 인증서가 저장된 경로 및 파일 설정
    SSLCertificateFile /etc/pki/tls/ssl.crt  
    SSLCertificateKeyFile /etc/pki/tls/ssl_nopass.key
    SSLCertificateChainFile /etc/pki/tls/chain_ssl.crt
</VirtualHost>

# NameVirtualHost 127.0.0.1:443와 VirtualHost의 아이피 주소 127.0.0.1을 본인의 아이피 주소로 변경해서 사용

3. http를 https로 리다이렉션 설정

혹시 http로 접속해오는 사람들이 있는 경우를 대비하여, http로 접속 시 https로 리다이렉트 하도록 설정해준다. 

1) 아파치 모듈 중 mod_rewrite 모듈 활성화

CentOS 7에서는 mod_rewirte가 기본적으로 활성화 되어 있다. 혹시 안되어 있는 경우, 아래와 같이 활성화 한다.

# 아래 명령어로 00-base.conf 파일을 연다
# vi /etc/httpd/conf.modules.d/00-base.conf

파일이 열리면 다음 줄을 추가하거나, 주석 처리를 제거한다.

LoadModule rewrite_module modules/mod_rewrite.so

2) httpd.conf 파일에 리다이렉션 룰을 추가 (아래 내용을 추가)

<Directory "${SRVROOT}/htdocs/www">
    <IfModule mod_rewrite.c>
        # for SSL redirect http to https
        RewriteCond %{HTTPS} != on
        RewriteRule ^(.*)$ https://%{HTTP_HOST} /$1 [R,L]
    </IfModule>
</Directory>

4. Apache 재시작

apache 설정을 바꾸어 주었으므로, 재시작하여 반영한다.

5. https로 접속이 되는지 확인 및 링크 수정

http를 https로 바꾸어 접속이 되는지 확인한다.

또한, 웹사이트 내에서 링크가 걸려 있는 경우, 링크 경로의 http를 https로 모두 바꾸어준다.


용어 정의

HTTP (Hypertext Transfer Protocol)

상에서 정보를 주고받을 수 있는 어플리케이션 레이어 프로토콜(전송 규약) 이다. 주로 HTML 문서를 주고받는데 사용된다.
HTTP는 클라이언트와 서버 사이에 이루어지는 요청/응답(request/response) 프로토콜이다. 예를 들면, 클라이언트인 웹 브라우저가 HTTP를 통하여 서버로부터 웹페이지(HTML)나 그림 정보를 요청하면, 서버는 이 요청에 응답하여 필요한 정보를 해당 사용자에게 전달하게 된다. 이 정보가 모니터와 같은 출력 장치를 통해 사용자에게 나타나는 것이다. HTTP를 통해 전달되는 자료는 http:로 시작하는 URL(인터넷 주소)로 조회할 수 있다.

출처 : https://ko.wikipedia.org/wiki/HTTP

 

HTTPS (HTTP Secure)

HTTPS (HTTP Secure) 는 HTTP protocol의 암호화된 버전이다. 이것은 대개 클라이언트와 서버 간의 모든 커뮤니케이션을 암호화 하기 위하여 SSL 이나 TLS을 사용한다. 이 커넥션은 클라이언트가 민감한 정보를 서버와 안전하게 주고받도록 해준다. 예를들면 금융 활동 이나 온라인 쇼핑이 있을 수 있다.

출처 : https://developer.mozilla.org/ko/docs/Glossary/https

SSL (Secure Sockets Layer)

Secure Sockets Layer(SSL)는 클라이언트와 서버 간의 안전한 링크를 통해 송수신되는 모든 데이터를 안전하게 보장하는 과거의 보안 표준 기술이었다. SSL 버전 3.0은 Netscape가 1999년에 발표했으며 현재에는 Transport Layer Security (TLS) 로 대체되었다. 

출처 : https://developer.mozilla.org/ko/docs/Glossary/SSL

TLS (Transport Layer Security) 전송 계층 보안

SSL의 새로운 명칭. 컴퓨터 네트워크상에서 통신 보안을 제공하기 위해 설계된 암호 규약이다.
이메일, 웹 브라우징, 메세징, 그리고 다른 프로토콜들의 감청을 통한 정보의 변형을 방지한다. SSL과 TLS 모두 네트워크 상에서 보안을 제공하는 cryptographic 프로토콜을 사용한 클라이언트 / 서버 프로토콜이다. 서버와 클라이언트가 TLS로 통신을 할때, 어떠한 제 3자도 메세지를 변형시키거나 감청할 수 없도록 한다.

출처 :

https://developer.mozilla.org/ko/docs/Glossary/TLS

https://en.wikipedia.org/wiki/Transport_Layer_Security

 

 

 

 

 

 

 

데이터베이스(DB; Database) 란?

여러 응용시스템들의 통합된 정보들을 저장하여 공유하고 운영할 수 있는 데이터의 집합체

특징

  • 통합성(Integrated Data) : 중복의 최소화 통합 데이터
  • 저장성(Stored Data) : 접근 가능한 형태의 시스템에 저장
  • 공유성(Shared Data) : 데이터의 여러 시스템 간 공유 가능
  • 운영성(Operational Data) : 조직 시스템의 기능 수행

데이터베이스 시스템(DBS; Database System) 이란?

데이터베이스를 관리하여 필요한 정보를 활용할 수 있도록 자동화한 시스템을 의미한다.
데이터베이스 시스템은 데이터베이스(DB), 데이터베이스 관리 시스템(DBMS), 사람과 시스템 간의 인터페이스를 제공하는 언어, 데이터를 사용하고 관리하는 사용자 및 관리자, 물리적인 하드웨어를 포함하는 개념이다.

데이터베이스 관리 시스템(DBMS; Database Management System) 이란?

응용프로그램과 데이터베이스의 중재자 역할을 통해, 응용 프로그램이 데이터를 공유할 수 있도록 데이터베이스를 관리해 주는 소프트웨어이다.

1. 데이터베이스 언어

데이터베이스 관리 시스템은 데이터베이스에 접근하여 원하는 정보를 얻고자 할 경우, 데이터 정의 언어(DDL), 데이터 조작 언어(DML), 데이터 제어 언어(DCL)을 통해서 처리한다.

1) 데이터 정의 언어(DDL; Data Definition Language)

DDL은 데이터베이스를 정의하거나 변경의 목적으로 사용된다. Create, Drop, Alter 등의 명령어가 있으며, 주로 DBA가 사용한다.

2) 데이터 조작 언어(DML; Data Manipulation Language)

DML은 사용자가 생성된 데이터베이스의 정보를 검색·삽입·삭제·수정 등의 처리를 수행할 목적으로 사용되며, 사용자와 데이터베이스의 인터페이스를 제공한다.

Select, Update, Delete, Insert 등의 명령어를 통해 데이터를 조작할 수 있는 언어이다.

3) 데이터 제어 언어(DCL; Data Control Language)

DCL은 데이터베이스를 제어관리하기 위한 목적으로 사용된다. 허가받지 않은 사용자로부터 데이터를 보호하기 위한 보안, 데이터 무결성, 시스템 장애 식 회복, 동시 접근 시 병행정 제어를 위한 명령어로 Grant, Revoke, Commit, Rollback, Set등이 있으며, 주로 DBA가 사용하는 언어이다.

2. 사용자

데이터베이스 시스템을 이용하는 사용자에는 일반 사용자(end user), 응용 프로그래머, 데이터베이스 관리자(DBA; Database Administrator)등이 있다.

1) 데이터베이스 관리자(DBA; DataBase Administrator)란?

데이터베이스의 원활한 기능을 수행하기 위해 데이터베이스 구성 및 관리운영 전반에 대한 책임을 지고 직무를 수행하는 사람을 말한다. DBA는 데이터베이스 설계 및 구축, 데이터베이스 운영관리, 데이터베이스 튜닝을 수행한다.

 

출처 : 데이터베이스 시스템 (장경애 저)

'DB' 카테고리의 다른 글

[DB] MariaDB 보안 설정  (0) 2024.09.27
[MariaDB] root 패스워드 리셋 방법  (0) 2023.07.19
[DB] MariaDB 인코딩  (0) 2023.02.10
[SQLite] SQLite DB 한계  (0) 2022.04.03
[InnoDB] cannot allocate memory for the buffer pool 에러  (0) 2022.03.22

DateInterval 클래스를 사용하면 편하다.

방법 1. DateTime 클래스의 diff() 함수 사용

diff 결과를 가지는 DateInterval 클래스가 리턴됨

<?php
$d1 = new DateTime('2021-12-01 03:04:39');
$d2 = new DateTime('now');

echo "d1 : " . $d1->format('Y-m-d H:i:s') . "\n";
echo "d2 : " . $d2->format('Y-m-d H:i:s') . "\n";

// Method 1 : DateTime->diff() 
$diff = $d1->diff($d2);
print_r($diff);
echo "days : " . $diff->days;
?>

방법 2. date_diff( 함수 사용

역시 diff 결과를 가지는 DateInterval 클래스가 리턴됨

<?php
$d1 = new DateTime('2021-12-01 03:04:39');
$d2 = new DateTime('now');

echo "d1 : " . $d1->format('Y-m-d H:i:s') . "\n";
echo "d2 : " . $d2->format('Y-m-d H:i:s') . "\n";

// Method 2 : date_diff()
$interval = date_diff($d1, $d2);
print_r($interval);
echo "days : " . $interval->days;
?>

+ Recent posts