자바스크립트/Ext JS2012. 7. 20. 14:51

ExtJS 4가 정식버전이 출시되었습니다.

기존에 문제 있던 던 부분들에 대해 거의 해결된 듯 하고 성능부분도 

충분히 개선되었다고 생각됩니다

해서 실무에서 적용가능한 코드를 공개하여 강좌를 진행 해보려고 합니다.

현재 mvc모델을 적용하여 프로젝트를 진행하고 있고 이 프로젝트에서 나오는

주요 코드를 오픈하는 것으로 강좌를 진행할까 합니다.

주요 내용

1. ExtJS 4를 활용한 UI 구성

2. MVC모델을 통한 콤포넌트 제어

3 실프로젝트에서 활용가능한 공통컴포넌트 만들기(데이터베이스 연계)

4 스프링프레임웍을 활용한 서버쪽 코드 만들기

5 Sencha SDK를 활용한 배포파일 만들기

Posted by 베니94
자바스크립트/Ext JS2012. 4. 18. 09:21

ExtJS 포럼을 개설했습니다.

블로그를 운영도 병행 해야 겠지만

현재 프로젝트 중이라 글을 올리기 힘드네요

포럼에 경우 프로젝트 진행 중에도 해결 안되는 문제를

올리고 해결하는 과정들도 댓글로 첨부와 함께 올려서

해결 완료까지 히스토리를 보기 좋다는 장점이 있습니다.

집에서 돌아가고 있는 DS210J(나스)에 phpbb를 이용해

포럼을 구성했습니다.

질문도 올려주시고 답변도 같이 해주시면 감사하겠습니다.

http://benney.able.or.kr/forum

 

Posted by 베니94
자바스크립트/Ext JS2012. 3. 9. 11:14
    
// History.js 의 예
Ext.define("Docs.etc.History", {
    singleton: true,
    init: function () {
    	
        Ext.util.History.init(function () {
        	
            this.historyLoaded = true;		// 최초에 true이고 이후 변경없음.
            this.initialNavigate();
        }, this);
        // 아래는 history가 변경될 때 navigate함수를 호출하라는 내용이다.
        // "이벤트", "호출함수", "인자1", "인자2"
        // 단 인자는 모두 object여야한다.
        Ext.util.History.on("change", this.navigate, this, {'a':'aa'});
    },
    navigate: function (c, d) { // 탭을 클릭하면 history 변경되고 아래를 실행
    	console.log('navigate::', c, d); // 이렇게 확인
    	var d = this.parseToken(c);
    	// loadIndex(true) : true는 왜 ??
    	if (d.url === "#!/guide") {
    		Docs.App.getController("Guides").loadIndex(true);
    	} else {
    		if (d.type === "guide") {
                Docs.App.getController("Guides").loadGuide(d.url, true);
            }
    	}
    }
-


Posted by 베니94
자바스크립트/Ext JS2012. 1. 25. 00:05

오늘은 히스토리 Api에 대해 알아보겠습니다.
Doc App을 분석하다보니 History가 중요한 역할을 하고 있습니다.

예제를 통해 알아 보겠습니다.

    

    
// app.js
Ext.onReady(function() {
    Ext.History.init();
    var tokenDelimiter = ':';
    
    function onAfterRender() {
    	for(var i=0; i<10; i++){
//    	 Ext.util.History.add(i)
    	 Ext.History.add(i);	
    	}
    }
    
    Ext.create('Ext.TabPanel', {
        renderTo: Ext.getBody(),
        id: 'main-tabs',
        height: 300,
        width: 600,
        activeTab: 0,
        defaults: {
            padding: 10
        },
        
        items: [
        {
            title: 'Tab 1',
            id: 'tab1',
            html: 'Tab 1 content'
        },{
            title: 'Tab 2',
            id: 'tab2',
            html: 'Tab 2 content'
        },{
            title: 'Tab 3',
            id: 'tab3',
            html: 'Tab 3 content'
        },{
            title: 'Tab 4',
            id: 'tab4',
            html: 'Tab 4 content'
        },{
            title: 'Tab 5',
            id: 'tab5',
            html: 'Tab 5 content'
        }],
        listeners: {
            afterrender: onAfterRender 
        }
    });
});

 
브라우저 "뒤로가기"버튼을 클릭 해보면 주소창의 #9가 0까지 변하는 것을 볼 수 있다. Ext.History.add(i) 이 부분을 실행 할 때 마다 브라우저에 #으로 add의 인자가 표시되는 것을 알 수 있다.


이제 코드를 좀 수정해서 ExtJs가 History가 변경되었을 경우를 모니터링 해봅니다.

    function onAfterRender() {
    	 Ext.History.on('change', function(token) {	// history 가 변경되었을 경우 감시할 리스너
    		 console.log(token);
    	 });
    	for(var i=0; i<10; i++){
//    	 Ext.util.History.add(i)
    	 Ext.History.add(i);	
    	}
    }

브라우저를 통해 실행 해 봅니다. 뒤로가기 버튼을 클릭해서 console에 숫자가 찍히는지 확인 합니다. 이를 통해 extjs가 히스토리 객체에 특정 텍스트를 넣고 브라우저의 뒤로가기 버튼을 클릭하면 변경된 히스토리를 감시 할 수 있습니다.


다시 소스를 수정합니다. 이번에는 히스토리에 위의 그림에서 보이는 5개의 탭아이디를 저장하고 뒤로가기 버튼을 클릭해서 각 탭이 선택되도록 해보겠습니다. 사용자가 클릭하지 않고 히스토리를 통해 UI를 조정하는 것입니다.
    function onAfterRender() {
    	 Ext.History.on('change', function(token) {	// history 가 변경되었을 경우 감시할 리스너
    		 console.log('History id ', token);
    		 Ext.getCmp('main-tabs').setActiveTab(Ext.getCmp(token));	// history값으로 탭이 선택되도록
    	 });
    	for(var i=0; i<5; i++){
//    	 Ext.util.History.add(i)
    	 Ext.History.add('tab'+(i+1));	// 아래쪽 탭들의 아이디와 매칭되도록 
    	}
    }
1

아래 그림처럼 뒤로가기 앞으로가기 버튼을 클릭할 때 마다 해당 히스토리에 맞게 탭에 활성화 되는 것을 확인 할 수 있습니다.


이제 좀 다르게 html에 링크를 만들고 해당 링크를 통해 탭을 활성화 해보겠습니다. href="#tab1"과 같이 코딩하고 클릭하면 해당 탭이 활성화 됩니다.
   
    

tab1


tab2


tab3


tab4


tab5




이런 식으로 히스토리에 UI의 아이디 등을 저장하고 사용자가 뒤로가기 또는 앞으로 가기 버튼을 클릭하면 ExtJS는 히스토리
변화를 감지하고 변경되었을 경우 해당 로직대로 화면을 보여줄 수 있도록 코딩하면 될 듯 싶습니다.
Posted by 베니94
자바스크립트/Ext JS2012. 1. 4. 17:08
이제 CSS를 적용해 봅니다. 우선 상단 전체에 백그라운드로 적용 된 CSS를 찾아보자. Docs.view.Viewport 클래스에 region:'north'영역의 아이디가 'north-region'으로 되어 있늘 걸 보면 동일한 이름의 CSS가 이 프로그램이 사용하고 있는 docs/resources/css/app.css에 있는지 확인합니다. 아래 그림처럼 north-region이름으로 css가 존재합니다.


index.html에 위의 css를 넣어 봅니다.

	
파이어폭스를 통해 해당 north-region이 어디인지 확인 해봅니다.


소스가 수정되었다면 다시 실행해서 css가 적용되었는지 확인합니다.


이제 상단 좌측의 docheader클래스의 영역의 css를 적용해 보자. docheader클래스는 index.html내부의 div 중 'header-content'를 contentEl로 삽입하고 있다 아마도 header-content라는 이름으로 app.css에 css가 존재할 것이다.

Ext.define("Docs.view.Viewport", {
..
..
    initComponent: function () {
    	this.items = [{
    		region: "north",
            id: "north-region",
            style: {borderColor:'#000000', borderStyle:'solid', borderWidth:'1px'},
            title: "north-region",
            height: 65,
            layout: {
                type: "vbox",
                align: "stretch"
            },
            items: [{
                height: 37,
                xtype: "container",
                layout: "hbox",
                items: [{
                	xtype: "docheader",
                	style: {borderColor:'#000000', borderStyle:'solid', borderWidth:'1px'}
                }, {
..

Ext.define("Docs.view.Header", {
    extend: "Ext.container.Container",
    alias: "widget.docheader",
    contentEl: "header-content", // index.html 내부의 header-content div를 현 클래스에 삽입.



index.html에 위의 css를 카피해 넣습니다.


// index.html
	


다시 실행해서 확인해보면 아래 그림처럼 좌측 상단에 로고에 선명하게 css가 적용된 것을 확인 할 수 있다.




상단 탭에 대한 css를 적용합니다. 이전에서 app/controller/Tabs에서 welcome, class 를 탭에 추가하는 코딩을 하였습니다. 해서 이미 보이지는 않지만 탭은 구성이 되어 있습니다. css만 추가하면 화면에 탭이 보여지게 될것 입니다. app.css에서 doctab이라는 이름으로 css를 검색해서 index.html에 추가합니다. 이 때 이미지의 경로는 좀 변경해줘야 합니다. app.css가 있던 위치가 달라졌으므로 index.html기준으로 resourcess/images/xxx.png 이렇게 말입니다.

	


상단 탭에 대한 css가 적용되었습니다..



Posted by 베니94
자바스크립트/Ext JS2012. 1. 2. 21:27

이전 시간까지의 결과를 실행해 보면 크게 달라진것이 없습니다. 이제 탭이 doctab영역에 보여지도록 해보겠습니다.
Docs.Application 클래스에 3개의 컨트롤러를 등록 하였습니다. 이 중에서 Tabs만 onLaunch가 구현되어 있습니다. onLaunch메소드는 init메소드와 동일하지만 조금 다른 듯합니다. 어찌되었든 init메소드가 호출되고 그 다음 onLaunch메소드가 호출되고 있습니다. Tabs 컨트롤러를 봅시다.
Ext.define("Docs.controller.Tabs", {
...    
	refs: [{        
		ref: "doctabs",		// this.getDoctabs()로 접근이 가능하다. 하단에서.        
		selector: "#doctabs"    
	},{        
		ref: "welcomeIndex",	// this.getWelcomeIndex()로 접근      
	  	selector: "#welcomeindex"    
	}, {      
	  	ref: "classIndex",		// this.getClassIndex()로 접근        
		selector: "#classindex"    
	}]
....    
	onLaunch: function () { 	// init 이 후 호출    	
		this.getDoctabs().setStaticTabs(Ext.Array.filter([	    											
							this.getWelcomeIndex().getTab(),     											
							this.getClassIndex().getTab()],     											
				function (a) {            
					a       
 				}
 		));    
 	}

onLaunch메소드 내부의 this.getWelcomeIndex().getTab()로 접근이 가능한 것은 ref로 참조를 하고 있기 때문이다. 아래 처럼 말이죠
// app/view/welcome/Index.js  
     getTab: function () {
        return this.hasContent ? {
            cls: "index",
            href: "#",
            tooltip: "Home"
        } : false
    }
 
Tab Controller가 하는 역할은 각각의 view를 탭에 표현되도록 하는 것입니다. 위의 onLaunch의 this.getDoctabs().setStaticTabs()는 Doc.view.Tabs 클래스의 함수입니다. 해당 클래스의 init와 setStaticTabs함수를 아래와 같이 구현해야한다..
    
    initComponent: function () {	
    	// XTemplate을 통해 루프돌며 탭을 보여질 수 있도록 구성해놓는다.
    	this.tpl = Ext.create("Ext.XTemplate", '', 
    						'
', '
', '', '
', "
", "
", '
 
', '
'); this.html = this.tpl.applyTemplate(this.staticTabs); this.tabTpl = Ext.create("Ext.XTemplate", '
', '
', '
', ' ', '{text}', "
", '', "
"); this.callParent() }, setStaticTabs: function (b) { console.log(b); this.staticTabs = b; this.refresh() }, refresh: function () { var i = this.tpl.applyTemplate(this.staticTabs); var f = this.maxTabsInBar() < this.tabs.length ? this.maxTabsInBar() : this.tabs.length; this.tabsInBar = this.tabs.slice(0, f); for (var j = 0; j < f; j++) { var h = this.tabCache[this.tabs[j]]; var g = Ext.apply(h, { visible: true, active: this.activeTab === h.href, width: this.tabWidth() }); i += this.tabTpl.applyTemplate(g) } this.el.dom.innerHTML = i; if (this.activeTab && this.activeTab !== this.tabs[f - 1]) { this.activateTab(this.activeTab); Docs.History.push(this.activeTab) } //this.highlightOverviewTab(this.activeTab); //this.createOverflow(); //this.addToolTips() },
이제 실행하면 아래와 같은 화면을 볼수 있습니다. 별로 변한건 없고 좌측 "탭영역"이라는 문구가 사라졌습니다. 아마도 css가 없으니 탭이 제대로 보여지지 않는것 같습니다.
Posted by 베니94
자바스크립트/Ext JS2012. 1. 2. 15:34
이제 필요한 각각의 탭을 관장할 컨트롤러를 만들어 보자.
/**
 * File			: app/controller/Content.js
 * Class Name	: Docs.controller.Content
 * Desc 		: 컨트롤러 상위클래스
 */
Ext.define("Docs.controller.Content", {
    extend: "Ext.app.Controller",
    MIDDLE: 1,
    title: "",
    onLaunch: function () {
    }
});
/**
 * File			: app/controller/Welcome.js
 * Class Name	: Docs.controller.Welcome
 * Desc 		: 환영페이지 컨트롤러
 */
Ext.define("Docs.controller.Welcome", {
    extend: "Docs.controller.Content",
    baseUrl: "#",
    init: function () {
    	console.log('Docs.controller.Welcome.init called..');
        this.addEvents("loadIndex")
    }
});

/**
 * File			: app/controller/Classes.js
 * Class Name	: Docs.controller.Classes
 * Desc 		: 
 */
Ext.define("Docs.controller.Classes", {
    extend: "Docs.controller.Content",
    baseUrl: "#!/api",
    title: "API Documentation",
    requires: ["Docs.Settings"],
    stores: ["Settings"],
    models: ["Setting"],
    init: function () { 
    	//console.log('Docs.controller.Classes call', this.stores);
    }
});

/**
 * File			: app/controller/Tabs.js
 * Class Name	: Docs.controller.Tabs
 * Desc 		: 상단 탭영역 처리
 */
Ext.define("Docs.controller.Tabs", {
    extend: "Ext.app.Controller",
    //requires: ["Docs.Settings"],
     refs: [{
        ref: "doctabs",		// this.getDoctabs()로 접근이 가능하다. 하단에서.
        selector: "#doctabs"
    },{
        ref: "welcomeIndex",
        selector: "#welcomeindex"
    }, {
        ref: "classIndex",
        selector: "#classindex"
    }],
    init: function () {
    	/*this.getController("Classes").addListener({
            showClass: function (b) {
            	//console.log('b', b);
                this.addTabFromTree("#!/api/" + b)
            },
            scope: this
        });*/
    },
    onLaunch: function () {
    	this.getDoctabs().setStaticTabs(Ext.Array.filter([	
    											this.getWelcomeIndex().getTab(), 
    											this.getClassIndex().getTab()], 
    											function (a) {
            return a
        }));
    }
});

/**
 * File			: app.js
 * Class Name	: Docs.Application
 * App Name		: Docs
 * Desc 		: Main Class
 */
// app.js 에 controller : ["Welcome"]를 추가한다.
Ext.define("Docs.Application", {
    extend: "Ext.app.Application",
    name: "Docs",
    appFolder : 'app',
    controllers: ["Welcome","Classes","Tabs"],  // 이 부분을 추가한다.
    launch: function () {
    	Docs.App = this;
        Ext.create("Docs.view.Viewport");
        Ext.get("loading").remove()
    }
});

// 위의 4개 클래스를 index.html에서 access할 수 있도록 하자.
  
  
  
  
Posted by 베니94
자바스크립트/Ext JS2011. 12. 30. 16:54

본격적으로 코딩에 들어 가기 앞서 디렉토로 구조를 먼저 설명합니다. 현재는 app.js에 모든 코드가 들어가 있지만 이렇게 되면 혼란스럽기도 하고 본래 mvc모델의 모습을 파악하기 힘듭니다. 해서 아래 그림처림 디렉토리를 나누면서 코딩을 하도록 하겠습니다.


/**
 * File			: LocalStore.js
 * Desc 		: store
 */
Ext.define("Docs.LocalStore", {
    storeName: "",
    init: function () {
       // 로컬 스토리지에 대한 초기화.
    	 this.localStorage = !! window.localStorage;
    	 // 여기서 this.storeName는 Docs.Settings의 storeName으로 
    	 // Classes Controller에서 Settings라는 이름의 store를 참조하기 때문에 아래의 코드가
    	 // 유효하다.
         this.store = Ext.getStore(this.storeName); 
         if (this.localStorage) {
             this.cleanup();
             this.store.load();
             if (window.addEventListener) {
                 window.addEventListener("storage", Ext.Function.bind(this.onStorageChange, this), false)
             } else {
                 window.attachEvent("onstorage", Ext.Function.bind(this.onStorageChange, this))
             }
         }
    },
    onStorageChange: function (b) {
       
    },
    syncStore: function () {
        
    },
    cleanup: function () {
       
    }
});

/**
 * File			: Settings.js
 * Desc 		: 
 */
Ext.define("Docs.Settings", {
    extend: "Docs.LocalStore",
    storeName: "Settings",  // 참조만.
    singleton: true,
    set: function (d, f) {
        
    },
    get: function (c) {
       
    }
});

/**
 * File			: ContentGrabber.js
 * Desc 		: 
 */
Ext.define("Docs.ContentGrabber", {
    singleton: true,
    get: function (f) {
        var e;
        var d = Ext.get(f);
        if (d) {
            e = d.dom.innerHTML;
            d.remove();
        }
        return e
    }
});

/**
 * File			: app/model/Setting.js
 * Class Name	: Docs.model.Setting
 * Desc 		: 
 */
Ext.define("Docs.model.Setting", {
    fields: ["id", "key", "value"],
    extend: "Ext.data.Model",
    proxy: {
        type: window.localStorage ? "localstorage" : "memory",
        id: Docs.data.localStorageDb + "-settings"
    }
});

/**
 * File			: app/store/Settings.js
 * Class Name	: Docs.store.Settings
 * Desc 		: 
 */
Ext.define("Docs.store.Settings", {
    extend: "Ext.data.Store",
    model: "Docs.model.Setting"
});


// 이하 app.js
/*
 * 메인 클래스로 전체 레이아웃을 관장하는 Docs.view.Viewport를 호출
 */
Ext.define("Docs.Application", {
    extend: "Ext.app.Application",
    name: "Docs",
    appFolder : 'app',
    launch: function () {
    	Docs.App = this;
        Ext.create("Docs.view.Viewport");
        Ext.get("loading").remove()
    }
});
Ext.onReady(function () {
    Ext.create("Docs.Application")
});


/**
 * File			: app/view/Viewport.js
 * Class Name	: Docs.view.Viewport
 * Desc 		: 화면 레이아웃
 */
Ext.define("Docs.view.Viewport", {
	extend: "Ext.container.Viewport",
	id: "viewport",
    layout: "border",
    border: 1,
    defaults: {
        xtype: "container"
    },
    initComponent: function () {
    	this.items = [{
    		region: "north",
            id: "north-region",
            style: {borderColor:'#000000', borderStyle:'solid', borderWidth:'1px'},
            title: "north-region",
            height: 65,
            layout: {
                type: "vbox",
                align: "stretch"
            },
            items: [{
                height: 37,
                xtype: "container",
                layout: "hbox",
                items: [{
                	xtype: "docheader",
                	style: {borderColor:'#000000', borderStyle:'solid', borderWidth:'1px'}
                }, {
                    xtype: "container",
                    html : '빈공간',
                    style: {borderColor:'#000000', borderStyle:'solid', borderWidth:'1px'},
                    flex: 1
                }, {
                    id: "loginContainer",
                    xtype: "authentication",
                    style: {borderColor:'#000000', borderStyle:'solid', borderWidth:'1px'},
                    width: 500,
                    padding: "10 20 0 0"
                }, {
                    xtype: "searchcontainer",
                    id: "search-container",
                    style: {borderColor:'#000000', borderStyle:'solid', borderWidth:'1px'},
                    width: 230,
                    margin: "4 0 0 0"
                }]
            }, {
                xtype: "doctabs"
            }]
    	},
    	{
    		region : 'center',
    		style: {borderColor:'#000000', borderStyle:'solid', borderWidth:'1px'},
    		html :'center',
    		layout: "border",
    		minWidth: 800,
    		items: [{
                region: "west",
                xtype: "treecontainer",
                id: "treecontainer",
                border: 1,
                bodyPadding: "10 9 4 9",
                width: 240
            },{
    			region: "center",
                id: "center-container",
                layout: "fit",
                minWidth: 800,
                border: false,
                padding: "5 10",
                style: {borderColor:'#000000', borderStyle:'solid', borderWidth:'1px'},
                items: {
                    id: "card-panel",
                    cls: "card-panel",
                    xtype: "container",
                    layout: {
                        type: "card",
                        deferredRender: true
                    },
                    items: [{
                    	autoScroll: true,
                        xtype: "welcomeindex",
                        id: "welcomeindex"
                    }, {
                        xtype: "container",
                        id: "failure"
                    }, {
                        autoScroll: true,
                        xtype: "classindex",
                        id: "classindex"
                    }]
                }
    		}]
    	}, {
            region: "south",
            id: "footer",
            height: 20,
            contentEl: "footer-content" // index.html div  footer-content.
        }];
        this.callParent(arguments)
    },
    setPageTitle: function (b) {
       
    }
});

/**
 * app/view/Header.js
 * 프로그램 상단
 */
Ext.define("Docs.view.Header", {
    extend: "Ext.container.Container",
    alias: "widget.docheader",
    contentEl: "header-content", // index.html 내부의 header-content div를 현 클래스에 삽입.
    initComponent: function () {       
        this.callParent();
    },
    listeners: {       
    }
});
/**
 * app/view/auth/Login.js
 * 상단 로그인영역
 */
Ext.define("Docs.view.auth.Login", {
    extend: "Ext.container.Container",
    alias: "widget.authentication",
    html : '로그인 영역',
    loginTplHtml: [
'
', '', '', '', '', " or ", '회원가입', "
"], initComponent: function () { this.callParent(arguments); } }); /** * app/view/Tabs.js * 상단 탭영역 , 계속 늘어나겠죠.. */ Ext.define("Docs.view.Tabs", { extend: "Ext.container.Container", alias: "widget.doctabs", id: "doctabs", html :'탭영역', style: {borderColor:'#000000', borderStyle:'solid', borderWidth:'1px'}, initComponent: function () { this.callParent(); }, setStaticTabs: function (b) { this.staticTabs = b; //this.refresh() } }); /** * File : app/view/welcome/Index.js * Class Name : Docs.view.welcome.Index * Widget Name : welcomeindex * Desc : 환영페이지 */ Ext.define("Docs.view.welcome.Index", { extend: "Ext.container.Container", alias: "widget.welcomeindex", requires: ["Docs.ContentGrabber"], cls: "welcome iScroll", initComponent: function () { this.html = Docs.ContentGrabber.get("welcome-content"); this.hasContent = !! this.html; this.callParent(arguments) }, getTab: function () { return this.hasContent ? { cls: "index", href: "#", tooltip: "Home" } : false } }); /** * File : app/view/cls/Index.js * Class Name : Docs.view.cls.Index * Widget Name : classindex * Desc : 클래스 Api 페이지 */ Ext.define("Docs.view.cls.Index", { extend: "Ext.container.Container", alias: "widget.classindex", requires: ["Docs.ContentGrabber"], cls: "class-categories iScroll", margin: "15 10", autoScroll: true, initComponent: function () { this.callParent(arguments); }, getTab: function () { var b = (Docs.data.classes || []).length > 0; return b ? { cls: "classes", href: "#!/api", tooltip: "API Documentation" } : false } }); // 이제 index.html에 아래와 같이 위에서 언급된 파일들을 기입하자.
이로서 Doc어플리케이션의 레이아웃을 구성했습니다. 프로그램의 뼈대를 만드는게 제일 중요합니다. 이번 강좌는 그런면에서 뼈대를 완성하고 또 완성된 뼈대에 어떻게 프로그램들을 채워 넣을지 알아보는게 핵심입니다.
Posted by 베니94
자바스크립트/Ext JS2011. 12. 30. 13:47
이제 app.js파일의 원상태를 확인 할 수 있으니 파일내용을 찬찬히 들여다 보기로 하겠습니다.
우선 메인 함수부터 찾아야 겠죠. 모든 프로그램이 그렇겠지만 맨 아래쪽에서 메인함수와 주요 클래스들을 발견할 수 있었습니다.
실마리는 여기서 부터 잡아나가면 되겠죠


그런데 좀 이상한 것은 파일 라인수 입니다. 4만5천라인이라니~ 용량도 1.3메가나 나갑니다.~ ext-all-debug.js파일이 13만라인에 4메가 정도인데 이건 뭔가 이상합니다.
해서 파일 내용을 좀 자세하게 살펴 보았더니 클래스 중에 Ext로 시작하는 클래스가 상당히 존재합니다. 아래 그림처럼 말이죠!

 


일단 예상하기로는 빌드 과정에서 용량을 최소화 하기 위해 해당 어플리케이션에서 사용하는 클래스들만 따로 골라서 app.js파일에 통체로 같이 빌드하지 않았나 싶습니다. 그래야만 전체 클래스를 내려받는것 보다 낫고 필요한 클래스만 내려 받을 수 있도록 해서 성능을 개선하기 위함이지 않나 싶습니다.  이놈들의 정체가 뭔지 확실히 알려면 Ext로 시작하는 클래스를 ext-all-debug.js와 비교해 보면 알 수 있겠죠 클래스 중 하나를 골라 ext-all-debug.js파일의 동일한 클래스와 내용이 같은지 확인 해보니 100%동일 한 것으로 확인 되었습니다.

분석하는 입장에서는 모두 불필요하니 Ext로 시작하는 클래스들은 모두 app.js파일에서 제거하였습니다. 그리고 브라우저를 통해 다시 실행해봅니다. 이상없는걸 보니 제 생각이 맞는것 같습니다. 일단 이상태의 소스를 카피하여 남겨 놓습니다.

그런데 Ext클래스들을 제거 했는데도 소스량이 아직도 상당히 많아 보여서 확인 해보니 CodeMirror라는 놈이 있습니다. 소스의 맨처음에 시작하여 4천라인정도를 사용하고 있습니다. 일단 제거 해보고 나중에 필요하다면 다시 넣는것으로 하겠습니다.

이제 소스코드 량은 4천 라인정도로 실제 필요한 내용들만 남기고 제거가 되었습니다. 이 상태의 소스도 남겨 놓습니다.

이제 하나씩 프로그램을 다시 조립해 보겠습니다.

우선 app.js의 내용을 모두 삭제합니다. index.html도 내용을 모두 정리합니다. 아래 처럼요 그래야 처음 부터 하나씩 조립해서 완성품을 만들 수 있겠죠!


이제 웹브라우저를 통해 실행해보면 아무것도 보이지 않습니다. 브라우저는 항상 파이어폭스를 쓰는게 좋습니다. 구글크롬도 좋지만 디버그 모드의 활용은 파이어폭스쪽이 좀더 나아 보입니다.
이제 본격적으로 app.js파일에 코딩을 하고 프로그램을 만들어봅니다. 제일 중요한 것은 이 어플리케이션의 layout이 어떻게 구성되어 있느냐를 파악해야 합니다. 해서 뼈대 부터 만들고 하나씩 뼈대위에 살을 붙여 보도록 하겠습니다.
아래 그림은 Doc 어플리케이션의 뼈만 앙상한 모습입니다.

Posted by 베니94
자바스크립트/Ext JS2011. 12. 30. 13:09

이제 실제 소스는 어떤지 확인해보겠습니다.
아래 그림처럼 docs폴더 구조를 확인 할 수 있습니다.


여기서 가장 중요한 역할을 하는 파일은 index.html과 app.js파일 입니다. 일단 두개의 파일은 분석하려면 수정해야 하니 하나씩 복사본을 만들어 놓았습니다.

우선 어플리케이션 구성에 대한 해답은 app.js파일에서 얻어야 할 듯 합니다. 이클립스는 무거우니 울트라로 열어보겠습니다. app.js를 열어보니 아래 그림처럼 도무지 해석하기 힘든 형태로 되어 있습니다. 마치 ext-all.js파일을 열었을때와 똑 같죠~ 아마도 최적화를 툴을 통해서 한것 같습니다. 아니면 이렇게 나올수 없겠죠. ExtJS 는 Sench SDK를 통해서 배포용 파일을 만들게 되는데 doc app 도 그러했겠죠~ 어찌 되었던 제게는 해석 가능한 소스가 필요해서 여기저기 폴더를 모두 뒤져보았지만 원 소스는 찾지 못하였습니다.


그렇다고 방법이 없는 것은 아닙니다. 전에 Web Desktop 소스를 활용해서 프로젝트에 적용할 때 이런 문제를 역 컴파일러를 통해 해결한 적이 있습니다. 위와 같은 소스를 아래의 그림과 같이 말끔하게 정리되게 끔 할 수 있습니다.



http://www.jsbeautifier.org/ 로 접속하면 아래 처럼 최적화된 자바스크립트 코드를 원상태로 알아볼 수 있도록 변환해줍니다.


app.js를 카피하여 붙여넣자.


상단의 변환 버튼을 클릭하면 아래처럼 변환되어 알아보기 쉬운 코드로 바꿔준다.


이제 변환된 코드를 통해 어플리케이션을 실행해보면 변환된 코드가 정상적인지 확인 할 수 있을 것입니다. 원본 상태의 app.js를 카피하여 복사본을 만들고 변환된 코드 내용을 app.js에 복사해 넣습니다. 다시 실행해보면 정상적으로 아무런 이상없이 실행되는 것을 볼 수 있습니다.
Posted by 베니94