GCJ, 리눅스, SWT가 협력하여 자바 UI 문제를 해결하는 방법
Kirk
Vogen I/T 전문가, IBM Global Services 2002년 4월
자바 언어는 서버측과 애플릿 분야에서 뚜렷한 발전을 이룩했지만, 최종 사용자
애플리케이션에 있어서는 그렇지 못했다. 리눅스, GNU Compiler for Java 및 Standard
Widget Toolkit을 사용하면 자바 언어로 프로그래밍된 신속한 원시 GUI 애플리케이션을 개발할 수
있다. (Windows도 머지 않았다). 베테랑 자바 개발자인 Kirk Vogen이 그 방법을
보여준다.
우리 모두는 자바 언어가 서버와 애플릿 분야에서 큰 성공을 거두었다는
것을 알고 있지만, 최종 사용자 애플리케이션 분야에서는 왜 큰 평판을 얻지 못했을까? 여기에는 몇 가지 이유가
있다. 먼저, 심지어는 작은 애플리케이션에서조차 수 메가바이트의 메모리를 차지하곤 한다. 둘째, 자바 언어와 함께 제공되는 GUI 라이브러리는 종종 원시 언어로 구축된
애플리케이션과 달라 보이는 애플리케이션을 만들어낸다. 따라서 여러분의 애플리케이션이 아무리 강력하고
안정적이다 하더라도 원시 애플리케이션에 비해 어색하고 서툴러 보이는 모습이 눈에 띈다
GNU Compiler for
Java
메모리 용량 문제로 시작해 보자. 가상 머신은 자바 바이트코드를 실행시킬 때 수많은 "작업"을 수행해야
하기 때문에 자바 애플리케이션은 추가적인 메모리를 사용한다. 오늘날의 고급 컴파일러에서 컴파일은 적시에
이루어지고 컴파일러는 다음에 사용하기 위해 정보를 급히 캐시에 저장해야 한다. 분명히 요즈음 메모리가 저렴하기는
하지만, 실행중인 자바 애플리케이션이 여러 개라면 대형 머신이라도 지속적인 메모리 페이징에 의해 느려질 수
있다. GNU Compiler for Java (GCJ)을 생각해 보자. GCJ는 자바 소스나 바이트 코드를
취해 이들을 원시 머신 코드로 컴파일한다. 여러 자바 클래스에서 나온 머신 코드들은 하나의 원시 애플리케이션으로
함께 결합될 수 있다.
지난 1월 Martyn Honeyford는 원시 컴파일과 GCJ에 대해 소개하였다 (참고자료).
예제를 통해 보여주었듯이, 원시 컴파일된 애플리케이션이 가상 머신에서 실행되는 애플리케이션보다 훨씬 적은
메모리를 사용하는 경우가 많았다.
Standard Widget
Toolkit
이것은 아주 훌륭하지만, Martyn이 지적했듯이 GCJ는 아직 AWT나 Swing을 지원하지 않는다.
그러면 우리는 원시 컴파일된 GUI 애플리케이션을 어떻게 만들 것인가? Standard Widget
Toolkit (SWT)을 생각해 보자. 이 API는 오픈 소스 Eclipse 툴 플랫폼의 일부이다. Swing
대 SWT 전쟁을 일으킬 위험을 무릅쓰고 (참고자료),
SWT의 몇 가지 이점을 설명하겠다.
SWT는 AWT과 Swing의 단점을 벌충하려고 시도한다. AWT에서 우리는 최소 공분모 제한을 가진다.
모든 플랫폼에서 존재하는 widget만 지원된다. 따라서, Motif는 원시 트리 widget을 제공하지 않지만
Windows는 제공하기 때문에 AWT는 트리 widget을 포함하지 않는다.
윈도우에서의 GCJ
윈도우에서의 GCJ는 아직 실현되지 않았다. 대소문자를 구별하지 않는 파일 시스템 (예 :
FAT와 NTFS)과 관련된 GCJ의 버그 때문에 GCJ는 현재 윈도우에서 실행되지 않는다. 그러나
훌륭한 차기 기능이 GCJ 3.1에서 등장해야 한다. 리눅스를 사용하여 여러분은 리눅스상에서 여러분의
코드를 컴파일하고 윈도우 바이너리도 만들어 내는 크로스 컴파일러로 GCJ를 설정할 수 있을 것이다.
GCJ 3.1은 2002년 4월 15일 출시를 목표로 하고 있다. 최신 진척 사항을 보려면 GCJ
홈페이지와 메일링 리스트 (참고자료)를
보기 바란다. |
Swing은 다른 극단으로 간다. 훌륭한 API를 갖추고 멋지게 설계되었지만 Swing은 자체적으로
widget을 구현한다. 따라서 Swing은 widget을 제공하기 위해 운영 체제에 의존하지 않는다. 이 점은
원시 지원과 관계없이 Swing에게 믿을 수 없을 정도의 유연성을 준다. 그러나 Swing이 직접 이들
widget들을 작성하기 때문에 결과로 나오는 모습은 종종 원시 애플리케이션과 눈에 띄게 다르다.
SWT은 이들 두 GUI 툴킷 간의 차이를 메우려고 시도한다. SWT의 진격 명령은 "원시 widget이
있다면 이를 사용하라. 없다면 이를 에뮬레이트하라"이다. 이 예가 앞에서 언급한 트리 widget이다. 윈도우는
원시 tree widget을 지원하기 때문에 SWT가 윈도우에서 실행될 때는 이를 사용한다. 그러나 Motif는
트리 widget을 지원하지 않기 때문에 Motif 하에서 실행할 때는 SWT가 자체적인 widget 버전을
작성한다. WST를 사용한 결과로 나온 애플리케이션은 원시 애플리케이션과 아주 유사한 모습인데, 원시
widget이 가능한 한 많이 사용되기 때문이다.
개발 환경 구성하기
그렇다면 여러분은 이 모든 것을 원시적으로 어떻게 컴파일할 것인가? 우선 GCJ를 갖춘 개발 환경 구성이
필요하다. GCJ는 리눅스와 다른 UNIX 변종 (윈도우에서의 GCJ의 상태에 대해서는 보조자료 "윈도우에서의
GCJ"를 참조한다)에서 지원된다. 개발 환경을 구성하는 가장 쉬운 방법은 리눅스 배포판 중 하나의 최신 버전을
설치하는 것이다. Mandrake 8.1와 Red Hat 7.2는 모두 구입 즉시 설치되는 GCJ 3.0.1과
함께 제공된다. GCJ는 아직 발전하고 있는 단계이기 때문에 여러분은 최신의, 그리고 가장 훌륭한 버전 사용을
고려하고 싶을 수 있다. 나는 버전 3.0.3과 3.0.4에서 테스트하였다. GCJ를 수작업으로 설치하는 것은
비교적 쉽다. GCJ 페이지는 훌륭한 문서를 제공한다. (참고자료)
일단 여러분이 GCJ를 갖춘 환경을 가지게 되면 Eclipse SDK를 다운로드받아 설치한다. 그러면
여러분은 SWT 소스와 바이트 코드를 얻게 된다. Eclipse SDK를 설치하려면 ZIP 파일 (Eclipse
SDK라는 제목을 가진)을 다운로드받고 한 디렉토리에 압축을 풀면 된다. 나는
/usr/local/eclipse을 추천하지만, 어떤 디렉토리라도 괜찮을 것이다.
컴파일
시작하기
개발 환경을 구성한 후에 컴파일을 시작할 수 있다. 애플리케이션 컴파일은 대부분의 SWT 소스에서 다음과
유사한 명령문을 실행시킨다.
gcj -c MyClass.java -o MyClass.o
|
SWT 소스 파일 중 약 30개가 자바 원시 인터페이스 (JNI)를 사용한다. 따라서 우리는 이들을 약간
다르게 컴파일해야 한다. (-fjni 플래그에 주목):
gcj -fjni -c MyJNIClass.java -o MyJNIClass.o
|
마지막으로, 결과로 나온 객체 파일들을 하나의 공유된 객체로 컴파일해야 한다.
gcj -shared -o swt.so MyClass.o MyJNIClass.o ...
|
SWT 구축하기
이 글에서 우리는 SWT를 하나의 공유된 객체로 컴파일하고 이 객체를 우리의 결과 애플리케이션에서 동적으로
참조할 것이다. 여러분은 SWT를 여러분의 코드와 함께 실행 파일로 컴파일
할 수 있다는 점에 주의해야 하지만 이 글의 범위상 우리는 공유 객체에 집중할 것이다. 자신의 이름이 가리키듯이
공유된 객체는 공유가 가능하다는 주요 이점을 가지고 있다. 애플리케이션들은 모두 런타임시에 동일한 객체를
동적으로 사용할 수 있다. 그 결과 나오는 실행 파일의 사이즈는 훨씬 작을 것이다.
소스 코드 다운로드판에 포함되어 있는 Ant Buildfile을 사용하여 우리는 SWT를 자동으로 컴파일할
것이다. 이 파일은 두 개의 주요 사항을 가지고 있다. 우선, SWT 소스 코드에 두 개의 패치를 적용한다 (몇
개의 작은 컴파일러 에러 때문에 세 개의 SWT 파일이 GCJ와 컴파일되지 않을 것이다. 예를 들어,
int x, y, x1, y1 를 한 행에 두려 하지 않고 여러 개의 행으로 나누어야
한다. 이 경우 이 세 소스 파일들을 기능적으로 동일하게 만들기 위한 작은 패치가 적용된다. 앞에서 언급했듯이,
GCJ는 아직 발전중이다. 이들 버그는 시간이 지남에 따라 없어져야 한다). 둘째, buildfile은 제공된
makefile을 사용하여 make 를 호출한다.
Listing 1에서 보여지듯이, Ant buildfile은 Ant 1.3과 Ant 1.4.1에서
테스트되었다. 아래 Listing에서는 공간에 여유를 두기 위해 주석을 삭제했음에 주의한다. 그러나
다운로드판에서는 완전한 코드를 이용할 수 있다.
구축판을 실행시키려면 다음 단계들을 따른다.
- Ant를 설치한다. (Ant에 대한 추가 정보는 참고자료
참조)
- 이 글의 소스
파일을 다운로드받고 한 디렉토리에 압축을 푼다. 소스 파일에는 buildfile, 패치 및 관련
makefile이 들어 있다.
- SWT 디렉토리를 항해하고
ant 를 입력한다. buildfile은 여러분이
usr/local/eclipse에 Eclipse를 설치했다고 가정한다. 여러분이 다른 디렉토리에
Eclipse를 설치했다면 ant
-Declipse_install_dir=your_directory 를 입력하는데, 여기서
your_directory 에 여러분이 Eclipse를 설치한 디렉토리를 지정한다.
주의: Ant buildfile을 사용하고 싶지 않다면 SWT 소스 코드와 바이트코드의 압축을
수작업으로 풀고 소스 코드에 들어 있는 세 개의 패치를 Unix 패치 명령어로 적용한다
(.patch 파일을 이용하여). 그리고 makefile을 실행시킨다.
Listing 1. Ant buildfile
<project name="BuildEclipse" default="cleanup">
<!--
The following properties can be overridden at the command-line.
e.g. ant -Declipse_install_dir=/usr/local/eclipse
-->
<property name="eclipse_install_dir" value="/usr/local/eclipse"/>
<property name="temp_dir" value="build_temp"/>
<property name="shared_object_name" value="swt.so"/>
<target name="init">
<mkdir dir="${temp_dir}"/>
</target>
<target name="unpack" depends="init">
<unzip src="${eclipse_install_dir}/plugins/org.eclipse.swt/swtsrc.zip"
dest="${temp_dir}"/>
<unjar src="${eclipse_install_dir}/plugins/org.eclipse.swt/swt.jar"
dest="${temp_dir}"/>
</target>
<target name="patch" depends="unpack">
<patch patchfile="TabFolder.patch"
originalfile="${temp_dir}/org/eclipse/swt/widgets/TabFolder.java"/>
<patch patchfile="Widget.patch"
originalfile="${temp_dir}/org/eclipse/swt/widgets/Widget.java"/>
<patch patchfile="TreeEditor.patch"/>
<move file="TreeEditor.java" todir="${temp_dir}/org/eclipse/swt/custom"/>
</target>
<target name="make" depends="patch">
<execon executable="touch">
<fileset dir="${temp_dir}" includes="**/*.java" excludes="**/*.class"/>
</execon>
<execon executable="touch">
<fileset dir="${temp_dir}" includes="**/*.class" excludes="**/*.java"/>
</execon>
<copy file="Makefile" todir="${temp_dir}"/>
<exec executable="make" dir="${temp_dir}">
<arg line="-f Makefile"/>
</exec>
</target>
<target name="cleanup" depends="make">
<move file="${temp_dir}/${shared_object_name}" todir="."/>
<delete dir="${temp_dir}"/>
</target>
</project>
|
애플리케이션
생성하기
이제 여러분은 SWT를 공유된 객체로 구축했기 때문에 샘플 애플리케이션 사용을 시도해 볼 수 있다.
Listing 2에 나와 있는 애플리케이션은 고전적인 "Hello, World!" 메시지를 표시하는 간단한
창으로 구성되어 있다.
Listing 2. Hello, World 샘플 애플리케이션
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.RowLayout;
public class Hello {
public static void main(String[] args) {
Display display = new Display();
final Shell shell = new Shell(display);
RowLayout layout = new RowLayout();
layout.justify = true;
layout.pack = true;
shell.setLayout(layout);
shell.setText("Hello, World!");
Label label = new Label(shell, SWT.CENTER);
label.setText("Hello, World!");
shell.pack();
shell.open ();
while (!shell.isDisposed()) {
if (!display.readAndDispatch()) display.sleep ();
}
display.dispose ()
|
이 애플리케이션을 컴파일하려면 다음 단계들을 따른다.
- 여러분의 SWT 공유 객체 (swt.so)를 Hello World 디렉토리에 복사하고 다음 명령문을
입력한다. (/usr/local/eclipse외의 디렉토리에 Eclipse SDK 를 입력했다면 잊지 말고
이를 여러분의 디렉토리명으로 바꾼다.)
gcj -CLASSPATH=/usr/local/eclipse/plugins/org.eclipse.swt/swt.jar
-c Hello.java -o Hello.o
gcj -main=Hello -o Hello Hello.o swt.so
|
- 다음 명령문을 사용하여 여러분의 라이브러리 경로를 설정한다 (여러분이 bash 셀을 사용하고 있다고
가정하고).
export LD_LIBRARY_PATH=
.:/usr/local/eclipse:/usr/local/eclipse/plugins/org.eclipse.swt/ws/motif
|
./Hello 를 입력하고, 원시 컴파일된 GUI 자바 애플리케이션이 여러분
화면에 등장하는 것을 지켜본다.
이 글의 소스
코드는 이 절차를 자동화하는 makefile을 포함하고 있다.
전망
윈도우 애플리케이션의 컴파일이 거의 현실화되고 있고 (보조자료 "윈도우에서의 GCJ" 참조), SWT가
현재 리눅스/Motif 및 윈도우에서만 지원되고 있지만 SWT를 리눅스 상의 GTK/GNOME에 포팅하려는
활발한 개발 작업이 진행 중이다. SWT는 또한 AIX와 Solaris 상의 Motif에서도 작동하지만,
광범위하게 테스트되지 않았다. 마지막으로, Windows CE, QNX 및 매킨토시 포트에 대해서도 일부 작업이
진행 중이지만, 현재 시점에서 주요 포커스는 다른 플랫폼들에 주어지고 있는 것으로 보인다. 다양한 SWT 포트의
진척 사항을 보려면 참고자료를
참조한다.
결론
우리 모두는 자바 언어가 일부 훌륭한 기능을 가지고 있음을 알고 있다. 자바는 멋지고 객체 지향 개념의
훌륭한 구현을 제공하며 매우 유용한 표준 클래스 라이브러리와 함께 제공된다. 이 이점들을 GCJ 및 SWT와
결합시키면 자바 언어는 서버와 웹 브라우저에서 자신의 토대를 구축하고 최종 사용자 애플리케이션 세계로 이행하도록
자리매김할 수 있다.
참고자료
|