작업 테이블(TBL_TASK)은 다음과 같이 구성되었다.

CREATE TABLE PRJ_TASK(
    PRNO             INT          NULL  COMMENT '프로젝트 번호',
    TSNO             BIGINT       NOT NULL AUTO_INCREMENT COMMENT '업무번호',
    TSPARENT       BIGINT       NULL  COMMENT '부모업무번호',
    TSSORT          MEDIUMINT(10) NULL  COMMENT '정렬',
    TSTITLE          VARCHAR(100) NULL  COMMENT '업무 제목',
    TSSTARTDATE   VARCHAR(10)  NULL  COMMENT '시작일자',
    TSENDDATE      VARCHAR(10)  NULL  COMMENT '종료일자',
    TSRATE          SMALLINT     NULL  COMMENT '진행율',
    DELETEFLAG     CHAR(1)      NULL  COMMENT '삭제'
) COMMENT='프로젝트 업무';


테이블 구조와

treegrid 예제에서 사용된 필드 구조를 비교하면

이름이 조금씩 다를 뿐

구조는 같다는 것을 알 수 있다.
treegrid 예제PRJ_TASK
키 필드id작업 번호TSNO
이름name작업 명TSTITLE
시작일자begin시작일자TSSTARTDATE
종료일자end종료일자TSENDDATE
진행율progress진행율TSRATE
부모parentid부모TSPARENT
작업자persons별도 테이블PRJ_TASKUSER

따라서, 매칭되는 이름으로 바꾸기를 진행한다.

바꾸기는 소문자로 한번씩만 진행하면 된다.

다만, treegrid에서 사용된 필드가 한단어 영어로

id, name등은 HTML에서 사용하는 문자이다.

따라서 replaceAll로 한번에 수정하면 안된다.

하나씩 찾아서 바꾸어 준다.

(변수명등은 한단어 영어로 작성하지 않는것이 좋다.)


하나씩 바꿀때 좋은 에디터가 Notepad++이다.

문자열을 선택하면 파일내에 있는 같은 문자열을 표시해줘서

쉽게 식별할 수 있다.

이방법을 쓰거나 하나씩 replace해서 표와 같이 바꾸어 준다.

수정 부분은 데이터 지정부분(dataset), 추가(append) 부분과

treegred설정 부분(table), treegred 출력 부분(table태그의 th)이다.

treegred의 필드명은 알맞게 수정하거나 그대로 사용한다.

다국어 처리를 하는 것이 좋겠지만

개발 방법과 관련되지 않고 어렵지 않아 넘어간다.

이렇게 구성한 코드를

포함할 외부 파일, 자바스크립트, HTML의

3가지로 구분해서

프로젝트 읽기 파일에(ProjectRead.jsp) 복사한다.

프로젝트 읽기 파일에서

개별 작업을 추가하고 삭제하는 등의 관리를 할 것이다.


먼저, 가지고올 CSS와 JS 등의 외부 파일 정보를

ProjectRead.jsp에 추가한다.

<link rel="stylesheet" type="text/css" href="../../themes/default/easyui.css">
<link rel="stylesheet" type="text/css" href="../../themes/icon.css">
<link rel="stylesheet" type="text/css" href="../demo.css">
<script type="text/javascript" src="../../jquery.min.js"></script>
<script type="text/javascript" src="../../jquery.easyui.min.js"></script>

외부 파일의 경로를 보면 모두 ".." 으로 상대 경로를 지정하고 있다.

현재 개발하고 있는 과제 관리 시스템에서는 이와 같은 경로가 없다.

jquery.min.js파일을 제외하고

나머지 4개 파일을 코드에서 작성한 디렉토리에서 찾아서

과제관리 시스템 프로젝트 웹 폴더에 넣어준다.

여기에서는 webapp 하위에 js 폴더에

자바스크립트 파일을 모아 두었다.

그림과 같이 js 폴더 하위에 easyui 폴더를 생성해서 복사한다.


복사한뒤, 다음 코드와 같이 수정한다.

과제 관리 시스템(실제로는 Project9)에서

JQuery(jquery-2.2.3.min.js)를 사용하고 있기 때문에

jquery.min.js는 제외한다.

    <link rel="stylesheet" type="text/css" href="js/easyui/easyui.css">
    <link rel="stylesheet" type="text/css" href="js/easyui/icon.css">
    <link rel="stylesheet" type="text/css" href="js/easyui/demo.css">
    <script type="text/javascript" src="js/easyui/jquery.easyui.min.js"></script>

생성한 easyui 폴더에

다운로드 받은 easyui파일 중에서

jquery-easyui-1.5.1\themes\default 경로에 있는

images 폴더를 복사해서 붙여 넣어야 한다 [위 그림 참조].

easyui(icon).css에서 참조하는 이미지이다.


다음으로,

자바스크립트 코드로 작성된 부분을 복사해서

ProjectRead.jsp의 HEAD 태그 안에 붙여 넣는다.


마지막으로 HTML 코드를 복사해서

ProjectRead.jsp에서 적당한 위치에 붙여넣기 한다.

다만, 과제 관리 시스템은

시각적이 부분을 bootstrap을 기반으로 하기 때문에

다음과 같이 div 태그에 row클래스를 넣어서 사용해야 한다.

bootstrap에서 한 행을 사용한다는 의미이다.

            <div class="row">
                <div style="margin:20px 0;">
                    <a href="javascript:void(0)" class="easyui-linkbutton" onclick="edit()">Edit</a>
                    <a href="javascript:void(0)" class="easyui-linkbutton" onclick="save()">Save</a>
                    <a href="javascript:void(0)" class="easyui-linkbutton" onclick="cancel()">Cancel</a>
                    <a href="javascript:void(0)" class="easyui-linkbutton" onclick="append()">append</a>
                    <a href="javascript:void(0)" class="easyui-linkbutton" onclick="removeIt()">remove</a>
                </div>
                <table id="tg" class="easyui-treegrid" style="width:700px;height:250px"
~~ 생략 ~~
                    <thead>
                        <tr>
                            <th data-options="field:'tstitle',width:180,editor:'text'">Task Name</th>
                            <th data-options="field:'persons',width:60,align:'right',editor:'numberbox'">Persons</th>
                            <th data-options="field:'tsstartdate',width:80,editor:'datebox'">Begin Date</th>
                            <th data-options="field:'tsenddate',width:80,editor:'datebox'">End Date</th>
                            <th data-options="field:'tsrate',width:120,formatter:formatProgress,editor:'numberbox'">Progress</th>
                        </tr>
                    </thead>
                </table>           
            </div>

웹 브라우저에서 다음 주소로 접속해서

잘 되는지 확인한다.

                http://localhost:8080/pms9/projectRead?prno=과제번호

다음 그림을 보면

윗 부분은 지정된 프로젝트에 대한 정보와 관리할 수 있는 링크가 있고,

아랫 부분은 작업을 관리할 수 있는 기능이 구현되는 것을 볼 수 있다.

공개한 PMS9에서는 이 버튼들을 좀더 보기 좋게 정리했지만

여기서는 개발 방법을 정리하는 것이라 그냥 넘어 간다.


실행에 문제가 없으면

몇가지 추가 작업을 진행한다.

먼저, 해당 작업에 대한 담당자를 지정해야 한다.

담당자는 각 작업에 대한 진행 상황(progress, tsrate)을 입력한다.

여기서 구현하지 않지만 산출물(첨부파일)이나

진행 상황에 대한 메모도 등록할 수 있다.


두번째는 이렇게 작성된 작업 정보를

서버로 보내서 저장(삭제)해야 한다.

모든 할당을 완료한 후 저장하면

구현이 복잡해지기 때문에

여기서는 하나의 작업(행)을 완료하면 (save버튼 클릭)

Ajax로 데이터를 서버로 전송해서 저장한다.


먼저, 각 업무에 대한 담당자를 지정하도록 구현한다.

담당자 지정은 treegrid에서 담당자 셀(Persons)를

마우스로 두 번 클릭하면 (DblClickCell)

직원들을 선택할 수 있는 팝업 화면이 실행되고 (그림 참조)

담당 직원들을 선택하면

Treegrid의 담당자 셀(Persons)에 이름이 나열되도록 구현한다.

treegrid 예제에서

담당자는 persons라는 필드로 사용되고

담당자 수를 의미한다.

과제 관리에서는

Porject9 샘플에서 작성한 것과 같이

담당자 이름이 출력된다.

더욱이 화면 출력은 이름이지만

데이터 베이스에 저장할 때는

각 담당자(사용자)의 고유번호(userno)로 저장한다.


따라서

Treegrid의 persons는 담당자 이름(usernm)으로 바꾸고,

데이터 베이스 저장을 위해

담당자고유번호(userno)를 숨겨진 필드로 가지고 있는다.

Treegrid의 숨겨진 필드는

데이터(dataSet)에는 값을 가지고 있지만

출력 부분(Table 태그)에는 없는 필드이다.


persons을 usernm으로 모두 바꾸어 준다.

save()함수에 사용된 persons은

입력 인원수를 더해서

treegrid footer에 보여주기 위한 코드로

삭제하고,

append()와 table 태그에서 지정하는 두 부분만 수정하면 된다.

function append(){
    idIndex++;
    var d1 = new Date();
    var d2 = new Date();
    d2.setMonth(d2.getMonth()+1);
    var parentid = null;
    var node = $('#tg').treegrid('getSelected');
    if (node) parentid=node.tsno;
    $('#tg').treegrid('append',{
        parent: parentid,
        data: [{
            tsno: idIndex,
            tstitle: 'New Task'+idIndex,
            usernm: "",
            userno: "",

            tsstartdate: $.fn.datebox.defaults.formatter(d1),
            tsenddate: $.fn.datebox.defaults.formatter(d2),
            tsrate: parseInt(Math.random()*100)
        }]
    })
}
</script>  
</head>

<table id="tg" class="easyui-treegrid" style="width:700px;height:250px"
    <thead>
        <tr>
            <th data-options="field:'tstitle',width:180,editor:'text'">Task Name</th>
            <th data-options="field:'usernm',width:60,align:'right'">Persons</th>
            <th data-options="field:'tsstartdate',width:80,editor:'datebox'">Begin Date</th>
            <th data-options="field:'tsenddate',width:80,editor:'datebox'">End Date</th>
            <th data-options="field:'tsrate',width:120,formatter:formatProgress,editor:'numberbox'">Progress</th>
        </tr>
    </thead>
</table>           

table 태그에서 담당자(usernm) 지정시

입력 방식으로 지정된 editor:'numberbox'는 제거한다.

인원수를 입력하지 않고

마우스로 두번 클릭하면

앞서의 사용자 선택 팝업에서 선택하도록 구현할 것이다.


Project9에서 제공하는

샘플 1의 3번째 예제인 [코드보기]를 실행한다.

3개의 블럭으로 구성되어 있다.

2번째 블럭 코드는 HTML(text, hidden) 태그를 이용하여

사용자 선택 값을 주고 받는 코드로

여기에서는 treegrid를 사용할 것이기 때문에 사용하지 않는다.

첫 번째 블럭 코드는 다음과 같다.

<link href="js/dynatree/ui.dynatree.css" rel="stylesheet"/>
<script src="js/jquery-2.2.3.min.js"></script>
<script src="js/jquery-ui.js"></script>
<script src="js/dynatree/jquery.dynatree.js"></script>

<script>                       
function fn_searchUsers(){
    $.ajax({
        url: "popupUsers",
        type: "post"       
    }).success(function(result){
                $("#popupUsers").html(result);
                if ($("#usernos").val()!==""){
                    set_Users($("#usernos").val(), $("#usernms").val());
                }
        }           
    );
    $("#popupUsers").modal("show");
}
function deptTreeInUsersActivate(node) {
    if (node==null || node.data.key==0) return;
   
    $.ajax({
        url: "popupUsers4Users",
        type:"post",
        data: { deptno : node.data.key }       
    }).success(function(result){
                $("#userlist4Users").html(result);
        }           
    );
}

function fn_selectUsers(usernos, usernms) {
    $("#usernos").val(usernos);
    $("#usernms").val(usernms);
    $("#popupUsers").modal("hide");
}
</script>

이 코드는 부서 조직도를 보여 주기 위해 사용한

dynatree 용 코드와

사용자 명(usernms)과 코드(usernos)를

주고 받기 위한 코드로 구성되어 있다.

usernms, usernos의 s를 모두 지운다.

treegrid와 테이블(PRJ_TASKUSER)에서는

usernm, userno로 사용하기 때문이다.

(의미상 그냥 사용해도 되지만 혼동을 줄이기 위해 수정한다.)


Project9의 샘플 코드는

사용자 명은 text, 사용자 코드는 hidden 태그를 사용했다.

여기서는 사용자 명은 treegrid 필드,

사용자 코드는 숨겨진 필드로 구현한다.

(dataset 변수에 지정되었지만 treegrid에 지정되지 않은면 숨겨진 필드가 된다)

따라서 위 코드를 다음과 같이 수정한다.

function fn_searchUsers(row){                                  
    $.ajax({
        url: "popupUsers",
        type: "post"      
    }).success(function(result){
                $("#popupUsers").html(result);
                   if (row.userno){
                       set_Users(row.userno, row.usernm);
                   }
        }          
    );
    $("#popupUsers").modal("show");
}
~~ 생략 ~~
function fn_selectUsers(userno, usernm) {                      
    var node = $('#tg').treegrid('getSelected');
   
    $('#tg').treegrid('update', {
        id : node.tsno,
        row : {userno : userno, usernm:usernm}
    });
    $('#tg').treegrid('endEdit', editingId);
    $('#tg').treegrid('beginEdit', editingId);
    $("#popupUsers").modal("hide");
}

treegrid 사용에 대한 상세한 설명은 정리하지 않으니 찾아보길 바란다.

① fn_searchUsers()는 treegrid에서

담당자(persons)필드를 두번 클릭하면 호출하는 함수로

사용자 선택 팝업을 실행하기 전에 기존에 선택된 값을 넘겨준다 (set_Users).

② fn_selectUsers()는 팝업에서 사용자를 선택하고

[확인]을 누르면

선택된 값을 treegrid에 넣어주는(update)는 역할을 한다.


이렇게 수정한 코드를 ProjectRead.jsp에 넣어준다.


앞서의 원본 코드에서 앞에 있는 link 와 script 를 복사해서

ProjectRead.jsp 파일의 head 태그에 붙여 넣는다.

jquery-2.2.3.min.js는 이미 사용중이니 제외한다.


[코드 보기] 팝업에서

마지막에 있는 div를 복사해서 ProjectRead.jsp의 마지막에 붙여 넣는다.

<div id="popupUsers" class="modal fade bs-example-modal-lg" tabindex="-1" role="dialog" aria-labelledby="myLargeModalLabel"></div>

이 코드는 팝업 내용을 출력하기 위한

Bootstrap 코드이다.


담당자 지정과 관련해서 마지막 작업으로

treegrid 생성부분에 다음 코드를 추가 한다.

$(function() {
    var dataSet = {"total":0,"rows":[]}

    $('#tg').treegrid({
        data: dataSet,
        onDblClickCell : function(field, row) {
            edit();
            if (field==="usernm") {
                fn_searchUsers(row);
            }
        }
    });
});

하나의 행을 두번 클릭하면

데이터를 수정하는 코드이다.

다만, 두번 클릭한 필드가 담당자 필드이면

fn_searchUsers()를 호출해서

사용자 선택 팝업을 실행시킨다.

파라메터로 row를 사용하는 것은

treegrid에서 주는 값으로

하나의 행에 대한 모든 값을 Json으로 가지고 있기 때문이다.

row.tsno, row.usernm, row.userno, row.tstitle 등으로

이용할 수 있다.

실행해서

그림과 같이 담당자를 지정한다.



하나의 프로젝트에

개별 작업(Task)들을 구성하는 마지막 처리는

treegrid에서 작성한 내용을

데이터 베이스에 저장하고 관리하는 것이다.


작성한 내용을 데이터 베이스에 저장하는 처리는

개별 작업(Task)을 작성하고 [append(), edit() 호출]

treegrid에 저장할 때[save()] 데이터 베이스에도 저장한다.

또, treegrid에서 하나의 작업을 선택하고

삭제할 때 (removeIt)

데이터베이스에서도 삭제하면 된다.

이러한 처리는 Ajax로 데이터(Json)만 주고 받도록 한다.


이렇게 저장한 작업 데이터는

하나의 프로젝트 정보를 보여줄 때

같이 보여주도록 해야 한다.

따라서, 작업에 대한 조회(select),

저장(insert/update), 삭제(delete)로 구성된

CRUD를 제작한다.

다만, 앞서 작성한 프로젝트의 CRUD와

개념은 같지만 처리 방법이 다르다.


먼저, 지정한 작업(Task)을 PRJ_TASK 테이블에 저장한다.

하나의 작업에 대한 저장은 save() 함수에서 처리된다.

본격적인 작업전에

먼저 if 문 사용을 바꾸어 준다.

if 문 블럭내에 코드가 많을 경우

다음 코드의 왼쪽 보다는 오른쪽이 조금더 가독성이 좋다.

if (editingId != undefined){

     처리

}

 if (editingId === undefined){

     return

}

처리

블럭 내에 긴 코드를 작성하면

블럭의 시작과 끝을 알기 어렵기 때문이다.

그리고, 비교 연산자는 !=, == 보다 !==, === 사용이 추천된다.


작업자 수(persons)를 합산하는 코드는 제거한다.

따라서 save()와 removeIt()를 다음과 같이 수정한다.

function save(){
    if (editingId === undefined){return;}
    var t = $('#tg');
    t.treegrid('endEdit', editingId);
    // 현재 선택된 행
    var node = t.treegrid('getSelected');                             
     t.treegrid('endEdit', editingId);
     // 부모 노드 정보 추출
     var parentId=null;
     var parent = t.treegrid('getParent', node.tsno);                 
     if (parent) {
         parentId = parent.id;
     }   
     node.tsparent=parentId;
     node.prno='<c:out value="${projectInfo.prno}" />';
     // 데이터 전송
     $.ajax({                                                         
         url : "taskSave",
         type: "post",
         dataType: "json",
         data: node
     }).done(function(data){
         $('#tg').treegrid('update', {                                  
             id : "N",
            row : {id: data, tsno: data}
         });       
     });
    
     editingId = undefined;       
}

function removeIt(){
    var node = $('#tg').treegrid('getSelected');
    if (!node) {return;}
   
    $.ajax({
        url : "taskDelete",
        cache : false,
        dataType : "json",
        data : {tsno:node.tsno}
    }).done(function(data){
        $('#tg').treegrid('remove', node.tsno);                         
    });
   
}

저장/수정과 삭제 모두 Ajax를 이용하여

데이터를 서버로 전송한다.


삭제는 삭제할 데이터(현재 선택한 행의 tsno)를 서버로 전송하여

데이터 베이스에서 삭제하고(delete),

treegrid에서도 삭제해(remove) 주면 된다 ⑤.


저장시 전송할 데이터는 현재 선택된 행을 찾아서(node)

해당 행의 정보를 전송한다 ①.

getSelected로 반환된 개체(node)는 Json 데이터를 반환하는데

행의 모든 필드 정보를 가지고 있지만

부모 노드에 대한 정보가 없어서 별도의 처리를 했다 ②.

taskSave 컨트롤을 호출해서 ③

해당 작업을 저장하고

작업번호(tsno)을 반환 받아서

treegrid의 해당 행을 수정(update)해 준다 ④.

수정일 경우에는 작업번호(tsno)가 있지만

신규일 경우에는 작업번호(tsno)에 N 문자를 넣어서 전송한다.

서버에서는 이 값을 기준으로 insert를 실행 할지

update를 실행할지를 결정한다.

treegrid 버그인지 tsno를 키로 지정했지만

id를 키로 사용하기 때문에 두 개의 필드 값을 수정(update)해 준다.

이 처리를 좀더 정확하게 수행하기 위해

append() 함수도 다음과 같이 수정한다.

function append(){
    ~~ 생략 ~~
    $('#tg').treegrid('append',{
        parent: parentid,
        data: [{
            id: 'N',
            tsno: 'N',
            tstitle: 'New Task'+idIndex,
            usernm: "",
            userno: "",
            tsstartdate: $.fn.datebox.defaults.formatter(d1),
            tsenddate: $.fn.datebox.defaults.formatter(d2),
            tsrate: parseInt(Math.random()*100)
        }]
    });
    editingId = 'N';
    $('#tg').treegrid('select', editingId);
    $('#tg').treegrid('beginEdit', editingId);   
}

추가시 신규와 수정을 구분하기 위해 id와 tsno에 N값을 넣어준다.

추가후 반드시 저장하도록 하기 위해

현재 추가한 행(select)을 수정모드(beginEdit)로 설정한다.


이렇게 클라이언트쪽 작업을 마치고

서버쪽 작업을 진행한다.


resource > sql 폴더에

projectTask.xml 파일을 생성하고 다음 코드를 작성한다.

insertCrud, updateCrud, deleteCrud를 복사해서 수정(replace)해도 되고,

그냥 작성한다 (CPR추천).

    <insert id="insertTask" parameterType="gu.project.TaskVO" useGeneratedKeys="true" keyProperty="tsno">
        INSERT INTO PRJ_TASK(PRNO, TSPARENT, TSSORT, TSTITLE,  TSSTARTDATE, TSENDDATE, TSRATE, DELETEFLAG)
        VALUES (#{prno}, #{tsparent}, #{tssort}, #{tstitle}, #{ tsstartdate}, #{tsenddate}, #{tsrate}, 'N')
    </insert>
   
    <update id="updateTask" parameterType="gu.project.TaskVO">
        UPDATE PRJ_TASK
           SET TSTITLE=#{tstitle}
             , TSSTARTDATE=#{tsstartdate}
             , TSENDDATE=#{tsenddate}
             , TSRATE=#{tsrate}
         WHERE DELETEFLAG='N'
           AND TSNO=#{tsno}
    </update>
   
    <delete id="deleteTask" parameterType="String">
        UPDATE PRJ_TASK
           SET DELETEFLAG='Y'
         WHERE TSNO=#{tsno}
    </delete>
    <insert id="insertTaskUser" parameterType="gu.common.Field3VO" >
        INSERT INTO PRJ_TASKUSER(TSNO, USERNO)
        VALUES (#{field1}, #{field2})
    </insert>
    <delete id="deleteTaskUser" parameterType="String" >
        DELETE FROM PRJ_TASKUSER WHERE TSNO = #{tsno}
    </delete>

다음으로

gu > project 폴더에

TaskVO.java 파일을 생성하고

PRJ_TASK 테이블의 필드에 해당하는 변수들을 선언한다.


TaskCtr.java 파일을 생성해서

다음과 같이 작성한다.

    @RequestMapping(value = "/taskSave")
    public void taskSave(HttpServletRequest request, HttpServletResponse response, TaskVO taskInfo) {
       
        taskSvc.insertTask(taskInfo);
       
        UtilEtc.responseJsonValue(response, taskInfo.getTsno());
    }
   
    @RequestMapping(value = "/taskDelete")
    public void taskDelete(HttpServletRequest request, HttpServletResponse response) {
        String tsno = request.getParameter("tsno");

        taskSvc.deleteTask(tsno);
       
        UtilEtc.responseJsonValue(response, "OK");
    }

컨트롤 파일에서는

데이터 베이스에 저장할 수 있도록 서비스(TaskSvc)를 호출하고

Ajax 호출에 따른 결과를 반환한다.

Ajax로 값을 반환하는 코드는

UtilEtc파일에 responseJsonValue() 함수로 미리 구현되어 있어서

함수만 호출해 주면 된다.


마지막으로 TaskSvc.java을 생성하고

다음과 같이 코드를 작성한다.

    public void insertTask(TaskVO param) {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus status = txManager.getTransaction(def);
       
        try {
            if (param.getTsno() == null || "N".equals(param.getTsno())) {               
                if ("".equals(param.getTsparent())) {
                    param.setTsparent(null);
                }
                sqlSession.insert("insertTask", param);
            } else {
                sqlSession.update("updateTask", param);
                sqlSession.delete("deleteTaskUser", param.getTsno());                 
            }
            String userno = param.getUserno();
            if (userno!=null) {                                                             
                Field3VO fld = new Field3VO(param.getTsno(), null, null);
                String[] usernos = userno.split(",");
                for (int i=0; i< usernos.length; i++){
                    if ("".equals(usernos[i])) {continue;}
                    fld.setField2(usernos[i]);
                    sqlSession.update("insertTaskUser", fld);
                }
            }
            txManager.commit(status);
        } catch (TransactionException ex) {
            txManager.rollback(status);
            LOGGER.error("insertTask");
        } 
    }
 
    public void deleteTask(String param) {
        sqlSession.delete("deleteTask", param);
    }

코드가 복잡해 보이는데

CRUD 샘플의 내용과 비슷한다.

작업 키 필드(tsno)의 값이 없거나 N이면 ①

신규로 insert를 실행하고

아니면(값이 있으면) 수정으로 보고 Update를 실행한다.


CRUD 샘플과 다른 점은

해당 작업에 대한 담당자 처리 부분이다.

담당자정보(userno)는 여러 명일 수 있기 때문에

콤마(,)로 묶여서 하나의 변수(userno)에 넣어져 전송된다.

이것을 분리해서(split)

개수만큼 저장하게 처리한다 ③.

다만, 수정시 누가 어떻게 바뀌는지 알 수 없기 때문에

모두 지운후 ②

새로 저장하는 방식으로 구현한다.


여기까지 작업한후 실행 결과를 확인해도 되지만

제대로 저장/수정/삭제 되었는지 확인하려면

데이터 베이스에 접속해서 확인해야 한다.

웹 페이지를 새로고침(F5) 해보면 데이터가 사라지는 것을 볼 수 있다.


데이터 베이스에 접속하지 않고

프로젝트 정보를 출력할 때 출력하도록 한다.

projectTask.xml에 다음 코드를 추가한다.

(project.xml 에 추가해도 된다.)

<select id="selectTaskList" resultType="gu.project.TaskVO" parameterType="String">
    SELECT PRNO, TSNO, TSPARENT, TSSORT, TSTITLE, TSSTARTDATE, TSENDDATE, TSRATE
         , (SELECT GROUP_CONCAT(USERNO) FROM PRJ_TASKUSER WHERE TSNO=PT.TSNO) USERNO
         , (SELECT GROUP_CONCAT(USERNM) FROM PRJ_TASKUSER PTU, COM_USER CU WHERE TSNO=PT.TSNO AND PTU.USERNO=CU.USERNO) USERNM
      FROM PRJ_TASK PT
     WHERE PT.DELETEFLAG='N' AND PRNO=#{pjno}
     ORDER BY TSNO
</select>

PRJ_TASK 테이블에서 필요한 정보를 가져오는 SQL문으로

담당자 정보(USERNO, USERNM)에서

subquery로 GROUP_CONCAT을 사용했다.

앞서의 코드에서 여러 명의 담당자가 PRJ_TASKUSER에 각각 저장되게 하고,

정보를 출력할 때는

콤마(,)를 이용하여 다시 하나의 값으로 묶어서 처리한다.

TaskSvc에 이 SQL을 실행하는 함수를 다음과 같이 작성한다.

    public List<?> selectTaskList(String param) {
        return sqlSession.selectList("selectTaskList", param);
    }


이 서비스를 호출하는 다음 코드를

ProjectCtrprojectRead 컨트롤에 추가한다.

@RequestMapping(value = "/projectRead")
public String projectRead(HttpServletRequest request, ProjectVO projectVO, ModelMap modelMap) {
    ~~ 생략 ~~   
    ProjectVO projectInfo = projectSvc.selectProjectOne(projectVO);

    List<?> listview  = taskSvc.selectTaskList(projectVO.getPrno());
   
    modelMap.addAttribute("projectInfo", projectInfo);
    modelMap.addAttribute("listview", listview);
   
    return "project/ProjectRead";
}

데이터 베이스에서 작업 리스트를 가지고 와서

ProjectRead.jsp에 작업 리스트를 넘겨 주는 코드이다.


다른 서비스(taskSvc)를 사용하기 때문에

클래스 앞부분에 (projectSvc 근처)에 다음 코드를 추가해야 한다.

    @Autowired
    private TaskSvc taskSvc;

컨트롤(ProjectCtr)에서 반환된 listview의 데이터를

JSP 파일에서 treegrid에 맞는 데이터(Json)로

변환하는 다음 코드를 추가한다.

var dataSet = {"total":<c:out value="${listview.size()}" />,"rows":[
    <c:forEach var="listview" items="${listview}" varStatus="status">
        {"id":'<c:out value="${listview.tsno}" />', "tsno":'<c:out value="${listview.tsno}" />'
        ,"tstitle":'<c:out value="${listview.tstitle}" />',"tsstartdate":"<c:out value="${listview.tsstartdate}" />"
        ,"tsenddate":"<c:out value="${listview.tsenddate}" />", "tsrate":"<c:out value="${listview.tsrate}" />"
        ,"userno": "<c:out value="${listview.userno}" />", "usernm": "<c:out value="${listview.usernm}" />"
        <c:if test="${listview.tsparent!=null}">,"_parentId":"<c:out value="${listview.tsparent}" />"</c:if> } <c:if test="${!status.last}">,</c:if>
    </c:forEach>
]};

$('#tg').treegrid({ 
    data: dataSet,
    onDblClickCell : function(field, row) {
        edit();
        if (field==="usernm") {
            fn_searchUsers(row);
        }
    }
});


데이터 베이스에서 작업 리스트(listview)를 가지고 와서 (select)

id, tsno, tstitle 등으로 구성된

Json 데이터를 동적으로 생성한다.


다음 그림과 같이 실행 결과를 확인한다.

append 버튼을 눌러서 작업을 추가하고

Save버튼으로 저장한다.

행을 선택하고 Edit 버튼으로 수정하고

Save버튼으로 저장한다.

행을 선택하고 remove 버튼으로 삭제 한다.


이렇게 작업한 후

새로고침(F5)해서 제대로 서버에 저장되었는지 확인한다.


지금까지 teegrid를 이용하여

세부 작업을 관리하는 간단한 기능을 구현하였다.

디자인도 정리, 다국어 처리,

작성자만 수정하도록 하는 등의

많은 기능들이 더 구현되어야 하지만

간단하게 개발하는 방법을 정리하는 것이 목적이라

작업 할당에서는 여기까지만 정리한다.

단순한 기능만으로도

제법 많은 코딩을 추가하고

복잡한 처리를 구현 한 것 같지만

Spring과 웹 개발을 알고 있다면

크게 어려운 것이 없는 내용이다.


정리하면

Treegrid 샘플을

과제 관리 시스템에 맞게끔 수정하고

작업을 추가/수정한 뒤 저장(Save)하거나

삭제할 때 Ajax로 데이터를 전송하여

데이터 베이스에 저장하였다.

이렇게 작성한 작업들을

프로젝트 정보가 출력될 때 같이 출력되도록

Treegrid의 기본 데이터로 지정하였다.







+ Recent posts