Mono 데스크탑 응용 프로그램 개발 3가지 방법

클라이언트 응용 프로그램 또는 데스크탑 응용 프로그램을 Mono로 만든다면, 어떤 방법으로 만들면 좋을까? 이 세 가지 방법을 알아본다.

1. System.Windows.Forms

System.Windows.Forms(일명 윈폼-WinForm)으로 GDI(그래픽 장치 인터페이스(Graphics Device Interface)의 APIs를 객체지향적으로 제공하는 것을 일컷는다. 이제 대한 자세한 설명은 GDI+ 링크를 참고 하기 바란다.

그러나 Mono 환경에서 Windows Forms 개발은 사용자에게 좋은 사용자 인터페이스를 제공하지 못한다.

  1. virtual OnPaint(PaintEventArgs e) 가 적절하게 동작하지 않는다.
    화면의 창 사이즈를 변경할 때 새로 고침 영역(Update 영역)이 동작하지 않는다. 그래서 창에 아무 것도 표시가 되지 않는다. 창의 업데이트가 필요한데, void Update() 가 화면을 제대로 그려내지 못한다.



  2. 레티나(Retina) 디스플레이를 지원하지 못한다.
    레티나 디스플레이를 제공하지 못하기 때문에 이미지와 폰트가 투박스럽게 보인다. 레티나 디스플레이에서 레티나를 지원하지 않는 응용 프로그램은 사용자에게 그다지 사랑 받지 못하는 게 현실이다.

  3. 성능 문제
    Windows Forms 개발은 윈도우(Windows) 운영체제 환경이 아니라면 응용 프로그램의 Drawing 성능이 매우 느리다.

2. Gtk#

Gtk\#은 오픈 소스로 제공되는 그래픽 라이브러리를 C# 버전으로 제공한다. Gtk+와 Gnome 2 기반이며, 아직 Gnome 3 버전을 바인딩한 C# 버전은 제공하지 않는다.
Gtk#은 Windows Forms와 개념적으로 다른 부분이 많다. Gtk#에서는 Model & View 패턴을 자주 볼 수 있다. 예를 들어, Store 클래스에 따라 View가 달라지는데 ListStore냐, TreeStore냐에 따라 View를 다르게 랜더링 한다.

하지만, Gtk를 C# 버전으로 Adapting 한 Gtk#을 사용하려고 한다면 충분히 고려해야 할 것이다. Gtk#은 아직 해결되지 않은 버그들이 너무 많다. Gtk#으로 개발을 하고자 마음 먹었다면 개발 중에 막히더라도 인터넷을 검색하면 답변이 달리지 않은 questions들이 많다.

3. Glade#

Glade#은 Gtk#의 서브셋(Subset)이라고 한다, Glade#의 전신인 Glade을 사용하여 사용자 인터페이스를 디자인 할 수 있는 디자이너를 제공한다. Glade은 XML로 저장되고, 이를 MonoDevelop에서 Import해서 사용할 수 있다.

좋은 대안이 될 수 있겠지만, 단방향 import만 지원하기 때문에 prototype 개발에는 적합하지 않다고 생각한다.

결론

크로스 플랫폼에서 System.Windows.Forms.Form은 너무하다 싶을 정도로 윈도우에서 표현해 내는 것 만큼 표현하질 못한다. 특히 눈이 높은 맥 사용자라면 아마 쓰레기 취급을 할 지도 모르겠다.
그렇다면 Gtk 가 대안이 될 수 있는데, MonoDevelop 개발툴도 Gtk#을 이용하여 개발되었다. 때문에 MonoDevelop도 그래픽 표현에 버그가 종종 눈에 띈다. 그리고 pango# (pango-폰트 처리 라이브러리) 도 일부 플랫폼에서는 한글 처리를 완벽하게 표현하지 못한다.

응용 프로그램 개발에 Gtk#(Gtk가 아닌 Gtk#), pango(pango가 아닌 pango#)은 아직 믿고 사용하기 섣부르기는 하겠지만, 규모가 작은 응용 프로그램에서는 좋은 대안이 될 수 있다.

Posted by 땡초 POWERUMC

댓글을 달아 주세요

  1. 컴포지트 2014.03.21 09:21 Address Modify/Delete Reply

    모노가 닷넷 기술만 따라가는 게 문제가 아니고 지금 변화하는 컴퓨터와 소프트웨어 환경에 적응하고 따라가는 것까지 해야 하니 복병이 꽤나 많아 따라가질 못하나 봅니다...

프로그래밍은 언제나 숫자와의 경쟁인 것 같다. 반올림이 되느냐, 부동소수점이냐, 정수 오버플로우(integer overflow) 등은 백발이 되어 코드를 만질 때 까지 항상 따라다니는 문제가 될 것이다.

MySQL 날짜 관련 이슈

얼마 전 필자가 다니는 회사에서 발생한 데이터베이스 관련 이슈로 다음과 같은 문제가 발생하였다. 아래는 MySQL 관련 문제에 대하여 공유된 내용이다.

MySQL 5.6.4 부터 시간값 저장시 밀리세컨드를 지원한다. 하지만 DATETIME의 경우 길이가 6일 경우에만 가능하다. 그런데, DATETIME 타입(이는 DATETIME(4)와 같다)일 경우 밀리세컨드 부분을 반올림(round)하는 버그가 있다. #68760
이 버그로 인해 1999년 12월 31일 23시 59분 59초 500ms 같은 값을 DATETIME 타입에 입력하면 반올림되어 2000년 1월 1일 0시 0분 0초 0ms 가 돼 버리는 현상이 발생한다.

MySQL Community Server 의 C 언어 소스코드를 보면 날짜와 관련된 데이터를 다루는 세 가지가 데이터 타입이 있다. MYSQL_TYPE_DATE, MYSQL_TYPE_DATETIME, MYSQL_TYPE_TIMESTAMP.

// MySQL 데이터 타입  
/*****************/  
#define CLIENT_MULTI_QUERIES    CLIENT_MULTI_STATEMENTS      
#define FIELD_TYPE_DECIMAL     MYSQL_TYPE_DECIMAL  
#define FIELD_TYPE_NEWDECIMAL  MYSQL_TYPE_NEWDECIMAL  
#define FIELD_TYPE_TINY        MYSQL_TYPE_TINY  
#define FIELD_TYPE_SHORT       MYSQL_TYPE_SHORT  
#define FIELD_TYPE_LONG        MYSQL_TYPE_LONG  
#define FIELD_TYPE_FLOAT       MYSQL_TYPE_FLOAT  
#define FIELD_TYPE_DOUBLE      MYSQL_TYPE_DOUBLE  
#define FIELD_TYPE_NULL        MYSQL_TYPE_NULL  
#define FIELD_TYPE_TIMESTAMP   MYSQL_TYPE_TIMESTAMP  
#define FIELD_TYPE_LONGLONG    MYSQL_TYPE_LONGLONG  
#define FIELD_TYPE_INT24       MYSQL_TYPE_INT24  
#define FIELD_TYPE_DATE        MYSQL_TYPE_DATE  
#define FIELD_TYPE_TIME        MYSQL_TYPE_TIME  
#define FIELD_TYPE_DATETIME    MYSQL_TYPE_DATETIME  
#define FIELD_TYPE_YEAR        MYSQL_TYPE_YEAR  
#define FIELD_TYPE_NEWDATE     MYSQL_TYPE_NEWDATE  
#define FIELD_TYPE_ENUM        MYSQL_TYPE_ENUM  
#define FIELD_TYPE_SET         MYSQL_TYPE_SET  
#define FIELD_TYPE_TINY_BLOB   MYSQL_TYPE_TINY_BLOB  
#define FIELD_TYPE_MEDIUM_BLOB MYSQL_TYPE_MEDIUM_BLOB  
#define FIELD_TYPE_LONG_BLOB   MYSQL_TYPE_LONG_BLOB  
#define FIELD_TYPE_BLOB        MYSQL_TYPE_BLOB  
#define FIELD_TYPE_VAR_STRING  MYSQL_TYPE_VAR_STRING  
#define FIELD_TYPE_STRING      MYSQL_TYPE_STRING  
#define FIELD_TYPE_CHAR        MYSQL_TYPE_TINY  
#define FIELD_TYPE_INTERVAL    MYSQL_TYPE_ENUM  
#define FIELD_TYPE_GEOMETRY    MYSQL_TYPE_GEOMETRY  
#define FIELD_TYPE_BIT         MYSQL_TYPE_BIT  

날짜가 포함되는 데이터 타입에 대해 코드를 분석해 본 결과 날짜 데이터를 조작하여 저장하는 코드를 찾지 못했다. 꼼꼼하게 분석한 것은 아니기 때문에 혹시 놓친 부분도 있을 거라고 생각한다.

MySQL Server에서 문제를 발견하지 못했다면,다음 봐야 할 것이 MySQL Client와 MySQL Connector/J 소스 코드인데, MySQL Connector/J 에서 이슈와 직/간접적으로 관련된 코드를 발견하였다.

MySQL Connector/J

MySQL Connector/J의 소스 코드에서 /src/com/mysql/jdbc/TimeUtil.java 파일에서 한 가지 이슈를 재연할 수 있는 코드를 발견했다. 메서드 이름  public static Time changeTimezone(…) 메서드를 살펴보면 클라이언트와 서버 간에 TimeZone이 다른 경우 이를 보정하는 연산이 포함된다.

다음은 TimeZone.java의 코드의 일부분이다.

Calendar fromCal = Calendar.getInstance(fromTz);  
fromCal.setTime(tstamp);  

int fromOffset = fromCal.get(Calendar.ZONE_OFFSET)
               + fromCal.get(Calendar.DST_OFFSET);  
Calendar toCal = Calendar.getInstance(toTz);  
toCal.setTime(tstamp);  

int toOffset = toCal.get(Calebndar.ZONE_OFFSET)
              + toCal.get(Calendar.DST_OFFSET);  
int offsetDiff = fromOffset - toOffset;  
long toTime = toCal.getTime().getTime();  

if (rollForward || (conn.isServerTzUTC() && !conn.isClientTzUTC())) {  
    toTime += offsetDiff;  
} else {  
    toTime -= offsetDiff;  
}  

Timestamp changedTimestamp = new Timestamp(toTime);  

만약, 클라이언트와 서버 사이에 0.001ms 시간 차이가 생긴다면, TimeZone은 13이라는 값의 차이가 발생한다. 클라이언트와 서버의 시간 차이 기준은 MySQL Server에서 열린 세션(session)을 기준으로 판단한다. 혹시나 클라이언트와 서버의 TimeZone이 상이하게 설정된 경우 어마 어마한 시간 차이가 생기게 된다.

다음과 같이 필자가 의도적으로 0.001ms 차이가 발생하는 경우 MySQL Connector/J 가 DateTime을 교정하는 것을 확인하였다. (단, getTimeFast 메서드는 MySQL Connector/J 에 포함된 메서드임)

Calendar calendar = Calendar.getInstance();  
calendar.set(1999, 12, 31, 23, 59, 59);  
calendar.setTimeInMillis(nanos);  

System.out.println(getTimeFast(0, bits, 12, 14, calendar, tz, true));  

// output  
2000-01-01 00:00:00.0  

결론

MySQL 날짜 관련 이슈의 직접적인 발생 원인은 찾지 못했다. 하지만 MySQL Connector는 MySQL Client와 쌍으로 움직이니 간접적으로 영향을 미칠 수 있을 것이다.

현재까지 살펴본 바로는 가장 가능성이 있는 이 이슈/버그는 MySQL Connector/J와 MySQL Client일 가능성이 가장 크고, MySQL Server 내부 버그일 가능성은 다소 낮다고 본다. 이 이슈/버그 밑에 코멘트에 MySQL .NET Connector를 사용하는 경우도 문제가 있다고 하니.. 어쨌거나 저쨋거나..

Posted by 땡초 POWERUMC

댓글을 달아 주세요