Qt를 한 번 이라도 해본 사람은 아마도 많이 느꼈을 것이다. Qt에 도전하는 것이 그 어떤 것보다도 쉬웠음을 말이다. 이 연재는 Qt에 관심이 있거나 Qt 프로그램을 시작하려는 사람들에게 초점이 맞춰져 있다. 그러나 그 끝은 Qt의 고수라는 호칭과 함께 마무리 될 것이다. Qt의 전반적인 내용보다는 소스 위주와 관련 팁으로 꾸려나갈 것이며 이 연재로 인해 독자들이 Qt를 조금이나마 알 수 있는 기회가 되길 바란다.
프로그래머는 지적 호기심을 가지고 새로운 지식(언어, 패러다임)을 언제든지 수용할 수 있고, 자기 계발을 충분히 즐길 수 있는 사람이라 말한다면 억지라고 할 수 있을까? 필자 스스로 프로그래머라고 부를 수 있을 정도에 이를 만큼의 긴 경력을 갖고 있지는 못하지만 이 직업이야 말로 다른 어떤 직업보다도 새로움에 적응하고 미래를 예측하는 능력이 절실히 필요하다고 생각한다. 많은 사람들이 프로그래머란 직업에 흥미를 갖고 도전하려 한다. 하지만 그 중에 많은 사람들이 프로그래머로서의 정체성을 의심하는 것 또한 사실이다.
한 가지의 프로그램 언어만 확실히 알아도 다른 프로그램 언어를 쉽게 사용할 수 있다는 말을 선배 개발자로부터 종종 듣게 된다. 쉽게 사용할 수 있다는 말은 어떻게 보면 익히는데 그 만큼의 시간이 덜 걸린다는 말이지 다른 언어를 사용하는 것 자체가 습득 절차 없이 모국어를 쓰듯 사용할 수 있다는 것을 의미하진 않을 것이다. 한 시스템에서 다른 시스템으로 이식한다고 했을 때 적지 않은 시간이 소요된다는 사실은 이를 분명하게 증명해 준다. 한 시스템에서 프로그램을 개발하고 그 프로그램을 다른 언어를 사용하는 시스템으로 이식하는 일은 프로그래머로서 단순한 작업에 지나지 않는다. 그래서 필자는 항상 꿈을 꾸곤 했다. A라는 환경에서 작업한 프로그램을 B라는 환경에서 별다른 작업을 하지 않고 그대로 소스를 가져올 수 있다면, 그것도 네이티브하게 가져올 수 있다면(자바처럼 중간에 인터프리터가 있는 경우가 아니라 해당 머신에 최적화된 컴파일을 할 수 있다면) 얼마나 환상적일까 하는 상상을 했다. Qt를 만나기 전까지만 해도 이것은 머리 속에서만 그려지는 이상이었다. 하지만 Qt는 이상을 현실로의 이행을 쉽게 가져올 수 있는 계기를 마련해 주었다.
독자들이 이 기사를 읽고 있다면 대부분이 프로그래머이거나 프로그래머를 꿈꾸는 사람들 중에 하나일 것이다. 독자들이 필자와 같은 꿈을 꾼 적이 있다면(한 시스템에서 다른 시스템으로 포팅을 해본 경험이 있는 프로그래머라면) 이번 글이 흥미로울 것이다. Qt에 대한 소개로 이번 연재를 시작하고자 한다. Qt를 처음 접하거나 관심 정도에 머물렀던 독자들은 이 글이 필수적일 테지만 중급 혹은 고급 프로그래머들은 다소 따분한 연재일지도 모르겠다. 하지만 Qt의 철학을 알 수 있다는 점에서 한번 훑어본다는 생각으로 읽어주었으면 좋겠다.






<화면 1> Qt를 사용한 애플리케이션
Qt 설치하기
앞에서도 언급했다시피 Qt는 다양한 플랫폼을 지원한다. 여기에선 가장 많이 쓰이는 두 가지 플랫폼(윈도우와 리눅스)에서 설치하는 방법을 간단히 설명할 것이다. 그 외의 플랫폼은 이 두 플랫폼과 성격이 유사하므로 배제하겠다.
윈도우에서 Qt 설치
윈도우용 Qt는 QPL 정책으로 풀려있지 않은 상태이다. 따라서 상업적인 제품을 개발하려고자 한다면 라이선스를 얻어야 하지만 비 상업적인 용도로 사용한다면 버전은 낮지만 트롤테크 홈페이지에서 다운 받을 수 있다(ftp://ftp.trolltech.com/qt/non-commercial/QtWin230-NonCommercial.exe).
이 버전은 다른 버전과 달리 Qt 소스를 포함하고 있지 않다. 그리고 아쉽게도 볼랜드 C++은 지원하지 않는다. Qt 라이브러리, Qt 디자이너, tmake, 예제 프로그램으로 구성되어 있다. 설치하는 법은 일반 윈도우 애플리케이션 설치 방법과 동일하다.
비주얼 스튜디오 환경에서 Qt를 사용하려면 몇 가지 작업이 필요하다. Qt를 애드인(add-in)으로 넣는 과정으로 Tools-Customize 다이얼로그를 실행한 후, Add-ins and Macro Files 탭에서 QMsDev Developer Studio-Add-In을 추가시켜야 한다. 이제 비주얼 스튜디오에서 Qt를 사용할 준비는 다 된 셈이다. 애드인이 설치되면 <화면 2>와 같은 플로팅 툴바가 하나 보일 것이다.

<화면 2> Qt 플로팅 툴바
◆ Generate Qt Project : .pro 프로젝트 파일을 VS의 dsp 파일로 변환시켜 준다.
◆ New Qt Dialog : 디자이너에서 수정할 수 있는 Qt 다이얼로그를 생성한다. 이 다이얼로그는 .pro 프로젝트 파일에 자동으로 추가된다.
◆ Qt Designer : 디자이너를 실행한다(디자이너에 대한 설명은 다음 호에 있을 예정이다)
리눅스에서 Qt 설치
먼저 Qt는 http://www.trolltech.com/download/qt/x11.html에서 얻을 수 있다. 다운받은 Qt를 아무 디렉토리에 푼다. 필자는 /home 디렉토리에 qt라는 디렉토리를 만들고 그곳에 풀었다. 리눅스는 윈도우와 달리 몇 개의 환경변수를 직접 세팅해 줘야 한다. 먼저 필자가 .bash_profile에 작성한 것을 보자.
PATH=$QTDIR/bin:$PATH
MANPATH=$QTDIR/doc/man:$MANPATH
LD_LIBRARY_PATH=$QTDIR/lib:$LD_LIBRARY_PATH
추가되는 환경변수로 $QTDIR이 있다. 이 환경변수가 세팅되어 있지 않으면 Qt 컴파일 자체가 안 되는 것에 유의하자. 다운받은 QT를 풀면 생기는 폴더가 $QTDIR이 된다. QT를 구지 /usr/local에 설치할 필요는 없다. 대부분의 경우 /usr/local은 ‘/’ 파티션으로 잡혀 있어서 리눅스를 다시 설치하게 되면 QT도 다시 설치해야 한다.
그러나 대부분의 리눅스 유저들이 ‘/home‘ 파티션을 따로 잡을거라 생각한다. 이 경우 /home에 QT를 설치하면 리눅스를 다시 설치하는 일이 발생해도 QT는 그 자리에 남아있게 된다. 그러나 설치한 리눅스의 gcc나 중요 라이브러리들의 메이저 업그레이드가 되었다면 컴파일만 다시 한번 해주는 수고가 따를 것이다.
LD_LIBRARY_PATH는 라이브러리 로딩 시에 찾는 PATH이다. 만약 이 패스를 설정하기 싫다면 /etc/ld.so.conf를 열어 그 곳에 라이브러리 패스를 넣어 줘도 된다. /etc/ld.so.conf에 패스를 넣어 주면 ldconfig를 한번 실행해야 한다(물론 root로). 이렇게 환경변수를 세팅해 놓으면 QT 컴파일 준비 완료이다.
리눅스에서 Qt 컴파일
그럼 먼저 configure 옵션을 살펴보자.
# ./configure
이와 같이 아무런 옵션 없이 configure를 할 경우 다음과 같은 옵션들이 정의되었다는 메시지가 나온다. 이 경우 쓰레드 및 gif 등이 컴파일 모듈에서 빠지게 된다. 이렇게 되면 QThread나 gif 관련된 것을 사용할 수 없게 된다.이를 사용하기 위해서는 ./configure -thread -qt-gif라고 해야 한다. 기본 값은 릴리즈로 컴파일된다. QT를 디버깅하고 싶다면 -debug를 넣어야 한다. 만약 라이브러리 용량을 줄이고 싶다면 ./configure --help를 실행하여 help 내용을 보고, 모듈을 삭제 또는 추가해야 한다.
STL support ......... yes
Thread support ...... no
NIS support ......... yes
CUPS support ........ yes
Large File support .. partial
GIF support ......... no
MNG support ......... plugin (qt)
JPEG support ........ plugin (qt)
PNG support ......... yes (qt)
zlib support ........ yes
OpenGL support ...... no
NAS sound support ... no
Session management .. yes
Xinerama support .... yes
Tablet support ...... no
XRender support ..... yes
Xft support ......... yes
XKB Support ......... yes
./configure가 끝났다면 make라고 치고 커피 한잔 마시고 오면 컴파일이 다 되어 있을 것이다. 여기서 잠깐 알아야 할 것이 있다. QT 컴파일의 최종 목적을 $QTDIR/lib에 있는 QT 라이브러리를 얻기 위함이다. 그런데 QT는 라이브러리를 만들고 나서 example 디렉토리에 있는 예제들을 컴파일하기 시작한다. 이 example 폴더의 컴파일 시간이 꽤 길다.
example 폴더를 컴파일 하지 않기 위해서는 ./configure 후에 example 폴더를 example_ 로 바꾸면 example_ 폴더를 컴파일하지 않는다. 물론 ‘successful'이라는 결과가 나오지는 않지만 example 폴더를 제외한 나머지 필요한 다른 것들은 정상적으로 컴파일을 해서 라이브러리까지 만드는 작업까지 끝낸 상태가 된다.
이런 방법을 쓰면 “example 폴더를 찾을 수 없습니다.” 라는 메시지가 나올 것이다. 그러면 성공적으로 QT 컴파일을 한 것이다. example는 나중에 필요한 것만 컴파일 하면 된다. 이 방법이 찝찝한 사람은 하지 않아도 된다.
Qt 디렉토리 소개
$QTDIR로 이동 후 'ls'를 해 보면 여러 디렉토리들이 나올 것이다. 여기서 알아둬야 할 디렉토리를 몇 가지 설명하겠다.
◆ include : Qt의 모든 헤더 파일이 이 디렉토리 밑에 링크 되어있다. 만약에 삼바를 통하여 QT 디렉토리에 접근한다면 오픈한 파일을 쓰기(Write)해선 안된다. 쓰기를 하면 링크가 파일 자체로 바뀌기 때문에 두개의 헤더 파일이 존재하게 된다. 따라서 주의하기 바란다.
◆ src : Qt 소스가 들어있는 곳이다. 패치를 할 경우 이곳에서 하며, 패치 후 make할 때도 $QTDIR/src에서 make하면 된다.
◆ src/moc : src 중에서도 moc라는 디렉토리는 Qt 소스가 아닌 Mete-Object_compiler 소스이다. 다음 단락에서 moc를 설명하겠다.
◆ mkspecs : 이곳에는 플랫폼에 대한 스펙들이 들어 있다. 뒤에서 설명할 qmake도 이곳을 참조하여 Makefile를 만든다.
◆ tools/designer/uic : 다음 연재 때 소개할 디자이너에서 만들어지는 ui 파일을 컴파일하는 uic의 소스가 있는 디렉토리이다.
프로젝트 파일 만들기
Qt에서는 프로젝트 파일(.pro)을 이용하여 컴파일한다. 윈도우의 경우 이 파일을 이용하여 dsp 파일을 만들고, 리눅스의 경우 Makefile을 생성한다. 하나의 프로그램을 만들 때 가장 먼저 하는 작업이므로 기본적으로 알아야 할 사항을 언급하겠다.
TEMPLATE = app
SOURCES = hello.cpp
HEADERS = hello.h
CONFIG += qt warn_on release
앞의 프로젝트 파일은 가장 기본적인 프로파일이다. TEMPLATE은 기본적인 프로그램 성격을 가리킨다. app는 일반 애플리케이션을 말한다. 이 외에 lib는 라이브러리를 만들 때 사용한다(동적, 정적 라이브러리 모두를 포함한다). SOURCES는 프로그램에서 구현한 소스 파일들을 포함한다. 하나의 파일의 경우엔 문제가 없지만 대부분의 프로그램은 다수의 소스파일들을 포함한다. 이 경우엔 다음과 같이 사용하는 것이 편하다.
main.cpp
HEADERS는 프로그램에서 필요한 헤더 파일들을 포함한다. CONFIG에는 =가 아닌 +=를 사용했음에 주의할 필요가 있다. 그 이유는 configuration 옵션이 이미 있을 수도 있고, = 표시는 기존의 값을 대체하기 때문에 +=를 사용하는 편이 낫다.
qt는 프로그램이 Qt를 사용하고 있음을 나타낸다. 이것은 링크할 때나 컴파일 할 때 인클루드 패스의 필요에 따라 Qt 라이브러리를 가져올 수 있음을 의미한다. warn_on은 컴파일할 때 warning을 출력하도록 하는 부분이고 (↔warn_off) release는 제품을 출시할 때 사용하는 옵션이다. 일반적으로 개발할 땐 debug 옵션으로 해야 디버깅을 할 수 있다. 그 밖에 staticlib 옵션을 사용하면(TEMPLATE = lib 로 하였을 때) 정적 라이브러리가 만들어 지고, 아무 옵션도 주지 않으면 동적 라이브러리가 생성된다.
pro파일이 다 만들었으면 Makefile을 만들 차례이다. 여기에선 $QTDIR/example/hello 프로젝트를 이용해 보겠다. VS 환경에서는 Generate Qt Project를 이용해서 dsp 파일로 변환해주면 된다. 리눅스 환경에서는 qmake를 이용하여 다음과 같이 Makefile을 만든다.
'Hello World' 예제 컴파일
프로그램 언어를 시작할 때 항상 나오는 것이 Hello World(전체 소스 파일은 ‘이달의 디스켓’ 참조)이다. 소개를 어느 정도 마치고 나서 Hello World가 나오지 않으면 이상할 정도로 표준이 되어버렸다. 이 프로그램은 한 개의 버튼이 Hello World와 Hello Qt를 번갈아 가면서 디스플레이 하도록 하는 간단하면서도 Qt의 핵심 개념을 알아볼 수 있는 예제이다. 예전엔 프로그램 언어에 레퍼런스라는게 많지 않았다. 하지만 요즘엔 레퍼런스가 너무 잘 되어 있어 책을 참고할 일이 많이 줄어든 것이 사실이다. Qt도 여러 차례 레퍼런스에 대하여 극찬을 받은 적도 있고 상을 수상한 적도 있을 정도로 훌륭한 레퍼런스를 갖고 있다. 위의 프로그램에서 하나하나 설명을 하지 않을 것이다. 설명하는 것보다 레퍼런스를 찾아서 한 번 읽어 보는 것이 훨씬 도움이 많이 될 것이다.

<화면 3> Hello World

<화면 4> Hello Qt
<화면 3>과 <화면 4>는 Hello World 소스의 실행결과이다. 먼저 <화면 6>은 처음 실행시 윈도우 모양이고, 왼쪽 하단 버튼을 클릭하면 <화면 4>와 같이 된다. 또는 ‘Alt + H' 단축키를 이용하여 버튼 클릭을 대신할 수 있다.
소스 컴파일
hello.pro파일을 qmake를 하면 Makefile이 만들어 지고, 후에 make 하면 컴파일이 된다. 아주 간단하다.
# qmake -o Makefile hello.pro
# make
하지만 make하는 과정을 살펴보기 위해 방법 2를 보자.
# moc moc_hello.cpp hello.h
# g++ -c hello.cpp -I/home/qt/qt-x11-free-3.1.0/include
# g++ -c main.cpp -I/home/qt/qt-x11-free-3.1.0/include
# g++ -c moc_hello.cpp -I/home/qt/qt-x11-free-3.1.0/include
# g++ -o hello hello.o main.o moc_hello.o
-Wl,-rpath,/home/qt/qt-x11-free-3.1.0/lib -L/home/qt/qt-x11-free-3.1.0/lib -lqt
여기서 말하고자 하는 것은 moc이다. 자 그럼 이제 moc에 대해 알아보자. 그런데 moc를 알기 위해선 시그널과 슬롯에 대해 알아야 한다.
시그널과 슬롯
<그림 1>은 시그널과 슬롯에 관한 내용을 그림으로 설명해 놓은 것이다. 그림을 보면서 다음 내용을 이해하기 바란다.

<그림 1> 시그날과 슬롯
시그널과 슬롯을 구지 MFC와 비교하여 설명 한다면 이벤트와 이벤트를 받는 함수와 비슷하다. MFC와 QT를 많이 다뤄본 사람은 이 말이 틀리다고 말 할지도 모른다. 그러나 이렇게 설명하는 것이 필자가 생각한 것 중 가장 적절한 표현이라 생각한다. 예를 들자면, 키보드의 키가 눌린 것은 시그널이고 그 키에 대한 어떠한 표현은 슬롯이라 말할 수 있다. 명칭이 익숙하지 않아 좀 어색할지 모르지만 조금만 사용해 보면 너무도 쉬운 것이 될 것이다. 다음은 시그널과 슬롯의 사용방법과 주의 사항이다.
② 시그널과 슬롯을 정의하려는 모든 클래스는 QObject 클래스에서 파생되어야 한다.
③ 시그널과 슬롯을 선언하는 클래스 안에는 반드시 Q_OBJECT라는 매크로를 포함하고 있어야 한다.
④ 시그널의 정의는 ‘signals:’ 키워드를 사용하고, 슬롯은 public slots, private slots 등으로 정의해야 한다. 다만 정적(static)으로는 만들 수 없다.
⑤ 직접 작성한 시그널을 보내고 싶다면 emit라는 키워드를 사용한다.
⑥ 시그널과 슬롯의 매개 변수 개수는 같아야 한다.
⑦ 시그널과 슬롯의 연결은 QObject::connect() 메쏘드를 통해 이루어진다.
⑧ 시그널과 슬롯의 이름은 connect() 메쏘드안의 SIGNAL(), SLOT()의 인자로 들어간다.
⑨ 여러 개의 슬롯이 하나의 시그널에 연결될 수 있고 그 반대로도 연결할 수 있다.
사용 방법은 이와 같다. 예제에서 사용된 문장을 보면 hello.h 파일에서 Q_OBJECT 매크로가 있으며, 한 개의 시그널과 2개의 슬롯을 정의하고 있다. 이 시그널과 슬롯의 사용은 hello.cpp에서 사용하고 있으며, 생성자에서 connect() 메쏘드를 이용하여 연결시켜준다. connect안의 인자를 보면 다음과 같이 되어 있다.
<리스트 1>를 자세히 보면 의심이 가는 곳이 있을 것이다. 바로 첫 번째 connect()의 'clicked()' 시그널이 무엇인지 궁금할 것이다. 이 'clicked()'은 QPushButton 클래스의 부모인 QButton 클래스의 시그널로 정의 되어 있다. 이것은 QT 내부에서 가지고 있는 시그널이며, 버튼을 클릭 했을 때 발생하는 시그널이다.
MOC
자, 이제 시그널과 슬롯에 대하여 조금 알았으니 방법 2에서 말한 moc를 알아보자. moc는 $QTDIR/bin에 있으며, 전 단락에서 설명한 것과 같이 $QTDIR/src/moc에 소스가 있다. 컴파일 과정을 보면 moc는 헤더파일을 파싱하여 cpp파일을 만들고 그것을 다시 g++ 컴파일러가 컴파일 하여 object 파일을 만든다. 최종적으로 링크 시에는 이 object 파일과 같이 링크를 해줘야 시그널과 슬롯을 연결하여 사용할 수 있으며, 에러 없이 컴파일이 된다. 방법 2는 moc를 설명하기 위한 과정이었고, 다음부터는 방법 1로 컴파일을 할 것이다(moc가 파싱할 때 “#if 0과 같은 전처리 구문으로 막아놓은 곳을 제외하지 않고 파싱한다. 즉, #if 전처리 구문을 moc가 파싱할 때 무시하고 처리하므로 유의해야 한다).
예제)

이 경우에 _USE_DOC_를 정의하지 않고 사용한다면 에러가 발생한다. moc가 헤더파일에 #ifdef _USE_DOC_를 무시하기 때문이다(“-Wl, -rpath, $DIRECTORY” 옵션은 어떤 특정한 프로그램이 컴파일될 때 실시간으로 라이브러리의 패스를 정해주는 역할을 한다).


이제 Qt 중급 프로그래머!
Qt의 장점을 가능한 부각시키면서도 Qt를 처음으로 접하는 사람들에게 쉽게 접근하기 위해 설명하려고 노력했다. ‘시작이 반이다.’라는 글과 함께 연재를 시작했는데 독자들이 느끼기에는 반을 넘어서 새로운 도전으로 받아들여 줬으면 하는 바람이다. 다음 연재에서는 Qt에서 사용하는 컨트롤들을 자세히 다루어볼 생각이다. 독자들이 많이 요구하는 컨트롤에 대하여 자세히 설명했으면 한다. 그러기 위해서는 독자 여러분들의 의견이 있었으면 한다.
아쉽게도 검색엔진에서 Qt라고 치면, 전혀 엉뚱한 내용만 나오는 것을 확인할 수 있다. 이는 개발자 층이 폭넓지 않은 이유가 가장 크겠지만, 개발자들을 모을 수 있는 구심점 역할을 하는 커뮤니티의 부재가 더 큰 문제라고 생각한다. 유일하게 Q&A로 간신히(!) 커뮤니티를 유지하는 곳이 바로 Qt 센터(http://www.qtcenter.co.kr)이다. QT 개발자로서 이는 참으로 안타깝다고 할 수 밖에 없는 상황이다. 이 연재를 통해서 많은 QT 개발자들이 하나로 뭉쳤으면 하는 바람을 가지고 이 사이트를 소개해 본다. 이번 연재의 내용 중 궁금한 사항이나 옳지 않았던 내용이 있으면 QA 게시판에 글을 올려주길 바란다. 성심성의껏 답변하도록 최선을 다하겠다.@