본문 바로가기
Ext JS

12 MVVM 아키텍처

by haheehee 2023. 8. 31.
  • MVC 대체하기 위한 아키텍처
  • MVC : 각 뷰를 통제하는 controller를 제어하기 어렵고, view와 controller가 N:N구조로 만들어지다 보면 복잡
  • Model과 Controller를 View에 종속적인 구조로 구성한 View기준 아키텍처.
  • View가 종료되면 ViewController와 ViewModel은 같이 소멸.

→ ViewModel

뷰가 보여주는 화면을 대신.

통신을 위한 데이터 레이아웃이 아니다!

→ ViewController

뷰의 이벤트나 필요한 메소드를 구현해 view와 상호 연동.

전역적인 글로벌 controller와는 달리, 자신과 연결된 View에 한하며 이벤트나 참조 등의 구현이 단순

  1. Reference 이용
  2. 데이터 바인딩 이용 : viewModel의 데이터를 이용

기본 구성 만들기

  • cmd창에 sencha generate app -ext MemberApp ./MemberApp
  • cd MemberApp
  • sencha app watch
  • C:\Workspace\workspaceExtjs\MemberApp\app\view\main 경로에 Main.js 생성
Ext.define('MemberApp.view.main.Main', {
    extend: 'Ext.container.Container',
    xtype: 'main',
    controller: 'main',
    viewModel: {
        type: 'main'
    },
    layout: 'border',  
    defaults: {
        border: true
    },
    items:[{
        region: 'north',
        height: 50,
        bodyStyle: 'background-color: black',
        html: '<h2><font color="white">&nbsp;&nbsp;&nbsp;회원관리</font></h2>',
    }, {
        region: 'west',
        title: '메뉴',
        html: '메뉴',
        collapsible: true,
        width: 200
    }, {
        region: 'center',
        xtype: 'tabpanel',
        name: 'mainbar',
        items: [
        {
            title: '메뉴1',
            html: '컨텐트 위치'
        }]
    }, {
        region: 'south',
        height: 30,
        html: '도움말',
    }],
});


메뉴만들기

  • 메뉴는 트리패널로 구성
  • 트리패널에 보여줄 items는 resources폴더에 트리뷰에 알맞은 파일을 위치시키고 proxy를 이용해 호출해 사용
  • MVVM 아키텍처에서 기본적으로 이벤트나 동작시키려는 알고리즘은 ViewController에 위치시키는 것이 일반적
  • ViewController에 함수를 생성하고 View에서 ViewController의 함수를 호출하는 방법 : 이벤트의 이름을 해당 이벤트에 정의하는 것
listeners: {
    itemclick: 'onMenuClick'
}
  • ViewController의 onMenuClick 메소드를 호출하게 됨

→ C:\Workspace\workspaceExtjs\MemberApp\app\view\menu 경로에 LeftMenu.js 생성

Ext.define('MemberApp.view.menu.LeftMenu', {
    extend:'Ext.tree.TreePanel',
    alias:'widget.leftmenu',
    requires:['MemberApp.view.menu.LeftMenuController'],
    controller:'leftmenu',
    width:200,
    title:'메뉴입니다',
    rootVisible:false,
    displayField:'name',
    useArrows:true,
    store:{
        type:'tree',
        fields:['name','url'],
        proxy:{
            type:'ajax',
            url:'./resources/data/LeftMenu.json',
            reader:{
                type:'json',
            }
        },
        autoload:true,
    },
    listeners:{
        itemclick:'onMenuClick'
    }
});
  • onMenuClick 이벤트가 연동
  • autoload: true → LeftMenu가 보여주는 트리구조의 데이터는 프로젝트의 resources 폴더에 위치. 위치시킨 데이터를 불러오기 위해 설정하는 속성, 파일 리스트는 LeftMenu.json

→ C:\Workspace\workspaceExtjs\MemberApp\app\view\menu 경로에 LeftMenuController.js 생성

Ext.define('MemberApp.view.menu.LeftMenuController',{
    extend:'Ext.app.ViewController',
    alias:'controller.leftmenu',
    onMenuClick:function(obj, selObj){
        Ext.Msg.alert("선택", selObj.data.url);
    }
});
  • 이벤트가 발생하면 alert창

→ C:\Workspace\workspaceExtjs\MemberApp\resources\data 경로에 LeftMenu.json

{
    "children":[
    {
        "name":"사용자",
        "url":"",
        "leaf":false,
        "children":[{
            "name":"사용자조회",
            "url":"MemberApp.view.user.UserShow",
            "leaf":true
        },{
            "name":"사용자목록조회",
            "url":"MemberApp.view.user.UserShowList",
            "leaf":true
        },{
            "name":"사용자등록",
            "url":"MemberApp.view.user.UserReg",
            "leaf":true
        },{
            "name":"사용자변경",
            "url":"MemberApp.view.user.UserUpd",
            "leaf":true
        }]
    },{
        "name":"메뉴",
        "url":"",
        "leaf":false,
        "children":[{
            "name":"메뉴1",
            "url":"...",
            "leaf":true
        },{
            "name":"메뉴2",
            "url":"...",
            "leaf":true
        },{
            "name":"메뉴3",
            "url":"...",
            "leaf":true
        }]
    }]
}

→ Main.js 수정

Ext.define('MemberApp.view.main.Main', {
    extend: 'Ext.container.Container',
    requires:[
        'MemberApp.view.menu.LeftMenu'
    ],
    xtype:'main',
    controller:'main',
    viewModel:{
        type:'main'
    },
    layout:'border',  
    defaults:{
        border:true,
    },
    items:[{
        region:'north',
        height:50,
        bodyStyle:'background-color:black',
        html:'<h2><font color="white">&nbsp;&nbsp;&nbsp;회원관리</font></h2>',
    }, {
        region:'west',
        xtype:'leftmenu',
        collapsible:true,
        width:200
    }, {
        region:'center',
        xtype:'tabpanel',
        name:'mainbar',
        items:[{
            title:'메뉴1',
            html:'컨텐트 위치'
        }]
    }, {
        region:'south',
        height:30,
        html:'도움말',
    }],
});


Reference를 이용한 제어

  • 사용자 조회를 Reference를 이용해 구현
  • MVVM 아키텍처는 ViewController가 View에 접근하기 위한 두 가지 방법 제공
    • Reference 이용한 객체의 접근
    • bind를 이용한 공유 방법

지금 여기서는 Reference를 이용한 객체의 접근 방법을 사용

  • Reference는 객체를 바로 지정하여 사용 (필요시 객체를 제어하여 사용)
  • this.getReference를 사용 (Ext.ComponentQuery.query 대신)

→ C:\Workspace\workspaceExtjs\MemberApp\app\view\user 경로에 UserShow.js 생성

  • userId를 입력받아 닉네임, password, email 위젯을 배치하고, 속성에 reference 추가
Ext.define('MemberApp.view.user.UserShow', {
    extend:'Ext.panel.Panel',
    alias:'widget.usershow',
    requires:['MemberApp.view.user.UserShowController'],
    controller:'usershow',
    title:'사용자 조회',
    bodyPadding:'5 5 5 5',
    closable:true,
    items:[{
        xtype:'fieldset',
        layout:'hbox',
        padding:'5 5 5 5',
        items:[{
            xtype:'textfield',
            name:'userId',
            fieldLabel:'사용자 ID',
            labelAlign:'right',
            reference:'userId'
        },{
            xtype:'tbspacer',
            flex:1
        },{
            xtype:'button',
            text:'조회',
            handler:'onSearch'
        }],
    },{
        xtype:'fieldset',
        padding:'5 5 5 5',
        items:[{
            xtype:'textfield',
            fieldLabel:'닉네임',
            labelAlign:'right',
            reference:'nickName',
            name:'nickName'
        },{
            xtype:'textfield',
            fieldLabel:'이메일',
            labelAlign:'right',
            reference:'email',
            name:'email'
        }]
    }]
});

→ C:\Workspace\workspaceExtjs\MemberApp\app\view\user 경로에 UserShowController.js 생성

  • view에서 각 위젯에 부여한 reference속성을 불러오기 위하여 viewController는 this.lookupReference를 이용해 접근
  • F12-local storage를 사용하기 때문에 localStorage.getItem(’키’);를 사용하여 로컬 저장소에 저장된 정보를 불러옴
Ext.define('MemberApp.view.user.UserShowController', {
    extend:'Ext.app.ViewController',
    alias:'controller.usershow',
    onSearch:function(obj, selObj){
        var me = this;

        //var userId = this.lookupReference('userId').getValue();
        var userId = localStorage.getItem('userId');
        var password = localStorage.getItem('password');
        var nickName = localStorage.getItem('nickName');
        var email = localStorage.getItem('email');

        var userIdField = me.lookupReference('userId'); // textfield 컴포넌트 찾기
        var currentUserId = userIdField.getValue(); // 현재 입력된 값 가져오기

        //if (userId) {
        if (userId === currentUserId) {
            me.lookupReference('nickName').setValue(nickName);
            me.lookupReference('email').setValue(email);
            Ext.Msg.alert("확인", "조회완료!");
        } else {
            Ext.Msg.alert("오류", "사용자 정보를 찾을 수 없습니다.");
        }
    }
});

→ Main 수정

  • Main 패널안에 탭 패널이 있으므로, 탭 패널에 UserShow화면을 사용할 수 있도록 requires하고 xtype 추가
Ext.define('MemberApp.view.main.Main', {
    extend: 'Ext.container.Container',
    requires:[
        'MemberApp.view.menu.LeftMenu',
        'MemberApp.view.user.UserShow'
    ],
    ...
    items:[
			...
			,{
        region:'center',
        xtype:'tabpanel',
        name:'mainbar',
        items:[{
            xtype:'usershow'
        },{
            title:'메뉴1',
            html:'컨텐트 위치'
        }]
    }, 
	...
});


Form Submit 구현

  • Controller에 전달하여 Form Submit 구현
  • ServerPage 폴더에 RegUser.jsp파일 생성
  • 사용자 정보 등록은 F12(DevTools)의 Application에서 로컬 저장소를 사용
// 등록 버튼 클릭 시 처리 로직
var values = form.getForm().getValues();

// 로컬스토리지에 사용자 정보 저장
localStorage.setItem('userId', values.userId);
localStorage.setItem('password', values.password);
localStorage.setItem('nickName', values.nickName);
localStorage.setItem('email', values.email);
Ext.Msg.alert("등록되었습니다.");
  • 등록한 사용자 정보를 사용할 때
var userId = localStorage.getItem('userId');
var password = localStorage.getItem('password');
var nickName = localStorage.getItem('nickName');
var email = localStorage.getItem('email');

→ C:\Workspace\workspaceExtjs\MemberApp\app\view\user 경로에 UseReg.js 생성

  • 사용자 등록을 위한 파일
  • form으로 위젯의 데이터를 전송하기 위하여 submit 사용
  • form에 위치한 위젯의 name을 지정하고, ViewController는 form만 읽어 처리
Ext.define('MemberApp.view.user.UserReg', {
    extend:'Ext.form.Panel',
    alias:'widget.userreg',
    requires:['MemberApp.view.user.UserRegController'],
    controller:'userreg',
    title:'사용자 등록',
    bodyPadding:'5 5 5 5',
    closable:true,
    items:[{
        xtype:'fieldset',
        title:'입력하세요',
        items:[{
            xtype:'textfield',
            fieldLabel:'사용자 ID',
            labelAlign:'right',
            allowBlank:false,
            name:'userId'
        },{
            xtype:'textfield',
            fieldLabel:'닉네임',
            labelAlign:'right',
            allowBlank:false,
            name:'nickName'
        },{
            xtype:'textfield',
            fieldLabel:'패스워드',
            labelAlign:'right',
            allowBlank:false,
            name:'password'
        },{
            xtype:'textfield',
            fieldLabel:'이메일',
            labelAlign:'right',
            allowBlank:false,
            name:'email'
        }],
    }],
    buttons: [{
        text:'Reset',
        handler:'onReset'
    },{
        text:'Submit',
        formBind:true,
        disabled:true,
        //handler:'onSubmit'
        handler: function() {// 등록 버튼 클릭 시 처리 로직
            var form = this.up('form'); // 폼 컴포넌트 참조
            var values = form.getForm().getValues();
            
            // 로컬스토리지에 사용자 정보 저장
            localStorage.setItem('userId', values.userId);
            localStorage.setItem('password', values.password);
            localStorage.setItem('nickName', values.nickName);
            localStorage.setItem('email', values.email);
            Ext.Msg.alert("등록되었습니다.");
        }
    }]
});

→ C:\Workspace\workspaceExtjs\MemberApp\app\view\user 경로에 UserRegController.js 생

  • 현재의 view는 form → this.getView().getForm()
Ext.define('MemberApp.view.user.UserRegController', {
    extend:'Ext.app.ViewController',
    alias:'controller.userreg',
    onSearch:function(){
        var form = this.getView().getForm();
        if(form.isValid()) {
            form.submit({
                success: function(form, action) {
                    Ext.Msg.alert('Success', action.result.msg);
                },
                failure: function(form, action) {
                    Ext.Msg.alert('Failed', action.result.msg);
                }
            });
        }
    },
    onReset: function() {
        var form = this.getView().getForm().reset();
    }
});

→ Main.js 수정

Ext.define('MemberApp.view.main.Main', {
    extend: 'Ext.container.Container',
    requires:[
        'MemberApp.view.menu.LeftMenu',
        'MemberApp.view.user.UserShow',
        'MemberApp.view.user.UserReg'
    ],
    ...
    }, {
        ...
        items:[{
            xtype:'usershow'
        },{
            xtype:'userreg'
        },
				...
    }, ...
	],
});


  • form으로 감싸고 form에 reference를 지정한 후, 상위 객체인 form을 읽어오며 form에 포함된 위젯들은 down()을 사용하여 읽을 수 있음

→ this.lookupReference(’form’).down(’textfield[name=이름]’)

  • (주의 : form이 최상위 뷰로 선언되었다면 reference 부여 불가)

 

https://hhahee.notion.site/12-MVVM-0ea4448b1aca46b48ee84a6c74a1a917?pvs=4 

 

12 MVVM 아키텍처

MVC 대체하기 위한 아키텍처

hhahee.notion.site

 

'Ext JS' 카테고리의 다른 글

07 위젯(그리드 & 트리) draganddrop  (0) 2023.08.31
04 레이아웃  (0) 2023.08.31
03 Ext JS 동작의 기본  (0) 2023.08.31
02 클래스  (0) 2023.08.31
Ext JS 시작해보기  (0) 2023.08.30

댓글