Rev. 2.73

지금까지 SproutCore의 프로젝트를 생성하고, 레이블-뷰를 작성해 보고, 뷰에 컨트롤러도 할당해 보고 바인딩한 값을 버튼으로 조작해 보았습니다. 이제 SproutCore의 진정한 힘을 체험해 볼 시간입니다.

SproutCore의 바인딩(Binding)은 매우 유용합니다. 이 바인딩이라는 강력한 특징을 부여하는 것은 실제로 SproutCore의 "Key Value Observing"(KVO)때문입니다. KVO의 간단한 의미를 이렇게 정의할 수 있습니다. 불특정한 SproutCore의 개체가 속성이 변경될 때 마다 알려주는 녀석이라고 말이죠. 예를 들면, 당신은 "firstName"과 "lastName"속성을 가진 모델 개체를 가지고 있습니다. 그리고 당신의 컨트롤러가 이 두 속성을 감시하고 있다면, 컨트롤러가 가진 메서드들에 의해 언제라도 변경된 속성을 호출할 수 있습니다.

SproutCore의 옵저빙(Observing)은 응용프로그램의 상태변화를 모니터링하는 매우 강력한 방법입니다. 하나의 클래스를 작성하고 클래스의 프로퍼티가 갱신되면 옵저버들이 속성의 변화된 값을 정확하게 알아내고 그에 따른 행동을 시작합니다. 지금부터 옵저버를 설정하는 방법을 알아봅시다.

종전에 버튼을 생성하고 버튼을 누를 때 마다 바인딩(트리거)에 의해 액션이 실행되도록 비교적 간단하게(평범하게) 구현했었습니다. 그러나 버튼은 "겨짐" 또는 "꺼짐" 상황을 표현하는 데에는 적합하지 않았습니다. 그래서 버튼을 채크박스(checkbox)로 변경하고 레이블-뷰의 변경 상태를 옵저버에 의하여 확인되도록 할 것입니다. "app.js"를 열고 "HelloWorld.appController"를 다음과 같이 작성해 주세요.

HelloWorld.appController = SC.Object.create({
  greeting: "Hello World!",
  isClockShowing: NO,
  isClockShowingObserver: function() { 
    var isClockShowing = this.get('isClockShowing') ; 
    var newGreeting = (isClockShowing) ? 'CLOCK!' : 'Hello World!' ; 
    this.set('greeting', newGreeting) ; 
  }.observes('isClockShowing')
}) ;

SproutCore에서 "YES"는 true를, "NO"는 false를 정의하고 있습니다. 마음에 들지 않는다면 false를 사용해도 무방합니다. "isClockShowing"속성과 "isClockShowingObserver"라는 함수를 선언했습니다. 그리고 함수 선언과 동시에 "observes"메서드를 통하여 "isClockShowing"이 감시할 대상임 알려줍니다. 정말로 감시가 되고 있는지 확인해 봅시다.

SC.run(function() { HelloWorld.appController.set('isClockShowing', YES); });

노트: SproutCore의 "get" 그리고 "set"메서드는 대단히 중요한 역할을 수행합니다. 왜냐하면 이 두 메서드를 통하여 옵저버들에게 속성 값의 변화여부를 알려줄 시기를 잡아낼 기회를 부여하는 계산이 포함되어 있기 때문입니다. 그래서 "foo.bar = X"와 같은 전형적인 변수할당 방법은 사용하지 않는 것이 좋습니다. 그리고 정 "get", 또는 "set"이름을 가진 프로퍼티를 사용하고 싶다면 언더스코어("_")를 사용하여 메서드들이 오버라이딩 되는것을 피하도록 하세요.

자, 옵저버가 잘 돌아가고 있나요? 그렇다면 이제 체크박스-뷰를 만들 순서입니다. "main_page.js"를 열어 "buttonView"를 제거하고 대신 "checkboxView"를 아래와 같이 작성합니다.

    childViews: 'labelView checkboxView'.w(),
    checkboxView: SC.CheckboxView.design({       
      layout: { centerX: 0, centerY: 20, width: 250, height: 18 },       
      title: "Show Clock",       
      valueBinding: "HelloWorld.appController.isClockShowing"
    }),

"checkboxView"의 valueBinding은 "HelloWorld.appController"의 "isClockShowing"속성입니다. 이것은 체크박스를 조작하여 "isClockShowing"속성 값을 갱신할 수 있다는 것과 반대로 "isClockShowing"속성 값의 변화에 의해 체크박스의 디스플레이가 변화할 수도 있음을 의미합니다. 이것이야말로 지금까지 우리가 그토록 바라던 마법과도 같은 것이란 말예요! 브라우저를 새로 고친 후 직접 확인해 보세요.

조금전에 컨트롤러에 작성했던 "isClockShowingObserver"메서드를 재작성하고 SproutCore에서 제공하는 Timer를 이용한 몇몇 메서드를 추가로 작성해서 진짜 시계를 만들어 봅시다.

  isClockShowingObserver: function() { 
    var isClockShowing = this.get('isClockShowing') ;  
    // create a timer if it does not exist already 
    if (!this._timer){
      this._timer = SC.Timer.schedule({ 
        target: this, 
        action: 'tick', 
        repeats: YES, 
        interval: 1000 
      });
    }  
    // pause the timer unless the clock is showing   
    this._timer.set('isPaused', !isClockShowing) ;  
    // update right now 
    var newGreeting = (isClockShowing) ? this.now() : 'Hello World'; 
    this.set('greeting', newGreeting) ;
  }.observes('isClockShowing'),
  tick: function() { 
    this.set('greeting', this.now()) ; 
  },  
  now: function() { 
    return new Date().format('hh:mm:ss'); 
  }

페이지를 새로 고침 하세요. 그리고 체크박스를 클릭해 보세요. 시계가 보이나요? 시계 모양이 좀 구리죠? 마지막으로 모양을 다듬고 스타일시트(CSS)를 추가로 작성해 보겠습니다. "main_page.js"를 열고 아래와 같이 작성합니다.

mainPane: SC.MainPane.design({     
  childViews: 'labelView checkboxView'.w(),      
    labelView: SC.LabelView.design({       
    layout: { centerX: 0, centerY: 0, width: 300, height: 50 },       
    valueBinding: "HelloWorld.appController.greeting"      
  }),      
  checkboxView: SC.CheckboxView.design({       
    layout: { centerX: 0, centerY: 30, width: 300, height: 18 },       
    title: "Show Clock",       
    valueBinding: "HelloWorld.appController.isClockShowing"
  })   
}).classNames('clock')

그리고 새로운 css파일을 등록해야 합니다. "main_page.js"가 위치한 "resources"폴더에 "clock.css"이름의 파일을 다음과 같이 작성하여 저장하세요.

.clock {   
  background: black;   
  color: white; 
}  
.sc-theme .clock .sc-label-view {   
  font-size: 48px;   
  line-height: 1.0; 
}

페이지를 새로 고칩니다. 훌륭한 시계가 만들어졌네요! 이것으로 "Hello World!" 튜토리얼을 마칩니다. 우리는 지금까지 SproutCore의 bindings, observers, controllers, views, 그리고 view helpers에 대하여 알아보았습니다. 이제 당신은 SproutCore를 이용하여 간단한 애플리케이션을 만들 준비가 된 셈입니다. SproutCore에 대하여 더 많은 학습이 필요하다면, SproutCore의 Reference 문서를 숙지하세요. 그리고 이미 만들어진 프로젝트를 다운로드하여 분석해 보세요. GitHub에 널렸습니다.

Comments

"SproutCore의 Views 작성하기"에서 뷰(View)와 컨트롤러(Controller)를 작성해 보았습니다. 이제 상호 작용을 발생시키는 버튼을 페이지에 추가해 보도록 하겠습니다. 페이지에 한 구성요소를 삽입하기 위해서 다른 방해 구성요소들로 인해 삽질해 본 경험이 있으신가요? SproutCore를 사용하면 그러한 방해 구성요소들에 대해서 걱정할 필요없이 하나에만 집중할 수 있습니다. 지금부터 학습할 내용은 "labelView"에 바인딩된 컨트롤러의 "greeting"속성 값을 조작하기 위한 새로운 메서드와 버튼를 추가하여 서로 다른 두가지 상황을 연출할 것입니다.

SproutCore에서 새로운 메서드를 추가하는 일은 정말 쉽습니다. 새로운 하위 클래스를 작성하거나 이와 비슷한 짓거리를 할 필요가 없기 때문입니다. 속성을 추가하는 일도 마찬가지 입니다. "app.js"파일을 열고 "HelloWorld.appController"클래스에 아래와 같은 메서드를 그냥 추가하고 브라우저를 "새로 고침"합니다.

  toggleGreeting: function() {
    var currentGreeting = this.get('greeting');
    var newGreeting = (currentGreeting === 'Hello World!') ?
      'I am on SproutCore!' : 'Hello World!' ;
    this.set('greeting', newGreeting);
  }

그리고 브라우저의 콘솔 커맨드-라인에 다음과 같이 입력하여 추가한 메서드가 작동하는지 확인하세요.

SC.run(function() { HelloWorld.appController.toggleGreeting(); }); 

"labelView"의 문자가 변하는 것을 보셨나요? SproutCore는 항상 이런식입니다. 개체의 속성에 바인딩된 값을 즉시 알아낼 수 있습니다. 중요한 것은 시스템의 컨트롤러와 모델(Model) 개체의 상태를 조작하는 일에만 집중하여 작업할 수 있는 것입니다. 이제 액션 메서드를 작성하여 버튼을 추가하고 메시지를 토글해 봅시다.

"main_page.js"를 열고 다음과 같이 buttonView를 추가합니다.

    childViews: 'labelView buttonView'.w(),
    buttonView: SC.ButtonView.design({      
      layout: { centerX: 0, centerY: 20, width: 250, height: 24 },      
      title: "Change Title",      
      action: "toggleGreeting",      
      target: "HelloWorld.appController"     
    }),

노트: SproutCore는 클래스 속성을 바인딩하거나 참조할 때 스트링(string) 형식으로 선언해야 합니다. "프로퍼티 패스"(property pat)라는 규칙이 반영되어 있어 동적인 참조를 가능하게 하고 대상을 모니터링하기위한 프로세싱이 포함되어있기 때문입니다. 프로퍼티 패스는 크게 4가지 종류로 구분됩니다. "Simple Properties", "Absolute Properties", "Relative Properties" 그리고 "Chained Properties"가 바로 그것입니다. "Simple Properties"와 "Absolute Properties"는 단순하게 속성 이름 또는 속성의 경로를 지정하는 것이고 "Relative Properties" 와 "Chained Properties"는 참조 위치를 복수로(또는 유동적으로) 할당할 수 있습니다. 더욱 자세한 내용은 별도로 다루도록 하겠습니다.

버튼-뷰를 추가하고 "target"과 "action"속성에 방금 작성한 컨트롤러와 메서드 이름을 작성했습니다. 브라우저를 "새로 고침"한 후 무슨일이 발생했는지 살펴봅시다. 브라우저에서 제공하는 자바스크립트 이벤트 API를 생각할 겨를도 없이 끝나버렸군요. 이것으로 SproutCore의 뷰와 컨트롤러를 작성하고 액션(action)을 배정하는 기초적인 사용법을 알아보았습니다.

Comments

"SproutCore 1.0 시작하기"에서 SproutCore의 설치방법과 "Hello World!" 애플리케이션을 만들어 보았습니다. 이번에는 Hello World 프로젝트를 그대로 사용해서 "뷰(View)"를 만들어 보겠습니다. 뷰는 웹 페이지를 관리하기 위한 자바스크립트 클래스 입니다. DOM을 생성하거나 이벤트를 핸들링하고, 애니메이션 효과를 적용하는 등의 역할을 담당합니다. 사실, SproutCore를 사용하면 개발자에 의해 HTML이나 DOM을 직접 작성하는 일은 거의 일어나지 않습니다. 굉장히 유용한 뷰-클래스와 유틸리티들을 이미 갖추고 있기 때문에 애플리케이션의 레이아웃을 잡는데 큰 도움이 될 것입니다.

모든 CSS와 이미지 그리고 정적인 리소스는 “english.lproj”라 불리우는 곳에 저장됩니다. 왜냐하면 SproutCore는 기본적으로 다국어 환경을 지원하기 때문입니다. lproj 폴더를 추가하여 다른 언어를 지원하도록 구축할 수 있습니다. 기본 언어는 영어입니다. 다국어 처리에 대해서는 차차 알아 보기로 하고, 일단 HelloWorld 프로젝트에서 "resources/main_page.js"를 열면 아래와 같은 코드를 발견할 수 있습니다.

// This page describes the main user interface for your application.  
HelloWorld.mainPage = SC.Page.design({
  // The main pane is made visible on screen as soon as your app is loaded.
  // Add childViews to this pane for views to display immediately on page load.
  mainPane: SC.MainPane.design({
    childViews: 'labelView'.w(),
    labelView: SC.LabelView.design({
      layout: { centerX: 0, centerY: 0, width: 200, height: 18 },
      textAlign: SC.ALIGN_CENTER,
      tagName: "h1", 
      value: "Welcome to SproutCore!"
    })
  })
});

labelView의 "value"속성을 "Hello World! I'm Using Sproutcore"로 변경하고 저장합니다. 그리고 브라우저를 "새로 고침"하면, 바뀐 문자를 볼 수 있습니다. 어라? 그런데 조금 이상하죠? 저런~ 글씨가 잘려나갔군요. 그러나 걱정하지 마세요. 문제 없습니다. "layout"속성의 "width"속성 값을 200에서 300으로 늘려줍니다. 오호라~ 제대로 잘 보이는군요.

이제 컨트롤러(Controller)를 추가해서 조금 더 재미를 봐야겠죠? 실제로 뷰는 대부분 컨트롤러에 의해서 제어됩니다. 컨트롤러는 크게 두가지로 나눌 수 있는데요. 환면상의 변화에만 집중해서 작성하는 "뷰-헬퍼(View helper)"와 모델(Model)과 뷰를 연계하는 모델 컨트롤러입니다. 아마도 당신은 이 컨트롤러들을 작성하는데 대부분의 시간을 소비하게 될 것입니다. 그만큼 매우 중요하고 민감한 역할을 수행하기 때문이죠. 커맨드-라인을 실행하고 프로젝트 폴더로 이동합니다. 그리고 아래와 같이 타이핑하세요.

C:\Ruby\bin\hello_world\>sc-gen controller HelloWorld.appController
 ~ Created directory at controllers
 ~ Created file at controllers/app.js
 ~ Created directory at tests
 ~ Created directory at tests/control
 ~ Created file at tests/controllers/

Your controller is now ready to use!

자, "HelloWorld.appController"라는 이름의 컨트롤러가 뚝딱 생겨났네요. 그리고 "apps/hello_world" 폴더에 "controllers" 폴더와 "app.js" 파일이 생성되었습니다. 이 파일을 열면 아래와 같은 내용을 볼 수 있습니다.

HelloWorld.appController = SC.Object.create(
/** @scope HelloWorld.appController.prototype */ {
  // TODO: Add your own code here.
}) ;

이것은 create() 메서드에 의해 생성된 컨트롤러 클래스입니다. SproutCore에서는 이와 같이 매우 쉽게 사용자 정의 개체를 만들 수 있어, 자바스크립트 애플리케이션을 구축하는데 더욱 편리하고 빠른 개발환경을 제공합니다. 주석문을 지우고 아래와 같이 작성합니다.

HelloWorld.appController = SC.Object.create({
  greeting: "Hello World!"
}) ;

이제 컨트롤러가 준비되었으니 뷰에 바인딩(Binding)을 해 보겠습니다. "main_page.js"파일을 다시 엽니다. 그리고 LabelView의 "value"속성을 다음과 같이 교체한 후 브라우저를 "새로 고침"합니다.

    labelView: SC.LabelView.design({
      layout: { centerX: 0, centerY: 0, width: 200, height: 18 },
      textAlign: SC.ALIGN_CENTER,
      tagName: "h1", 
      valueBinding: "HelloWorld.appController.greeting"
    })

"Hello World!"가 보이세요? 이것은 컨트롤러에 의해서 출력된 레이블입니다. 그리고 컨트롤러의 "greeting"속성과의 연결고리는 살아있습니다. 언제라도 "greeting"속성의 값이 변경되면 "labelView"의 레이블은 자동으로 갱신될 것입니다. 정말로 그런지 확인해 볼까요? 콘솔 디버깅이 가능한 브라우저인 크롬 또는 사파리, 파이어폭스의 파이어버그 등의 커맨드-라인에 아래와 같이 입력해 봅시다.

SC.run(function() { HelloWorld.appController.set("greeting", "I am changing!!!"); });

노트: 콘솔 디버깅 할 때 SC.run 메서드를 사용해야 하는 이유는 해당 함수가 실행되는 동안 KVO(Key Value Observing)를 강제로 실행하여 바인딩된 대상의 변화여부를 모니터링하기 위해서 입니다. 이것은 외부에서 실행할 때에만 해당하며 SproutCore내부에서는 자동으로 처리되고 있어 실제 코딩에서는 신경쓰지 않아도 되는 부분입니다.

컨트롤러의 속성에 할당된 값을 변경했을 뿐인데 레이블까지 변경되는 것을 볼 수 있습니다. 이것은 "labelView"에서 컨트롤러의 "greeting"속성를 바인딩했기 때문입니다. 바인딩은 특정한 개체에 있는 속성에 대한 전선과도 같습니다. 속성의 값이 변경되면 묻지도 따지지도않고 자동으로 갱신되는 것이죠. 만약 이 속성을 함수에서 재정의한다면 더욱 동적인 뷰를 만들어 낼 수 있게 됩니다. 참 놀랍죠?

Comments