IBM®
메인 컨텐츠로 가기
    Korea [국가변경]    이용약관
 
 
   
        제품    서비스 & 솔루션    고객지원 & 다운로드    회원 서비스    
한국 developerWorks   >   Open developerWorks  > developerworks

루비로 만드는 아름다운 데스크톱 애플리케이션, Part 2: 용돈 기입장 만들기



박진형
박진형 jenix@jinhyung.org

연세대학교 수학과를 휴학하고 넥스지(NexG)에서 병역 특례로 일하고 있다. 리눅스 커널 개발, 오브젝티브-C, 코코아(Cocoa) 애플리케이션 개발에 관심이 많으며 OSXDev(http://www.osxdev.org)의 공동 대표를 맡고 있다.


난이도 : 초급
2007년 9월 18일


[오픈 디벨로퍼웍스]는 여러분이 직접 필자로 참가하는 코너입니다. 이번 연재에서는 RubyCocoa를 이용하여 실제로 사용할 수 있는 애플리케이션 제작해봅니다.

모델, 뷰, 컨트롤러

루비 온 레일즈(Ruby On Rails)를 이미 접해본 독자라면 MVC(Model-View-Controller) 디자인 패턴도 친숙하리라 생각한다. 코코아도 이러한 디자인 패턴을 따라간다. 애플리케이션의 구성 요소가 되는 모델을 디자인하고, 해당 모델들을 표시할 뷰를 디자인하고, 이 뷰와 모델을 관리할 컨트롤러를 디자인한다. 이렇게 기능별로 구성요소를 만들어 조립만 하면 된다. 코코아를 아직 접해보지 못한 독자라면 코코아에서 이를 얼마나 아름답게 구현했는지 놀라게 될 것이다.

   소셜 북마크

   mar.gar.in mar.gar.in
    digg Digg
    del.icio.us del.icio.us
    Slashdot Slashdot

바인딩(Binding)

우선 시작하기 전에 코코아 바인딩에 대해 간략하게 설명할 것이다. 코코아 바인딩은 Mac OS X 팬서(10.3) 이후 SDK에 포함된 기능으로 앞에서 말한 MVC 디자인 패턴을 이용한 애플리케이션 제작을 편하게 해준다. 누가 필자에게 간단하게 한마디로 코코아 바인딩을 설명하라고 하면, "코드 타이핑이 정말 많이 많이 많이 준다!"라고 설명할 것이다.
원래 코코아 자체는 MVC 디자인 패턴을 염두하고 만들었던 프레임워크라서 팬서 이전에도 이미 이러한 형태로 애플리케이션을 제작해 왔다. 하지만 모델 객체와 뷰 객체 사이의 동기화를 위한 코드를 프로그래머들이 직접 일일이 작성해야 했는데(당연한 이야기지만), 코코아 바인딩이 이러한 코드의 역할을 한다. 모델 객체와 뷰 객체 사이의 관계를 설정해 두면 런타임에 코코아 프레임워크가 이를 판단하여 두 객체를 연결해 준다. 코코아 바인딩에 관해서 더 자세한 내용은 ‘What are Cocoa Binding?’을 참고하도록 하자.



위로



루비로 용돈 관리하기, RubyCash

이번에 만들 애플리케이션은 간단한 용돈 기입장이다. 사용한 곳을 한 줄로 기록할 수 있고, 입출 내역과 총 잔액을 표시해준다. 물론, 기록 내용을 저장한다. 우선 Xcode에서 RubyCocoa 애플리케이션으로 새로운 프로젝트를 만들고 이름은 RubyCash라 하자. 제일 먼저 할 일은 RubyCash에서 사용할 모델 객체를 디자인하는 것이다. 앞에서 설명했듯이 돈을 이용한 것에 대한 간단한 기록과 얼마를 사용했는지에 대한 내용이 필요할 것이다. File->New File 메뉴를 클릭하여 Ruby NSObject subclass 파일을 새로 추가한다. 파일 이름은 Purpose.rb라 하자. Purpose.rb의 코드는 다음과 같다.

  

require 'osx/cocoa'

class Purpose < OSX::NSObject
  kvc_accessor :purpose, :money
  
  def init
    if super_init
      @purpose = 'Launch for Jenix :)'
      @money = '-5000'
      return self
    end
    return nil
  end
end


이전엔 보지 못했던 예약어를 볼 수 있을 것이다. 바로 kvc_accessor란 메서드인데, 이 메서드를 이용하여 여기에 클래스 변수를 등록해 두면, 해당 변수를 읽고 쓰는 함수를 따로 구현하지 않아도 된다. 코코아에서 키-값 코딩(Key-Value Coding)이란 기법을 이용한 것인데, 지정된 키를 통하여 해당 값을 설정하는 것이다. 여기에선 키 이름을 바로 우리가 사용할 돈을 사용한 곳(purpose)와 사용한 금액(money)으로 등록했다. init 메서드에서는 기본으로 필자를 위한 점심과 점심값 5000원을 제공토록 하였다.

이제 뷰를 디자인할 차례다. Xcode 메인 화면에서 MainMenu.nib을 더블클릭하여 인터페이스 빌더에서 그림 1과 같이 구성하도록 한다. 우리가 만들 최종 애플리케이션의 모습이다.

완성된 애플리케이션의 모습
그림 1. 완성된 애플리케이션의 모습

여기에 코코아 바인딩을 추가할 차례다. 코코아 바인딩 설정은 인터페이스 빌더에서 이루어진다. 하나씩 해 보자. 우선 인터페이스 빌더 팔레트에서 가장 오른쪽에 보면 파란색 정육면체가 있을 것이다. 해당 아이콘을 클릭해보면 몇 가지 구조물이 보이는데, 그 중에서 NSArrayController를 찾아 MainMenu.nib으로 드래그앤드롭하여 NSArrayController를 추가하도록 한다.

NSArrayController 추가
그림 2. NSArrayController 추가

이 NSArrayController가 우리의 모델 객체와 뷰의 테이블 뷰를 연결해줄 컨트롤러 객체가 된다. 테이블 뷰에서 행 이름 부분을 클릭하면 반전이 되는데, 여기에서 정보 패널을 열고 Bindings 항목으로 이동한다. 그림 3과 같이 Bind to 항목은 NSArrayController(NSArrayController)로, Controller Key는 arrangedObjects로, Model Key Path는 purpose로 설정한다. 마찬가지로 Money 행에 대해서도 같이 하여 Model Key Path는 money로 하도록 한다. 마지막으로 Total 오른쪽 텍스트 필드에서는 Money의 총합이 나올 수 있도록 그림 4와 같이 바인딩을 설정하도록 한다. @sum은 해당 키의 값들을 모두 더하라는 의미인데, 이는 NSArrayController에서 지원하는 것으로, 이 외에도 @min, @max, @avg 등 해당 키 경로에 관한 값들의 정보를 간단하게 가공할 수 있도록 해준다. 이렇게 @sum.money로 Model Key Path를 지정하고 아래 옵션의 Continuously Updates Value를 체크해두면, 위의 테이블 뷰에서 값이 변경될 때 바로 바로 money 키의 값들을 다시 합산하여 텍스트 필드에 총합을 바로 보여준다. 이로써 모델 객체와 바인딩의 관계 설정이 끝났다. 이제 뷰와 바인딩을 연결할 차례다.
+와 -로 이름을 붙인 두 버튼은 테이블 뷰에 새로운 내역을 추가하거나 삭제하는 내용이다. 이전에서 했던 것처럼 + 버튼에서 컨트롤 키를 누른 채 NSArrayController로 드래그하여 +는 insert: 액션으로, -는 remove: 액션으로 그림 5와 같이 연결한다.

각 뷰 객체의 바인딩 설정(1)
그림 3. 각 뷰 객체의 바인딩 설정(1)

각 뷰 객체의 바인딩 설정(2)
그림 4. 각 뷰 객체의 바인딩 설정(2)

NSArrayController와 뷰의 연결
그림 5. NSArrayController와 뷰의 연결

이 외에 추가적인 작업으로 Money란에는 숫자만 입력할 수 있도록 NSFormatter를 적용할 것이다. 숫자 외의 값을 입력하여 NSArrayController를 통해 Total 값을 구하는 데 문제가 발생하면 안 되기 때문이다. 텍스트 필드를 추가한 팔레트에서 왼쪽 아래에 보면 $ 마크가 텍스트 필드로 들어가는 형태의 팔레트가 하나 있는데 이를 Money 행으로 드래그앤드롭하면 된다. Total 텍스트 필드에서 역시 이를 적용시켜 주자. 정보 패널의 항목을 보면 Formatter란 항목이 새로 추가되어 있다. 그림 6과 같이 화폐 단위임을 나타내도록 선택하고 음수일 경우는 빨간색으로, 화폐 단위를 지역화해 표시하도록 한다.

NSArrayController와 뷰의 연결
그림 6. NSArrayController와 뷰의 연결

이제 중간 점검을 해볼 차례다. 인터페이스 빌더에서 nib 파일을 저장하고 Xcode로 돌아와 빌드하고 실행한다. +와 -를 눌러가며 테이블 뷰에 아이템이 추가됨을 확인하자! 놀랍지 않은가? 코드는 단순히 Purpose 클래스에 기본 변수만 넣었을 뿐인데! +로 여러 아이템을 추가하며 Money 항목 값을 변화시켜 Total 값도 자동으로 변하는가 살펴보도록 한다. 이렇게 동작하지 않는다면 앞의 과정에서 무언가 실수를 한 것이므로 다시 한번 과정을 살펴보도록 하자.

이제 입력한 내용을 저장하고 불러올 수 있도록 기능을 추가할 것이다. 이를 위해 코코아 프레임워크의 아카이브 기능을 이용할 것인데, 간략히 설명하자면, 아카이브할 특정 최상위 객체를 지정하면 해당 객체가 가지고 있는 하위 객체들을 탐색하며 이를 파일로 저장하는 기능이다. 물론 저장된 파일에서 객체로 언아카이브도 가능하다. 이러한 아카이브 기능을 이용하려면 해당 객체가 인코딩되고 디코딩되는 방법을 알아야 한다. 이는 NSCoding 프로토콜의 initWithCoder와 encodeWithCoder, 두 개의 메서드가 아카이브할 객체에 구현되어 있으면 된다. 이를 우리의 모델 클래스인 Purpose 클래스에 추가하도록 한다. 최종 Purpose.rb 코드는 첨부한 프로젝트 코드를 보기 바란다.

initWithCoder 메서드는 언아카이빙할 때 해당 객체를 어떻게 디코딩할지에 대해 결정을 해주고, encodeWithCoder 메서드는 아카이빙할 때 해당 객체를 어떻게 인코딩할지에 대해 결정을 한다. 코코아 프레임워크의 아카이브 기능을 이용할 수 있도록 Purpose 클래스를 수정하였으니 이제 뷰에서 L, S 버튼에 각각 불러오기와 저장하기 기능을 추가할 차례다. 이를 위해 AppController라는 컨트롤러 클래스를 하나 더 작성할 것인데, 이전 회에서 했듯이 인터페이스 빌더를 통해 NSObject의 서브클래스로 AppController를 생성하고 save:와 load: 액션을 추가한다. 또한 NSArrayController를 이용해야 하므로 아웃렛에는 NSArrayController 타입의 arrayController 아웃렛을 추가한다. 그림 7과 같이 L, S 버튼을 AppController에 연결하고 그림 8과 같이 NSArrayController를 AppController의 arrayController 아웃렛과 연결하도록 한다.

S 버튼과 AppController의 연결
그림 7. S 버튼과 AppController의 연결

arrayController 아웃렛 연결
그림 8. arrayController 아웃렛 연결

AppController 클래스를 만들었으니 이를 구현하자. Xcode의 File->New File... 메뉴를 통해 Ruby Cocoa NSObject subclass 유형으로 새 파일을 추가한다. 이름은 AppController.rb로 한다. 코드는 첨부한 프로젝트 코드를 보기 바란다.

이전에 한 것과 마찬가지로 ib_outlets 예약어를 통해 인터페이스 빌더에서 추가한 arrayController 아웃렛을 인식시키도록 하고, 이를 이용하였다. save 메서드에서는 NSKeyedArchiver 클래스의 archiveRootObject: toFile: 메서드를 이용하여 /tmp/test_data 위치에 NSArrayController가 담고 있는 내용을 파일로 기록하도록 하였고, load 메서드에서는 NSKeyedUnarchiver 클래스의 unarchiveObjectWithFile: 메서드를 이용하여 /tmp/test_data에서 save에서 저장한 객체 내용을 복구하고 arrayController의 addObjects 메서드를 이용하여 복구한 객체들을 다시 추가하도록 하였다. 애플리케이션을 빌드하고 실행해 보도록 한다. 몇 가지 내용을 입력하고 S 버튼을 누른 후, 다시 실행하여 L 버튼으로 앞에서 입력한 내용이 복구되는지 확인해보자. 잘 동작할 것이다.

파일로 저장하는 부분은 굳이 코코아 프레임워크의 아카이브를 사용하지 않아도 된다. 코코아 프레임워크의 NSArray 객체로 가져온 것은 루비의 배열 객체를 사용하듯 바로 사용할 수 있기 때문에 이를 이용해 루비의 파일 I/O로 바로 내용을 기록하거나 불러와도 될 것이다. 하지만 이보단 코코아 프레임워크의 아카이브 기능을 사용하는 것이 코드를 더 단순하게 했기 때문에 이번 예제에서는 이러한 방식으로 구현했다.



위로



더 공부할 거리

이번에 우리가 작성한 코드는 실제로 50줄 정도다. 이 정도의 코드로 테이블 뷰에 데이터를 입력하고, 한 행의 총합을 출력하고, 저장 기능까지 추가하였다. 입력한 코드 내용은 얼마 되지 않지만 실제로 다룬 내용은 정말 많다. 사실 코코아 바인딩에 관한 내용과 아카이브에 관한 내용은 여기에선 코드 설명만을 위해 간략하게 하였지만 이 두 가지 주제만으로도 책 반 권 정도의 분량을 차지할 내용이다. 이미 코코아에 익숙한 독자들은 무리 없이 이해하고 넘어갔겠지만 코코아엔 익숙하지 않은 독자들은 다음 두 문서를 읽어보면 도움이 될 것 같다.

* What are Cocoa bindings?
* Introduction to Archives and Serializations Programming Guide for Cocoa

참고자료
* http://rubycocoa.sf.net
* http://developer.apple.com

첨부 파일 - RubyCash.tgz(RubyCash 프로젝트 소스코드 전체)



이제 전문가의 글을 단순히 ‘보는 것’에서, 직접 여러분이 developerWorks의 필자가 될 수 있습니다. IBM developerWorks를 통해 공유하고 싶은 지식이 있으신 분들은 원고 기획안을 접수해주세요. 채택되신 분께는 소정의 원고료를 드립니다.
Open developerWorks 신청하기   MS워드 아이콘   아래아한글 아이콘



[지난 Open dW 보기]

사이트 여행

dW 커뮤니티
포럼 | 블로그 | Spaces
dW Student Community

로컬 컨텐츠

행사 및 세미나

기획 기사

개발자 입문

튜토리얼 및 교육

TOP 10 인기자료

SW 다운로드

RSS 피드

뉴스레터
  
자바스크립트가 작동이 중지되었습니다. 이 기능을 수행하시려면 브라우저에서 자바스크립스트를 작동시켜 주시거나 이곳을 클릭해주세요.
Special offers
IBM SOA Sandbox 시험판
dW Student Community
로보코드
코드 트레이닝


    IBM 소개 개인정보 보호정책 문의