이번 강좌에서는 게시판 애플리케이션의 주요 로직을 구현해 보도록 하자. 게시판의 로직을 구현하기 위해 Store와 모델에 의해 데이터를 로드하고 입력, 수정, 삭제가 가능하도록 코딩한다.
연재순서
1회 | 2013.09 | ExtJS4 클래스 시스템의 이해
2회 | 2013.10 | ExtJS4 MVC 아키텍처의 이해
3회 | 2013.11 | 시스템 뼈대 구현하기
4회 | 2013.12 | ExtJS4 실전코딩 : 멀티 게시판 애플리케이션 구현(1)
5회 | 2014.01 | ExtJS4 실전코딩 : 멀티 게시판 애플리케이션 구현(2)
이전 시간에 우리는 메뉴를 통해 동일한 형태의 게시판이지만 관리아이디가 다른 게시판 애플리케이션을 중복으로 실행하여 멀티게시판의 형태를 구성하였다. 이제 게시판의 구체적인 기능을 구현하도록 한다.
게시판 리스트가 데이터를 불러올 수 있도록 BoardList클래스를 <리스트 1>과 같이 render된 시점에 Store를 로드하도록 수정하자.
<리스트 1> BoardList클래스 렌더시 데이터 로드
me.on('render', function () {
me.store.proxy.extraParams = {
brd_category_cd: this.brd_category_cd
}
me.store.load();
});
Store클래스인 MyMvc.store.Boards를 통해 리스트를 제공받는다. 프록시의 read에 해당하는 url인 board.do?boards에 의해 전달받는다. 이는 아래의 json결과를 반환하고 BoardList클래스에 표현된다.
<리스트2> board.do?board에 제공되는 json데이터
{
success: true,
totalCount: "2",
entitys: [
{
brd_title: "테스트..",
brd_seq: 11,
brd_input_user_nm: "홍길동",
brd_read_cnt: 10,
brd_input_date: "2013-09-04 07:31:30",
brd_content: "test"
},
..
]
}
<그림1>은 그리드에 출력된 데이터를 보여주고 있다.
<그림 1> 게시물 리스트
다음은 그리드를 클릭 할 경우와 “글쓰기” 버튼을 클릭 할 경우를 구현하자. <리스트 3>와 Board 컨트롤러 코드를 수정하자.
<리스트 3> MyMvc.controller.Board.js
Ext.define('MyMvc.controller.Board', {
…
init : function(app) {
this.control({
'boardlist button[action=board.create]' : { // #1
click: this.newBoard // #2
},
'boardlist' : { // #3
itemclick : this.onBoardSelect // #4
}
});
},
onBoardSelect : function(grid, record, index) {
var fp = grid.up('boardmain').down('boardview'); // #5
if (fp.collapsed) { // #6
fp.expand(); // #7
}
fp.fireEvent('setFormData', record); // #8
},
newBoard: function(button){
var fp = button.up('boardmain').down('boardview'); // #9
if (fp.collapsed) {
fp.expand();
}
fp.fireEvent('setFormData', Ext.create('MyMvc.model.Board')); // #10
}
});
코드를 상세히 설명하기로 하자.
#1. boardlist는 게시물 리스트를 보여주는 그리드패널이다. 이 패널 내부의 버튼 중 action속성이 create인 버튼이 클릭(#2)이벤트를 발생 시킬 경우를 리스닝한다.
#2. 클릭 이벤트가 발생하면 컨트롤러 내부의 newBoard메소드를 실행시킨다.
#3. 게시물 리스트
#4. 게시물 리스트에서 itemclick이벤트가 발생하면 onBoardSelect메소드를 실행 시킨다.
#5. 게시물 상세내역을 보여주는 패널을 변수에 담는다.
#6. 패널이 접혀 있다면 #7과 같이 펼쳐지도록 한다.
#8. 게시물 상세내역 패널에 이벤트를 발생시키고 인자로 그리드의 클릭 된 모델객체를 전달한다.
#9. 새로운 게시물을 등록하는 메소드로 게시물 입력 패널(상세내역패널과 동일)을 변수에 담는다.
#10. setFormData이벤트를 발생시키고 비어있는 Board모델 객체를 전달한다.
이제 게시물 정보를 입력하고 보여주는 BoardView클래스에 setFormData이벤트를 추가하고 저장기능과, 삭제 기능을 처리할 메소드를 추가하도록 하자.
<리스트 4> BoardView.js
Ext.define('MyMvc.view.board.BoardView', {
..
initComponent: function () {
var me = this;
Ext.apply(this, {
items: [
{
xtype: 'form',
bodyPadding: 10,
itemId: 'board.form',
layout: 'anchor',
defaults: {
anchor: '100%'
},
buttons: [
{
xtype: 'statusbar',
defaultText: ' ',
flex: 1
},
{
action: 'board.save',
text: 'Save',
scope: me,
handler: function () {
this.onSave(); // #17
}
},
{
action: 'board.remove',
text: 'Del ',
scope: me,
handler: function () {
this.onRemove(); // #18
}
} ,
..
],
.. 중략 ..
}]
});
this.callParent(arguments);
this.on('setFormData', function (rec) { // #1
me.down('form[itemId=board.form]').getForm().reset(); // #2
me.down('form').loadRecord(rec); // #3
});
},
onSave: function () { // #4
var me = this;
var record = this.down('form').getForm().getRecord(); // #5
this.down('form').getForm().updateRecord(record); // #6
record.save({ // #7
success: function (a, b) { // #8
var pers = Ext.ModelMgr.getModel('MyMvc.model.Board');// #9
pers.load(record.get('brd_seq'), { // #10
success: function (record, operation) { // #11
me.down('form').loadRecord(record); // #12
me.up('boardmain').down('boardlist').insertRecord(record.data);// #13
}
});
}
});
},
onRemove: function () { // #14
var me = this, form = this.down('form').getForm(),
record = form.getRecord();
form.updateRecord(record);
me.collapse(); // #15
record.destroy(); // #16
}
});
코드를 자세히 설명하도록 하자.
#1. setFormData 이벤트가 이 클래스에서 발생할 경우 리스닝하도록 했다.
#2. 내부 폼패널을 사용하기 전에 reset한다.
#3. setFormData 이벤트는 인자로 데이터가 담긴 모델객체를 전달받는다. 이 전달받은 모델을 loadRecord메소드를 통해 폼에 세팅한다.
#4. 폼패널을 저장하는 메소드이다.
#5. 폼패널에 입력된 값을 갖는 모델객체를 변수에 담는다.
#6. updateRecord메소드에 입력된 값을 갖는 모델객체를 전달하여 폼패널에 세팅하고 #5의 모델데이터의 수정을 완료한다.(클라이언트에서의 완료를 의미함) updateRecord메소드를 꼭 호출해야함.
#7. 모델의 프록시 설정을 통해 서버에 저장을 요청한다.
#8. 저장 완료 후 호출된다.
#9. 저장한 데이터를 다시 불러오기 위해 모델 객체를 생성한다.
#10. 변경 된 조회수와, 수정일 등을 알아오기 위해 모델의 load메소드에 현재 게시물의 아이디를 전달해 최종 데이터를 불러온다.
#11. 모델의 load메소드의 호출이 성공하면 호출된다.
#12. loadRecord메소드를 통해 폼패널에 데이터를 다시 전달한다.
#13. 게시물리스트 그리드 패널에도 수정, 입력된 데이터를 반영하도록 한다.
#14. 게시물 삭제 처리용 메소드이다.
#15. 게시물 상세보기화면을 접도록 한다.
#16. 모델객체에 접근하여 destroy메소드를 호출 서버에서 삭제처리 한다.
#17. 저장 버튼을 클릭 할 경우 onSave메소드를 호출한다.
#18. 삭제 버튼을 클릭 할 경우 onRemove메소드를 호출한다.
게시물 리스트 클래스인 BoardList에 insertRecord메소드를 구현하자. 이 메소드는 게시물의 저장버튼에 반응하여 수정, 신규저장 된 폼패널의 내용을 좌측 그리드패널에 반영되도록 하는 역할을 한다.
insertRecord: function (record) {
index = this.store.findExact('brd_seq', record.brd_seq); // #1
if (index != -1) { // #2
var rs = this.store.getAt(index); // #3
rs.set(record); // #4
return; // #5
}
this.store.insert(0, record); // #6
}
#1. findExact메소드는 Store내부에서 원하는 필드에 값을 가진 데이터가 존재하는지 확인하는 메소드이다. 이 메소드에 인자로 전달받은 데이터의 brd_seq값을 넘겨 index변수에 저장한다.
#2. 검색된 결과가 존재하지 않으면 -1을 반환하므로 존재한다는 조건식은 if(index != -1)이고 조건식에 맞는 결과가 있다면 index변수에 결과에 맞는 모델객체는 Store내부에 몇 번째인지 index변수에 전달한다.
#3. index변수는 검색결과에 매칭되는 데이터의 순번을 가지므로 해당 순번으로 Store내부의 모델객체를 rs변수에 전달한다.
#4. 외부에서 전달한 데이터를 Store내부 모델객체에 세팅한다. 이렇게 하여 외부의 새로운 값을 Store에 반영하는 것이다.
#5. 여기까지는 수정에 대한 처리이다. Store내부에 외부에서 전달한 데이터의 brd_seq값이 존재하였으므로 수정행위로 보고 이후에 this.store.insert 로직이 실행되지 않도록 한다.
#6. index변수에 -1이 전달될 경우 즉 외부에서 전달한 데이터의 brd_set값이 Store내부에 존재하지 않으면 Store의 첫 번째로 신규 입력한다.
이제 코드를 실행하고 테스트를 진행하자.
게시물 리스트를 클릭하면 Board컨트롤러가 itemclick이벤트를 감지하여 onBoardSelect메소드를 호출하고 이 메소드는 BoardView클래스에 클릭한 모델데이터를 전달하여 폼패널에 데이터를 보여지도록 하였다.
<그림 2> 게시물 선택
“글쓰기” 버튼을 클릭하자. 글쓰기 버튼이 클릭되면 setFormData이벤트를 호출하고 인자로 비어 있는 모델객체(Ext.create('MyMvc.model.Board'))를 전달한다. 폼패널에 이 빈 모델 객체가 loadRecord메소드에 세팅되면 폼은 빈 상태로 사용자의 입력을 기다리게 된다.
데이터를 모두 채우고 ‘Save’버튼을 클릭하여 게시물을 저장하자. ‘Save’버튼이 클릭 되면 onSave메소드가 호출되고 이 메소드는 폼패널에서 모델객체를 추출하여 record.save() 메소드를 실행한다. 여기서 폼패널의 데이터가 입력이든 수정이든 record.save()메소드가 실행되는 것은 동일하다. 그렇다면 모델의 api설정 중 craete와 update 중 하나를 실행하여 입력과 수정작업이 이루어지는데 무엇으로 판단하는 것일까? 모델의 save메소드는 모델내부의 idProperty의 필드 값의 존재 유무에 따라, 즉 brd_seq값이 있다면 update이고 없다면 craete로 실행되는 것이다.
좌측 게시물을 선택하여 해당 모델객체를 우측 패널에 로드 할 경우 당연히 brd_seq값이 존재할 것이고 “글쓰기”버튼을 클릭하여 빈 모델객체를 우측 패널에 로드 할 경우 brd_seq값은 비어 있게 되므로 이러한 특성을 이용하여 입력과 수정을 한가지 로직으로 처리하게 된다.
<그림 3>은 데이터를 수정할 경우 Ajax호출 모습이다. record.save()메소드는 수정로직을 처리한 이후 수정한 데이터를 다시 로드 한 뒤 폼패널에 세팅하는 과정을 거쳐 서버에서 최종 업데이트 된 내용을 클라이언트에서 재확인 시켜준다.
<그림 3> 게시물 수정작업 결과
이번에는 입력처리에 대한 결과를 확인하자. 입력처리도 수정과 마찬가지 과정을 거친다. record.save()메소드를 실행하면 ExtJS는 모델객체 내부에 brd_seq값이 존재하지 않으므로 api의 create가 실행되고 입력작업을 서버에 요청한다. 이후 요청이 성공하면 입력된 데이터를 다시 로드하여 폼패널에 세팅하고 게시판리스트에 신규 데이터를 추가하도록 하였다.
<그림4> 게시물 입력작업 결과
마지막으로 삭제 기능에 대해 설명하자. 삭제 기능 또한 모델클래스의 destroy메소드를 이용하여 처리하였다. destroy메소드는 아래 <그림 5>와 같이 모델 api설정 중 destroy가 실행되고 모델객체의 데이터가 서버에 전송되므로 서버에서는 brd_seq값으로 해당 테이블의 데이터를 삭제 처리하면 되겠다. destory메소드가 호출되면 데이터베이스에 실제 데이터가 정상적으로 삭제 처리되면 이후 좌측 게시물리스트 그리드에서도 해당 데이터가 삭제되는 것을 확인 할 수 있다.
<그림 5> 삭제 처리 시 Ajax전송 처리 결과
정리하며
지금까지 총 5회에 걸쳐 ExtJS를 이용하여 멀티 게시판을 구현해 보았다. 강좌에서 핵심적으로 봐야 할 부분은 UI를 확장한 어떤 클래스도 메뉴를 통해 중앙영역에 추가할 수 있는 구조와, 동일한 애플리케이션 클래스를 여러 개 실행하여 각기 다른 데이터를 다루도록 하는 것이다.
우리는 이러한 모든 것을 클래스화 하였기에 가능하다는 것을 알게 되었다. 클래스의 개념은 ExtJS가 가지는 가장 중요한 개념이고 복잡하고 큰 애플리케이션을 자바스크립트로 개발할 수 있게되었다.
'자바스크립트 > Ext JS' 카테고리의 다른 글
ExtJS 5 멀티테마와 로케일 설정 (1) | 2014.08.18 |
---|---|
[ExtJS 강좌] Data Package(모델과 스토어) 알아보기 (0) | 2014.01.04 |
[ExtJS4 MVC 실전강좌] 멀티게시판 애플리케이션 구현(1) (1) | 2013.11.20 |
[ExtJS4 MVC 실전 강좌] 시스템 뼈대 구현하기 (14) | 2013.10.13 |
[ExtJS4 MVC 실전 강좌] ExtJS4 MVC 아키텍처의 이해 (1) | 2013.10.13 |