• "빌더"나 도메인 특화 언어(domain specific language, DSL) 스타일의 라이브러리 : 패널이나 폼을 구축하고 컴포넌트를 수작업으로 추가하는 일 정도를 꼼수로 빠르게 처리할 수 있다. 이를 위해 스윙과의 상호 작용을 더 루비스럽게 해 주는 몇몇 라이브러리가 있다. 예를 들면, 다음과 같은 것이 있다. 이러한 두 접근법은 패널, 폼, 수공예 코드를 사용한 레이아웃을 전제로 한다. 스윙 코드를 직접 사용하는 것과 비교하여 개선된 것은 분명하지만, 복잡한 사용자 인터페이스를 다루기에는 여전히 버거워 고생을 감수해야 한다.

  • “자바 클래스가 어디에서 왔든 상관하지 않겠다”는 접근법 : 세 번째 접근법은 그저 루비로 스윙 코드를 쉽게 작성하려는 시도를 접고 스윙 객체를 생성하는 컴파일된 자바 코드가 이미 있다는 가정에서 시작한다.
  • 마지막에 언급한 접근법이 바로 Monkeybars 라이브러리에서 취하는 방법이다. 몇 개의 매우 우수하면서도, 마음껏 쓸 수 있는 비주얼 스윙 UI 레이아웃 에디터가 있다. 앞서 언급한 (폭스나 GTK 등) GUI 툴킷을 사용하면서, 이따금 대화 상자를 띄우려고 UI 에디터를 쓰지는 않는다. 그러나 이 이상의 작업을 하면서 이 같은 도구를 무시할 수는 없을 것이다. 이러한 도구를 외면하면서까지 섬세한 데스크톱 애플리케이션의 UI 코드를 손으로 작성할 당위성은 어디에도 없다.

     

    Monkeybars

     

    Monkeybars 프로젝트는 어느 제품 개발 과정에서 Happy Camper Studios의 David Koontz에 의해 나왔고, 테스트가 쉽고 유지 보수가 가능하며 복잡한 루비 데스크톱 애플리케이션을 생성하려는 경험을 바탕으로 계속 진화했다. 이 프로젝트는 활발히 관리되고 있으며 누구든 완전히 자유롭게 사용할 수 있다.

    Monkeybars는 기존 자바 스윙 클래스(스윙 UI를 정의하는 말 그대로 컴파일된 자바 클래스)를 루비 코드와 연결하는 오픈 소스 루비 라이브러리다. 이는 모델(Model), 뷰(View), 컨트롤러(Controller)의 MVC 디자인 패턴을 따른다. MVC는 뷰 로직과 UI 컴포넌트를 애플리케이션 로직으로부터 분리하는 데 목적이 있다.

    이는 자바 언어와 스윙 라이브러리를 사용하므로, Monkeybars는 성숙하고 안정된 기술을 바탕으로 개발되었다. JRuby를 위한 다른 현재의 스윙 라이브러리와는 달리 크고, 복잡하면서, 다중 패널을 가진 애플리케이션을 작성하기에도 적합하다. 나중에 보겠지만, Monkeybars 애플리케이션을 생성하면 몇 가지 오버헤드를 수반한다. 따라서 이는 간단한 폼 제작을 위한 최적의 해법은 아니다. 그러나 어느 정도의 복잡도를 요구하는 JRuby 데스크톱 애플리케이션을 작성한다면 합리적인 선택이 될 수 있다.

    SWT 대신 스윙을 사용한 이유는 무엇인가?

    Monkeybars는 다음 이유로 SWT(Standard Widget Toolkit) 대신 스윙을 사용하였다.

    • 스윙은 자바 개발 환경의 일부다. 자바 개발 환경을 갖춘 사용자라면 스윙도 갖고 있다.
    • 스윙은 컴포넌트의 동작에 대해 세밀하게 제어할 수 있다.
    • 스윙 컴포넌트는 컴포넌트가 어떻게 보일지에 대해 유연성을 더 많이 제공한다.

    Profligacy 같이 Monkeybars는 스윙 API를 감추지 않는다. 그럼에도, Monkeybars는 컴파일된 UI 클래스를 다루므로, 실제 레이아웃을 생성하기 위한 어느 도구나 애플리케이션을 최대한 활용할 수 있다. 애플리케이션의 복잡도에 따라, 루비 코드에서 무엇을 할지 알아내려고 어느 정도는 스윙 컴포넌트 API 문서와 코드 예제를 참조하는 것은 불가피하다(그러나 루비와 자바 라이브러리 간의 깔끔한 통합 덕분에, 편리하게 재사용할 수 있도록 루비 API 내에 이러한 스윙 코드를 쉽게 감쌀 수 있다).

     

    Monkeybars를 써서 만든 프로그램은 임의의 복잡도를 가질 수 있지만, 몇 가지 기본 패턴을 따라 코드를 관리 가능한 수준으로 유지할 수 있다.

     

    Monkeybars가 MVC를 구현한 방식

     

    MVC 패턴은 여러 변종과 함께 오랜 역사를 갖고 있다. Monkeybars에서의 기본 전제는 각 스윙 프레임에 적용된 MVC 패턴이다(여기서 스윙 프레임이란 말하자면, 관련된 컴포넌트나 위젯을 잡고 있는 UI 객체다. 몇몇 경우에 이는 모델 패널일 수도 있다). 모델, 뷰, 컨트롤러에 해당하는 세 가지 루비 파일이 있다. 모델 클래스는 본질적인 비즈니스 로직을 갖고 애플리케이션에서 그 로직에 해당하는 데이터를 관리한다. 이러한 내용은 모델과 상호 작용하기 위한 수단으로 존재하는 뷰나 컨트롤러 코드와는 무관해야 한다. 뷰와 컨트롤러를 모델과 독립적으로 유지하는 것이 개발하고 테스트하기에 쉽다.

     

    뷰는 컴파일된 스윙 코드를 포함하는 특정 자바 클래스를 참조하기 위한 또 다른 루비 파일이다. 뷰는 스윙 컴포넌트와 모델 데이터 간의 상호 작용을 관장한다. 뷰는 모델에 직접 접근할 수 있다. 그러나 데이터를 컨트롤러에 전달하는 수단으로 모델의 사본과도 동작한다. 모델은 이러한 두 가지 목적을 달성하려는 것이므로, 이 점이 모델 클래스를 디자인할 때 유념해야 할 사항이다. 모델의 원본 인스턴스는 장기간 상태를 유지하고 애플리케이션 로직을 제공한다. 뷰가 사용하는 사본은 본질적으로 데이터 접근용 객체이고 폐기 처분할 수 있다. 모델은 뷰가 사용하는 데이터 객체를 레퍼런스로 참조하도록 하여 상대적으로 사본 생성에 부담이 없어야 한다.

     

    뷰는 컨트롤러와 직접적으로 접촉하지 않는다. 대신, 신호를 줄 수 있는 시스템을 제공하여 컨트롤러와 뷰 간에 상호 작용을 추상화한다. 이와 같이 뷰와 컨트롤러 간에 꼬임을 방지하면 더 쉽게 테스트할 수 있다.

     

    이렇게 기술함으로써 여러 상세한 부분을 섬세하게 단순화하고 일부 생략하기도 한다. 이러한 구조 하에서 뷰와 컨트롤러가 친밀하게 서로 상호 작용한다. 하부구조에서는 이러한 동작을 조율할 수 있는 수단이 필요하다. Monkeybars의 목적은 개발자의 손을 묶는 것이 아니라 테스트 가능하고 관리 가능한 코드를 작성하도록 돕는 데 있다. 개발자는 적절하다고 생각되면 의도한 API일지라도 자유롭게 건너 뛸 수 있다.

    컨트롤러 클래스는 (버튼 클릭과 텍스트 입력 변화 등) 스윙 이벤트의 핸들러를 정의하고 모델의 상태를 제어하는 곳이다. 컨트롤러는 모델의 원본 인스턴스의 레퍼런스를 유지한다. 컨트롤러는 뷰와 직접 통신하지 않는다.

     

    컨트롤러가 뷰에서 데이터를 얻고자 할 때, 뷰는 현재 UI 내용물과 관계하는 모델의 사본을 넘겨 준다. 컨트롤러는 이 데이터를 갖고 모델의 원본 인스턴스를 갱신하거나 이러한 값에 따라 몇 가지 동작을 수행할지 결정할 수 있다. 컨트롤러는 뷰에서 스스로 값을 갱신하고 갱신된 값을 돌려 주도록 명령할 수도 있다. 예제 애플리케이션을 통해 이러한 내용이 어떻게 동작하는지 살펴 보겠다.





    Monkeybars를 사용한 JRuby 스윙 애플리케이션 예제

     

    Monkeybars를 써서 생성한 간단한 프로그램을 살펴 보고, 스윙과 루비를 써서 데스크톱 애플리케이션을 작성하는 감을 잡자(전체 예제 소스 코드의 링크는 다운로드 를 보라).

     

    시작하기

     

    시작에 앞서, 몇 가지 갖출 것이 있다.

    1. jruby-complete.jar의 사본을 받자.

    2. Monkeybars 라이브러리를 설치하라. 여느 젬과 같이 이 라이브러리를 설치할 수 있다.
      sudo gem install monkeybars

      gitorious.org의 저장소에서 현재 소스 코드를 얻을 수도 있다.

    3. 다음과 같이 rawr 젬을 설치한다.
      sudo gem install rawr

      엄밀히 말해, rawr 는 Monkeybars 애플리케이션 작성의 필수품은 아니다. 그러나 이것은 JRuby 애플리케이션을 실행 가능한 단일 JAR 파일로 변환하는 여러 유용한 rake 태스크를 제공한다. 본 문서의 예제에서 이를 사용한다.

    애플리케이션 기본

     

    예제 애플리케이션은 “카드”의 수를 정의한 텍스트 파일을 읽는 “플래시 카드” 프로그램이다. 이 애플리케이션은 종료할 때까지 루프를 돌며, 주기적으로 짧은 순간 동안 자신을 보였다가 감췄다가 한다. 기본적으로, 이는 학습을 위한 도구다. 이 예제에서 카드란 독일어 어휘와 구문 모음이다. 이 프로그램은 또한 카드 정의 파일의 위치와 (보임/숨김 속도, 창 크기 등) 몇 가지 세팅을 정의한 설정 파일을 읽기도 한다.

     

    이 예제의 목적은 다음과 같다.

    Monkeybars 애플리케이션 생성 스크립트 사용하기

     

    일단 설치되면, Monkeybars는 일련의 초기 애플리케이션 파일을 생성할 용도로 커맨드라인 스크립트를 제공한다. 새로운 Monkeybars 프로젝트를 시작하려면, 젬과 함께 설치된 monkeybars 스크립트를 실행한다. 프로젝트 이름은 monkey_see 라고 하자.

     

    $ monkeybars monkey_see

    이 스크립트는 주어진 경로(또는 애플리케이션 이름만 주어진 경우 현재 디렉터리)에 새 디렉터리를 생성하고 새 애플리케이션에서 사용할 핵심 파일과 여러 디렉터리를 추가한다.

     

    rawr 을 사용하여 코드를 자바 환경으로 부트스트랩하기

     

    rawr 은 Monkeybars에서 파생된 또 다른 루비 라이브러리다. 이것은 잡다한 패키징 작업을 수행한다. 또한, 이것은 Monkeybars 애플리케이션에서 사용할 기본 자바 클래스를 생성하기 위한 커맨드라인 스크립트를 제공한다. 이렇게 생성되는 기본 자바 클래스 덕분에 (애플리케이션을 JRuby를 통하여 루비 프로그램처럼 실행하지 않고) Monkeybars 애플리케이션을 자바 애플리케이션처럼 실행하게끔 한다.

    Monkeybars 애플리케이션에서 이것을 쓰려면, 프로젝트 디렉터리로 가서 rawr 스크립트를 실행한다.

     

    $ cd money_see; rawr install

     

    Monkeybars rake 태스크를 사용하여 파일 생성하기

     

    Monkeybars가 기능을 모델, 뷰, 컨트롤러로 각각 분할하는 방식을 이미 살펴 보았다. 관례적으로 이러한 파일은 한 디렉터리에 위치한다. 이러한 작업을 도우려고, Monkeybars는 이러한 파일을 생성하기 위한 목적으로 rake 태스크를 제공한다. 세 파일 중 하나 또는 (더 일반적인 경우로) 전체 파일 세트를 생성할 수 있다.

     

    $ rake generate ALL=src/flash

    이 명령은 “src/” 아래 “flash”라는 이름의 서브디렉터리를 생성하고 flash_controller.rb, flash_view.rb, flash_model.rb라는 파일 세 개를 그 서브디렉터리에 생성한다. 처음 두 파일은 기본 Monkeybars 클래스에서 상속한 껍데기 클래스를 갖는다. 모델 코드의 경우는 이와 같지 않다. Monkeybars는 애플리케이션 로직과 데이터를 어떻게 관리할지에 대한 어떠한 가정도 하지 않는다. 이는 전적으로 개발자의 몫이다.





    UI 생성하기

     

    애플리케이션 인터페이스를 구성하려면 플래시 카드 데이터를 보여 주는 스윙 클래스가 필요하다. 이 클래스를 어떻게 생성할지는 개발자의 몫이다. Monkeybars는 특정 UI 도구나 스윙 코드 생성기와 완전 독립적이다. 편의상, 스윙 파일을 관련 튜플(src/flash/FlashFrame.java)과 같은 디렉터리에 둔다. 여기서 개발자는 클래스 패키지 이름을 알아 둘 필요가 있다. 그래야, 뷰 클래스에 이것들을 전달할 수 있다(여기서는 flash 패키지 이름을 사용하고 클래스 이름은 FlashFrame 이다).

     

    이 애플리케이션의 화면 레이아웃은 그림 1과 같다.


    그림 1. 애플리케이션의 화면 레이아웃

     

    몇 가지 요점: 플래시 카드 내용물을 보여줄 목적으로 JTextPane 을 써서 HTML로 표시할 텍스트 형식을 지정하도록 한다. 텍스트 페인과 버튼에는 인식할 수 있는 이름을 부여해야 한다. 이렇게 이름을 붙여 두면 뷰를 다루어 UI 컴포넌트에 대해 뭔가 알고자 할 때 편리하다. 프로그램 코드는 루비에 있으므로, 루비의 메서드 이름 짓기 관례를 따르도록 하라. 텍스트 페인에는 card_pane 이라고 이름을 붙이고 두 메뉴 아이템에는 edit_menu_item 과 quit_menu_item 이라는 이름을 붙인다. 필요한 단축 키도 물론 등록하도록 한다.

     

    프레임 자체의 이름은 그리 중요하지 않다. 뷰 클래스는 이름으로 컴포넌트를 직접 참조할 수 있다.

     

    모델 정의

     

    모델은 애플리케이션 로직과 UI 뒤에 숨은 데이터를 관리한다. Monkeybars 프로그램은 일반적으로 각 자바 폼 별로 모델을 가진다. 예제 애플리케이션은 플래시 카드 데이터를 다루기 위해 단일 모델을 사용한다. 모델 코드는 알려진 위치에서 데이터를 로딩할 수 있어야 하고, 데이터에 접근할 수 있도록 퍼블릭 메서드를 제공할 수 있어야 한다.

     

    단순함을 위해, 애플리케이션이 실행되는 곳에서부터 데이터를 서브디렉터리 내의 텍스트 파일에 저장해 보자. 수작업 HTML보다는 텍스타일(textile) 마크업으로 작성하고 RedCloth 루비 라이브러리로 마크업을 변형할 수 있다. 각 카드 입력은 구분자 문자열로 구분한다.

     

    외부 라이브러리 사용하기

     

    텍스타일은 단순한 일반 텍스트 관례를 따라 HTML을 정의할 목적으로 사용되는 텍스트 마크업 형식이다. 예를 들어, <em>italicized</em> 으로 쓰려면, 대신 _italicized_ 라고 쓴다. RedCloth는 젬으로 설치할 수 있는 루비 라이브러리로, 텍스타일 형식의 텍스트를 HTML로 변환한다.

     

    Rubygems는 외부 라이브러리를 쉽게 설치하고 사용할 수 있도록 한다. 그러나 개발자는 코드를 JAR에 패키징하고 잠재적으로 이것을 배포할 것이므로, 모든 코드가 애플리케이션에 포함되었는지 확인해야 한다. 이를 위해, RedCloth 젬을 풀고 redcloth.rb 파일을 프로젝트의 “ruby/lib/” 디렉터리에 복사해야 한다.

     

    $ cd /tmp; gem unpack RedCloth

    이와 같이 하면, (젬 버전이 다르지 않는 한) “/tmp/RedCloth-3.0.4/”가 생긴다. “/tmp/RedCloth-3.0.4/lib/redcloth.rb”를 monkey_see 프로젝트의 “lib/ruby/” 디렉터리로 복사한다.

     

    일반적으로 어떠한 루비 라이브러리든 애플리케이션의 핵심 요소가 아닌 것은 (관례를 따라) “lib/ruby/” 아래에 위치한다. 젬을 사용하면, 실제 라이브러리 파일을 풀어 해당 프로젝트에 그 파일을 추가해야 한다. 본 글의 나중에, 프로그램에서 이러한 파일을 어떤 식으로 찾게끔 하는지 언급하겠다.

     

    주요 모델 메서드

     

    load_cards 메서드는 텍스트 파일을 디스크에서 읽어서 각 카드를 나누고 @card 인스턴스 변수에 결과 값을 할당한다.

     

    select_card 메서드는 임의로 카드를 선택하여 @current_card 인스턴스 변수에 이를 할당한다. attr_accessor 를 사용하여 이 변수를 읽고 쓰기 위한 메서드를 정의한다.

    이제 이를 정리하여 어떤 카드가 UI에서 보일지를 그 자리에서 편집할 수 있도록 한다. 편집이 끝나면 update_current_card 메서드가 @current_card 내용물을 취하여 @cards 배열에 다시 삽입한다. save 메서드는 @cards 배열을 디스크로 다시 쓴다.

    current_card 메서드의 값이 바로 화면에 그리고자 하는 것이다. 이렇게 하려면 뷰 클래스가 필요하다.

     

    뷰 클래스 정의하기

     

    Monkeybars 뷰 클래스는 자바 스윙 클래스의 소유자다. flash_view.rb 파일을 열어 보면, 이것이 set_java_class 라는 클래스 메서드를 호출한다는 것을 알 수 있다. 이 메서드는 뷰를 위해 정의한 스윙 클래스에 세팅해야 한다. 본 예제에서는 flash.FlashFrame 이 이에 해당한다.

    일반적으로, Monkeybars 뷰 클래스는 다음 세 가지 작업을 수행할 필요가 있다.

    데이터 매핑하기

     

    Monkeybars는 모델 메서드를 스윙 컨트롤에 엮는 map 메서드를 지원한다. 가장 간단한 사용 예는 다음과 같이 그저 UI 컴포넌트 메서드와 모델 메서드를 연결하는 것이다.

     

    map :view => :card_pane.text, :model => :current_card

    이러한 매핑은 직접적인 양방향 관계에 기반을 둔 기본 동작을 사용한다. 즉, card_pane 컴포넌트의 text 메서드의 결과는 모델의 current_card= 메서드로 전달된다. 뷰가 모델로부터 갱신되면, 프로세스는 반대가 된다. model.current_card 결과는 card_pane.text 로 전파된다(노트: JRuby는 루비/자바 이름 규칙을 다룬다. 따라서 실제 스윙 메서드인 setText 는 set_text= 로 호출할 수 있다).

     

    종종 이런 형태의 단순한 매핑 무난히 동작하기도 한다. 그러나 때때로 데이터 타입이나 포맷의 차이 또는 일부 애플리케이션 로직의 요구 사항으로 인해 직접적인 데이터 교환을 원치 않을 수도 있다.

     

    Monkeybars에서는 데이터 교환시 중개자를 사용할 수 있다. :using 매개변수(즉, 배열을 가리키는 해시 키)를 사용하여 매핑을 전달한다. 이 매개변수는 데이터를 모델에서 뷰로 또는 뷰에서 모델로 옮길 때 사용하는 대체 수단을 가리킨다( :using 을 사용하는 또 다른 이유는 스윙 컴포넌트의 값이나 상태가 일반적인 getProperty 와 setProperty 패턴을 따르지 않는 컴포넌트 메서드나 자식 객체를 다룰 필요가 있는 상황을 처리하는 것이다).

     

    이번 코드에서 card_pane text 속성에 값을 할당하기 전에 모델에서 읽어낸 텍스타일 형식의 문자열을 HTML로 먼저 변환하고자 한다. 이를 처리하기 위해 to_html 메서드를 생성하자. 마찬가지로 뷰에서 직접 모델의 current_card 값을 갱신하지 않도록 하자. 뷰에서도 카드를 편집하기 위한 특별 코드가 필요하다. 따라서 뷰에서 모델로 매핑하는(view-to-model) 메서드 이름이 있어야 할 자리에 nil 을 대신 사용하자.

     

    결과는 다음과 같은 매핑이 된다.

     

    map :view => :content_pane.text, :model => :current_card, :using => [:to_html, nil ]

    스윙 프레임이 특별한 방식으로 자신을 표현할 필요가 있을 수도 있다. 기본적으로, 스윙 프레임은 스크린에서 상단 좌측에 나타난다. 애플리케이션에서 이 프레임을 상단 우측 모서리에 보이기를 원할 수 있다. 또는 갑자기 나타났다가 사라지는 것이 아니라 부드러운 슬라이딩 효과를 주고 싶을 수도 있다.

     

    스윙 객체 관리하기

     

    뷰 클래스는 해당 스윙 클래스가 참조하는, @main_view_component 라는 이름의 특별한 인스턴스 변수를 갖는다. 뷰 코드는 이 객체를 통해 스윙 컴포넌트와 상호 작용한다. 플래시 카드의 텍스트 페인의 내용물을 변경하려면, 본 예에서는 다음과 같이 쓰도록 한다.

     

    @main_view_component.card_pane.text = "Some new text"

    그러나 뷰 클래스는 본질적으로 이러한 유형의 코드를 위해 존재한다. 따라서 Monkeybars가 @main_view_component 변수를 대신 처리하고, 개발자는 이 변수를 명시적으로 사용하지 않고 컴포넌트를 직접 참조할 수 있다.

     

    card_pane.text = "Some new text"

    기본 Monkeybars::View 클래스는 method_missing 을 사용하여 이러한 코드를 가로채고, 이것이 컴포넌트 참조인지 확인하여 이 요청을 @main_view_component 에서 대리하도록 한다.

    스윙 프레임에 대한 명시적인 참조가 없이도, 다음과 같이 메서드를 호출할 수도 있다.

     

    @main_view_component.width = 500

    부드러운 슬라이딩 효과를 얻기 위해, 뷰 클래스는 스윙 프레임의 높이와 위치를 조작하는 메서드를 갖는다. 이 메서드는 점차 프레임을 확대하거나 축소하여 각 렌더링 주기마다 프레임이 스크린 상단에서 아래로 슬라이딩하며 내려 오게 하거나 다시 올라가게 한다.

     

    컨트롤러에서 요청 처리하기

     

    MVC 튜플에서 키 부분을 분리하도록 Monkeybars를 디자인했다. 뷰는 자바 스윙 객체에 대한 직접적인 참조를 가지므로, 이 부분은 테스트하기 가장 까다로운 부분이다. Monkeybars는 모델과 컨트롤러가 직접 뷰와 상호 작용하는 일은 최소화하려고 했다. 그러나 컨트롤러에는 UI 이벤트를 다룰 책임이 있으므로, 필연적으로 컨트롤러가 뷰와 반응하도록 제어할 필요가 있다. 그렇지만, 컨트롤러는 뷰 클래스와 직접 통신하지 않으며, 대신 시그널을 사용한다.

     

    잠깐 이 부분을 컨트롤러 측면에서 보자. 뷰에서는 define_signal 메서드를 써서 시그널 처리자를 정의할 필요가 있다. 이 메서드는 시그널 이름과 그 시그널을 처리하는 뷰 메서드를 정의하는 해시를 취한다.

     

    define_signal :name => :initialize, :handler => :do_roll_up

    시그널 처리 메서드는 반드시 (컨트롤러에서 전달되는) 모델과 전달 객체, 이 두 개의 인자를 가진다. 전달 객체는 컨트롤러와 뷰 간에 데이터를 주고 받기 위해 사용하는 임시 해시다. 뷰는 UI의 초기 위치, 슬라이딩으로 들어오고 나가는 순서, 카드 편집의 시작과 종료를 정의한 시그널을 가진다. 이러한 각 시그널 핸들러는 매우 짧다. 다음은 do_roll_up method 메서드의 예다.

     

    def do_roll_up model, transfer hide move_to_top_right roll_up end

    편집 순서는 메뉴 이벤트를 통해 신호를 준다. Edit 메뉴 아이템이 편집을 켜고 끈다. 뷰에서 편집 순서는 card_pane.editable = true 로 세팅하고 HTML로 그린 내용물을 원본 텍스타일 카드 텍스트와 치환하는 것을 의미한다. 또는, 컴포넌트의 콘텐츠 타입을 변경하여 일반 텍스트를 제대로 그릴 필요도 있다.

     

    편집이 끝나면, 반대 상황이 종료된다. 페인에 HTML이 주어지면, editable 은 false 로 세팅된다. 뷰는 스윙 컴포넌트의 상태를 관리하는 데만 관심이 있다. 컨트롤러는 모델에서 텍스트 갱신과 저장을 처리하도록 지시하는 일을 한다.

     

    컨트롤러 클래스 정의하기

     

    중첩된 컨트롤러

    Monkeybars 컨트롤러는 주로 싱글톤(singleton) 클래스로 사용된다. 일반적으로 다중 인스턴스가 필요하지 않다. 주의할 예외라면 프레임이나 페인이 같은 클래스의 인스턴스인 여러 서브컴포넌트를 가져 중첩된 컨트롤러를 사용하는 때다. 이 주제는 본 문서의 범위를 벗어나므로 깊이 다루지는 않겠다. 기본적으로 프레임, 페널, 컴포넌트의 복잡한 구성을 개별 MVC 튜플 세트로 나눌 수 있다. 주소록이 좋은 예가 될 수 있다. 최상위 스윙 프레임에서 여러 주소 객체를 그리는데, 이 때 각 주소 객체는 address_entry 라는 MVC 튜플 세트의 인스턴스가 되는 경우다.

    스윙 객체는 몇 가지 메뉴 아이템을 갖는다. 그러나 뷰 클래스에 아직 아무런 코드를 넣지 않았다. 이러한 코드는 컨트롤러에 속한다. 컨트롤러는 버튼 클릭, 메뉴 선택, 텍스트 변경 등 모든 UI 이벤트를 처리한다. Monkeybars는 이런 작업을 조율하여 기본적으로 스윙 코드로부터 오는 모든 이벤트를 조용히 삼키도록 한다. 관심 있는 이벤트에 대해서만 처리자를 정의하면 된다. 본 예제 애플리케이션에서 관심 있는 이벤트는 메뉴 클릭이다.

     

    이벤트 처리자는 형식은 다음과 같다.

     

    def your_component_name_action_performed # code end

    (코드에서 필요하다면 실제 스윙 이벤트를 매개변수로 받는 처리자를 정의할 수도 있다.)

    Quit 메뉴 아이템을 처리하려면, 그저 프로그램을 종료하면 된다.

     

    def quit_menu_item_action_performed java.lang.System.exit(0) end

    Edit 메뉴의 동작은 좀 더 작업이 필요하다.

     

    def edit_menu_item_action_performed if @editing @editing = false signal :end_edit update_card else @editing = true signal :begin_edit update_model view_model, :current_card end end

    이 코드는 뷰를 제어하는 시그널을 사용하여 편집 모드를 켜고 끄는 작업을 수행한다. 요점은 컨트롤러의 모델 인스턴스와 view_model 메서드가 제공하는 뷰의 모델 사본을 써서 카드 텍스트를 옮기는 방식(시그널에 의해 뷰에 암묵적으로 전달)이다.

     

    컨트롤러에서 사용자 인터페이스의 상태를 알고자 하면, view_state 메서드를 써서 뷰의 모델 사본과 현재 전송 객체를 참조할 수 있다. view_state 에서 모델 사본을 얻는 것은 매우 일반적인 일이므로, Monkeybars는 view_model 메서드를 지원한다.

     

    컨트롤러는 또한 초기 화면을 그리기 시작하라는 메서드와 보임/감춤 디스플레이 순서를 다루는 메서드를 갖는다. 이 두 메서드 모두 시그널을 써서 실제로 표현하는 코드를 뷰에 적용하도록 한다.





    애플리케이션 전체 조율하기

     

    하나 또는 그 이상의 MVC 튜플과 더불어, Monkeybars 애플리케이션은 코드를 준비하고 실행하는 두 가지 주요 도우미 파일을 사용한다.

     

    이 두 파일 모두 “src/” 디렉터리에 있다. manifest.rb 파일은 라이브러리 로딩 경로를 세팅하고 프로그램이 파일 시스템에서 직접 실행되거나 JAR 파일에서 실행되는지 여부에 따라 어떤 파일을 포함시킬 것인지를 정의한다.

     

    앞서 redcloth.rb 를 “lib/ruby/”에 추가했다. 애플리케이션에서 이 파일의 위치를 확인하려면, 이 디렉터리를 로딩 경로에 추가해야 한다. “lib/java” 디렉터리도 마찬가지다. 따라서 manifest.rb에 다음 두 줄이 반드시 포함되도록 하자.

     

    add_to_load_path "../lib/java" add_to_load_path "../lib/ruby"

    또한 main.rb가 “src/”에 있다. 이는 애플리케이션에서 루비 진입점(entry point)이다. 이 파일에는 다른 것도 있을 수 있지만, 주로 글로벌 오류 처리자를 정의하고 메인 애플리케이션 로직을 수행하기 앞서 수행해야 하는 플랫폼 특화된 코드를 넣어 둔다.

     

    예제 프로그램에서는 다음의 단순한 루프를 사용했다.

     

    begin flash_card = FlashController.instance flash_card.init_view :flash_interval_seconds => 8, :show_for_seconds => 20, :window_height => 200, :data_src => 'data/cards.rc' while true do flash_card.present end rescue => e show_error_dialog_and_exit(e) end





    코드 실행하기

     

    생성된 JAR 파일에 관한 몇 가지 함정

    rawr:jar 로 생성한 JAR 파일에는 프로그램 실행에 필요한 모든 정보가 들어 있지 않다. 특히, (jruby-complete.jar와 monkeybars-0.6.4.jar 같이) 프로그램에 필요한 라이브러리를 담은 다른 JAR의 경우 monkey_see.jar와 함께 번들로 포함되지 않는다.

    rawr:jar 를 실행할 때, 이것은 “package/deploy/” 아래에 몇 가지 파일을 생성한다. 이것들은 애플리케이션 실행에 필요한 파일이며, 함께 배포해야 한다. build_configuration.yaml 파일은 어떤 파일이 JAR에 있어야 하는지 어떤 파일과 디렉터리가 배포 디렉터리에 복사되어야 하는지 결정하도록 한다. 예를 들어, data/cards.rc 파일은 monkey_see.jar에 번들로 묶을 수 있다. 그러나 애플리케이션은 편집을 허용하므로, 이 파일을 외부 파일로 두어 변경된 사항을 다시 기록할 수 있어야 한다.

    제자리에 코드와 적절한 데이터 파일이 있을 때만 프로그램을 실행할 수 있다. rawr rake 태스크를 써서 실행 가능한 JAR 파일을 생성하라. 프로젝트를 시작할 때 rawr install 을 실행했는데, 이는 “src/org/rubyforge/rawr/” 아래 Main.java 파일을 생성한다. JAR에서 프로그램을 실행하려면 Main 자바 클래스가 필요한데 rawr 이 이 파일을 생성한다. 이 클래스에는 main.rb 파일을 찾아 해석하는 기본 코드를 담고 있다(파일을 찾지 못하면, 인라인으로 파일을 생성하여 대신 사용한다).

     

    raker rawr:jar 태스크는 이 코드를 컴파일하여 JAR로 파일을 패키징한다. build_configuration.yaml 파일은 이러한 작업을 조율한다. JAR를 생성하기 앞서, 이 파일을 편집하여 애플리케이션의 상세 정보가 반영되도록 하라.

     

    프로그램을 실행하려면, 먼저 다음과 같이 JAR 파일을 만든다.

     

    $ rake rawr:jar

    그리고 다음과 같이 실행한다.

     

    $ java -jar package/deploy/monkey_see.jar

    플래시 카드 스크린이 상단 좌측에서부터 내려와서 잠시 머물다가 말려 올라가는 모습을 볼 수 있을 것이다.

     

    창이 보이는 상태에서, 메뉴 아이템으로 현재 보이는 카드를 편집할 수 있다. 종료하려면, Quit 메뉴 아이템을 쓰면 된다(단축키를 정의했다면, Alt+Q를 누르면 된다).





    결론

     

    전통적인 C 구현의 루비를 대신하여 안정적이면서도 발전적인 대안으로 JRuby를 개발한 것은 루비 GUI 툴킷이 C 기반 옵션을 넘어서 자바 플랫폼에서 가용한 UI 도구도 쓸 수 있음을 의미한다. 스윙은 자바 런타임 설치에 포함된 표준 영역이므로, (J)Ruby는 스윙 컴포넌트로 인해 성숙하고 완비된 그래픽 툴킷을 갖게 되었다. 자바 플랫폼을 사용한다는 것은 애플리케이션을 여러 플랫폼에서 개발하고, 패키징하고, 사용자에게 배포할 수 있음을 뜻한다. Monkeybars 라이브러리를 쓰면, 루비 개발자는 자바 세계의 향상된 개발 편의를 누리면서 복잡한 데스크톱 애플리케이션을 테스트, 유지 관리 가능한 수준으로 개발할 수 있다.

     

    이 문서의 예제는 JRuby 스윙 GUI 개발에서 가능한 부분을 소개하기 위한 주 목적으로 작지만 섬세하게 취사 선택한 것이다. Monkeybars 사이트에는 더 많은 정보와 더 큰 덩치의 예제가 있다.

    • Daum
    • |
    • 카페
    • |
    • 테이블
    • |
    • 메일
    • |
    • 카페앱 설치
     
    카페정보
    # 함께하는 자바 #
     
     
     
    카페 게시글
    자바 웹 프로램 [펌] JRuby와 스윙(Swing)으로 크로스 플랫폼 개발
    플밍지기 추천 0 조회 231 09.09.24 12:29 댓글 0
    게시글 본문내용
     
    다음검색
    댓글
    최신목록