시작하기전에
본 튜토리얼은 여러분이 이미 ExtjS(자바스크립트 라이브러리)를 설치 했다고 가정합니다. 우리가 만들 어플리케이션 디렉터리의 하위에 extjs 라는 이름의 디렉터리에 설치되어 있어야 합니다. 만약 Ext JS를 이미 다른 곳에 설치했다면 예제 파일내의 스크립트 태그 속성인 src 속성값을 알맞은 경로로 수정합니다.
우리에게 필요한 것
ExtJS외에 2개의 파일이 더 필요합니다:
- applayout.html
- applayout.js
이제 최소한의 내용과 파일만으로 진행해봅시다. 여기에 상세한 설명이 있습니다:
applayout.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" type="text/css" href="../extjs/resources/css/ext-all.css">
<script type="text/javascript" src="../extjs/adapter/ext/ext-base.js"></script>
<script type="text/javascript" src="../extjs/ext-all-debug.js"></script>
<script type="text/javascript" src="applayout.js"></script>
<!-- 현지화 스크립트 파일은 여기에 위치시킵니다. -->
<script type="text/javascript">
Ext.onReady(myNameSpace.app.init, myNameSpace.app);
</script>
<title>Application Layout Tutorial</title>
</head>
<body>
</body>
</html>
첫 번째 두 줄은 문서 타입을 지정하고 있습니다. 어플리케이션은 문서타입을 지정하지 않아도 실행될 수 있지만 이 경우 브라우저는 작성된 어플리케이션에게 브라우저간 호환성을 제공하기 위해 기능이 떨어지는 하위호환 모드로 작동될 것입니다.
본 튜토리얼에서는 전통적인 HTML 4.01을 사용합니다. 왜냐하면 이 문서 타입은 대부분의 메이저 브라우저들이 아주 잘 지원하기 때문입니다. 물론 여러분의 기분이나 필요에 따라 다른 문서 타입을 사용 할 수도 있습니다. 하지만 반드시 항상 문서 타입을 지정해야 한다 라는 사실을 기억해두세요.
html과 head를 지나면 Content-Type을 지정하는 부분이 나옵니다. 아까와 마찬가지로 이 부분이 없어도 어플리케이션은 실행 됩니다만 여기에 있어야 합니다.
이제 우리는 Ext 스타일과 어댑터 그리고 ExtJS 자체를 인클루드 합니다. 여기에 두 가지 버전의 ExtJS가 있습니다:
- ext-all.js - 사람이 읽기 어렵고, 로딩이 빠르며, 출시용
- ext-all-debug.js - 사람이 읽기 쉽고, 개발용
우리는 개발을 해야 하니까 디버그 버전을 인클루드 합니다.
계속 내려가면 우리의 어플리케이션인 applayout.js 이 나옵니다. 그 다음에는 어플리케이션이 초기화가 되기 전에 applayout.js와 ExtJS내의 모든 영어 문자를 변환 시켜주는 현지화 스크립트가 나옵니다.
이제 우리는 문서가 모두 로드 되고 나서 초기화(실행) 할 수 있는 이벤트 핸들러를 설치 할 준비가 되었습니다.
Line
Ext.onReady(myNameSpace.app.init, myNameSpace.app);
읽기 : 문서가 모두 로드 되고 나면 myNameSpace.app 스코프(scope) 내에서 myNameSpace.app의 init 메소드를 실행시켜라.
계속해서 타이틀, head의 끝과 body(지금은 비어 있음) 그리고 닫는 태그가 나옵니다.
여기까지가 우리의 가장 작은 html 파일의 전부 입니다.
applayout.js
/**
* Application Layout
* by Jozef Sakalos, aka Saki
* http://extjs.com/learn/Tutorial:Application_Layout_for_Beginners
*/
// reference local blank image
Ext.BLANK_IMAGE_URL = '../extjs/resources/images/default/s.gif';
// create namespace
Ext.namespace('myNameSpace');
// create application
myNameSpace.app = function() {
// do NOT access DOM from here; elements don't exist yet
// private variables
// private functions
// public space
return {
// public properties, e.g. strings to translate
// public methods
init: function() {
alert('Application successfully initialized');
}
};
}(); // end of app
// end of file
이 파일의 첫 번째 줄은 주석인데 파일의 내용, 저자와 관련 정보를 설명합니다. 뭐, 어플리케이션은 이 한 줄의 주석이 없어도 실행이 됩니다만 다음을 기억해 두세요: 항상 다른 사람을 위해 작성하듯이 당신의 어플리케이션을 작성해라. 몇 달이 지나서 자신의 코드를 다시 보면 이전 규칙에 대한 기억이 나지 않을 것이고 여러분은 자신의 코딩 습관을 바꾸게 될 겁니다.
그리고 공백 이미지의 기본 참조 값이 extjs.com쪽으로 지정된 것을 여러분의 서버 쪽으로 변경합니다. 각각 어플리케이션이 로드 될 때마다 extjs.com에 접근하는 것을 원치 않으시겠죠?
이제 우리들만의 네임스페이스를 만들어 봅시다. 이렇게 하는 이유는 하나의 전역 객체 안에 모든 변수와 메소드를 담아서 변수 명 충돌을 피하고 여러 전역 함수들에 의해 변수가 예측할 수 없는 값으로 변경되는 것을 막기 위해서입니다. 이름은 여러분이 적당히 선정하면 됩니다.
이게 핵심인데, 마지막으로 우리는 리턴 값으로 즉시 실행되는 함수를 제공하는 myNameSpace의 app 프로퍼티를 만들었습니다.
만약 아래의 코드를 실행 시키면
var o = function() {
return {p1:11, p2:22};
}();
파싱 후에 즉시 실행될 수 있는 익명 함수(이름이 없는 함수)를 만든 겁니다.(함수 선언 뒤에 ()가 붙는 것에 주의 하세요). 이 함수는 객체를 리턴 하며 변수 o에 할당 되어 있습니다. 우리의 어플리케이션은 같은 로직을 사용합니다.
초기화나 private 변수 혹은 함수 선언구문을 함수의 시작부터 return 구문 사이에 넣을 수 있습니다. 하지만 다음을 기억하세요:이 코드는 head의 일부분으로 실행이 되고 이때는 html 엘리먼트들이 준비 안되어 있으므로 접근을 시도하려고 하면 에러가 발생됩니다.
이와는 반대로, init 함수는 단지 익명함수에 의해서 리턴 되는 하나의 메소드 인데 문서가 모두 로드 되었을 때 실행이 되며 모든 DOM 구조를 사용할 수 있습니다.
여기까지 잘 따라오셨습니다. 만약 실수를 하지 않았다면 http://여러분서버.com/applayout/applayout.html에 접근 했을 때 경고 창이 보일 겁니다(다른 말로 표현하면 어플리케이션을 실행시켰을 때).
이제 가장 어려운 부분입니다:이 비어 있는 템플릿 안에 먼가 쓸만한걸 집어 넣기.
Public, Private, Privileged?
우리의 어플리케이션에 생명을 불어 넣어 봅시다. 아래 부분을 applayout.html 파일 내의 body 태그에 추가 시킵니다:
<div id="btn1-ct"></div>
이것은 비어 있는 div 인데 버튼의 컨테이너로 이용 될 겁니다. 그리고 아래의 몇 줄을 applayout.js 파일에 추가 시킵니다:
/**
* Application Layout
* by Jozef Sakalos, aka Saki
* http://extjs.com/learn/Tutorial:Application_Layout_for_Beginners
*/
// reference local blank image
Ext.BLANK_IMAGE_URL = '../extjs/resources/images/default/s.gif';
// create namespace
Ext.namespace('myNameSpace');
// Just to allow this tutorial to work for 1.1 and 2.
Ext.Ext2 = (Ext.version && (Ext.version.indexOf("2") == 0));
// create application
myNameSpace.app = function() {
// do NOT access DOM from here; elements don't exist yet
// private variables
var btn1;
var privVar1 = 11;
// private functions
var btn1Handler = function(button, event) {
alert('privVar1=' + privVar1);
alert('this.btn1Text=' + this.btn1Text);
};
// public space
return {
// public properties, e.g. strings to translate
btn1Text: 'Button 1',
// public methods
init: function() {
if (Ext.version == "2.0") {
btn1 = new Ext.Button({
renderTo: 'btn1-ct',
text: this.btn1Text,
handler: btn1Handler
});
} else {
btn1 = new Ext.Button('btn1-ct', {
text: this.btn1Text,
handler: btn1Handler
});
}
}
};
}(); // end of app
// end of file
변수인 btn1 과 privVar1 은 private 인데, 이것이 뜻하는 바는 이들은 어플리케이션의 밖에서는 접근이 안되고 절대 보이지 않는다는 겁니다. priavate 함수인 btn1Handler 도 역시 마찬가지 입니다.
정반대로 btn1Text는 public 변수이며 어플리케이션의 밖에서 보이고 접근이 가능합니다.(잠시 후 시연을 할겁니다.)
함수 init 는 privileged 이며 이것은 private 와 public 양쪽의 변수 혹은 함수에 접근이 가능합니다. 우리의 예제에서 public 변수인 this.btn1Text 와 private 함수인 btn1Handler 에 접근이 가능합니다. 함수 init 은 public 이기도 하며 외부에서 호출 될 수 있습니다.
자 이제 실행시켜 봅시다! 좌측 상단 코너에 버튼이 보이나요? 좋습니다. 클릭해봅시다. privVar1=11 이 적혀 있는 경고 창이 보일 겁니다. 그렇죠? private 함수는 private 변수에 접근 할 수 있습니다.
하지만 두 번째 경고 창은 뭔가가 잘못되어 this.btn1Text=undefined 라고 보일 건데, 이건 우리가 기대하던 게 아니죠. 그 이유는 변수 this가 우리의 어플리케이션이 아니라 버튼의 핸들러 내부를 지정하고 있기 때문입니다. 아래 보이는 것처럼 버튼 설정부분에 scope:this 를 추가합니다:
if (Ext.Ext2) { //Ext.version.indexOf("2.0") == 0 와 같음.
btn1 = new Ext.Button({
renderTo: 'btn1-ct',
text: this.btn1Text,
handler: btn1Handler,
scope:this
});
} else {
btn1 = new Ext.Button('btn1-ct', {
text: this.btn1Text,
handler: btn1Handler,
scope:this
});
}
그리고 페이지를 새로 고칩니다. 잘 작동되지요?
public 변수 오버라이드(override)하기
아래의 코드를 applayout.js 의 마지막 부분에 추가 시킵니다:
Ext.apply(myNameSpace.app, {
btn1Text:'Taste 1'
});
// end of file
이 코드들은 무엇을 할까요? 처음으로 우리의 어플리케이션 객체를 생성하고 public 변수인 btn1text의 값을 오버라이드 합니다. 실행시키면 버튼의 글자가 변경되는 것을 볼 수 있습니다.
원본 어플리케이션 코드를 건드리지 않고도 쉽게 다른 언어로 변역 하는 것처럼 이것은 어플리케이션 내의 public 변수에 값을 입력하는 좋은 코딩 습관입니다.
물론 치수나 스타일 등등 모든 설정 가능한 옵션의 설정 값을 변경 할 수 있습니다. 여러분은 어딘가의 색상을 변경하기 위해 수천 라인의 코드를 뒤져 보는 것을 원치 않을 겁니다.
public 함수 오버라이드 하기
아래 보이는 것처럼 우리의 코드를 변경해봅시다:
Ext.apply(myNameSpace.app, {
btn1Text:'Taste 1',
init: function() {
try {
btn1 = new Ext.Button('btn1-ct', {
text: this.btn1Text,
handler: btn1Handler,
scope: this
});
}
catch(e) {
alert('Error: "' + e.message + '" at line: ' + e.lineNumber);
}
}
});
// end of file
우리가 여기서 무엇을 하고 있는 중 일가요? 단지 이전에 있던 동일한 코드에 에러를 처리하기 위한 try/catch 블록만 추가 시켜서 init 함수를 오버라이드 하는 중 입니다. 수정 이전과 동일한 실행 결과를 보일 것으로 기대 되지만 혹시 다를 수도 있을 까요? 실행시켜서 직접 확인해 봅시다.
이런! btn1Handler이 선언되어 있지 않아 에러 상태가 됩니다. 이유는 수정된 함수가 private 변수에 접근 하려고 했기 때문입니다. 이렇게는 실행될 수 가 없습니다. 하지만 아까 보았죠? 원본 init 가 privileged 라서 private 공간에 접근 할 수 있다는 걸, 하지만 새로운 init는 그렇게 할 수 가 없는데 여기서 안전성에 대한 관점을 다시 볼 수 있습니다:외부의 코드는 private 공간에 접근 할 수 없다. 오버라이드된 privileged들 까지도
계속 진행 중...
제가 생각 중인 유용한 튜토리얼의 일부로 처음 부분을 이렇게 릴리스 하였으며 그 외에도 다른 예제에 대한 계획 중 입니다.