27 DatasetSupport
hanmj edited this page 3 months ago

DatasetSupport

DatasetSupport란 Dataset의 데이터를 페이지의

  • UI 요소에 표시
  • UI 요소로 변경한 값을 Dataset에 적용
  • 데이터 상태에 따라 UI 요소를 제어

하는 것을 돕는 자바스크립트 클래스들이다.

지원하는 DatasetSupport로는

  • TableSupport
  • PagingSupport
  • CurrentDataSupport

가 있다.

시작하기

의존성 추가

DatasetSupport 클래스를 사용하려면 xit-web-res 모듈에 대한 dependency를 프로젝트에 추가해야 한다.

<dependency>
	<groupId>cokr.xit.base</groupId>
	<artifactId>xit-web-res</artifactId>
	<version>23.04.01-SNAPSHOT</version>
</dependency>

프로젝트가 xit-foundation 모듈을 포함하면 자동으로 추가된다.

스크립트 링크 추가

위의 dependency를 추가한 후 JSP 파일에 다음과 같이 스크립트 파일에 대한 링크를 설정한다.

<script src="<c:url value="/webjars/js/base/base.js?${ver}"/>"></script>
<script src="<c:url value="/webjars/js/base/dataset.js?${ver}"/>"></script>
<script src="<c:url value="/webjars/js/base/dataset-support.js?${ver}"/>"></script>

Controller

서비스의 조회결과(데이터 + 페이징 정보)를 DatasetSupport가 사용하려면 Controller는 조회결과를 setPagingInfo(...) 메소드로 반환해야 한다.

CustomerService service = ...;

public ModelAndView getCustomerList(XXXQuery req) {
    List<?> result = service.getList(req);
    return setPagingInfo(
        new ModelAndView("jsonView"), // ModelAndView
        result,                       // 조회 결과
        "customer"                    // 반환 객체들 이름의 접두사
    );
}

위 메소드를 실행하면 조회결과를 다음과 같이 반환한다.

{
    infoPrefix: "customer",   // 반환 객체들 이름의 접두사
    customerList: result, // 조회결과
    customerPaging: {     // 페이징 정보
        start: 0,         // 조회결과 데이터의 시작 인덱스(0부터 시작)
        dataSize: 10,     // 조회결과 중 반환하는 데이터 갯수
        totalSize: 50,    // 조회결과 데이터 전체 갯수
        fetchSize: 10,    // 한번에 반환하는 데이터 갯수
        more: true,       // 더 반환할 데이터가 있는지 여부
        next: true,       // 다음에 반환할 데이터가 있는지 여부
        prev: false       // 이전에 반환한 데이터가 있는지 여부
    }
}

DatasetControl

콘트롤러가 위와 같이 반환한 조회결과를 사용하는 DatasetControl은 다음과 같이 선언한다.

var custCtrl = newCustomerControl({
    prefix: "customer",                         // 조회결과 접두어, 필요할 경우에 설정
    keys: ["CUST_ID"],                          // 조회결과의 키 필드 이름
    doctx: "[data-doctx='customer'],#cust-form" // 조회결과 데이터를 사용할 element들의 부모 요소들의 selector
});

콘트롤러가 반환한 조회결과를 직접 DatasetControl에 설정하려면 다음과 같이 한다.

custCtrl.setData({
    customerList: ${customerList},
    customerPaging: ${customerPaging}
});

TableSupport

TableSupport는 DatasetControl의

  • 데이터를 표시
  • 사용자가 현재값로 지정한 행의 표시
  • 사용자의 데이터 선택상태 표시
  • 데이터의 로컬 정렬
  • 데이터 상태에 따라 .enable-onfound, .enable-onselect 클래스를 갖는 UI 요소의 활성화 / 비활성화

을 지원한다.

TableSupport가 바인딩하는 테이블의 형태는 다음과 같다.

<div data-doctx="customer">
...
<table name="cust-table" ...>
    <thead ...>
        <tr><th...>No.</th>
            <th...><%-- 전체 선택 / 선택 해제 --%>
                <input onchange="custCtrl.select(this.checked);" type="checkbox"...>
            </th>
            <th data-sort="CUST_ID" ...>아이디</th> <%-- data-sort: 데이터 정렬 컬럼 지정 --%>
            <th data-sort="CUST_NAME" ...>이름</th>
            <th data-sort="PHONE_NO" ...>전화번호</th>
            <th data-sort="EMAIL" ...>이메일</th>
            <th ...>등록일자</th>
        </tr>
    </thead>
    <tbody name="customerList"><%-- 조회결과를 표시할 테이블 body --%></tbody>
    <%-- 조회결과를 테이블에 표시하기 위한 템플릿(필수)
         {data-index}: 데이터 인덱스 마킹
         {data-no}: 데이터 번호 마킹		
	     data-index={data-index}: 데이터/행 식별을 위한 인덱스의 마킹		
    --%>
    <template name="customerRow"><tr data-index="{data-index}">
        <td {onclick} {ondblclick} ...>{data-no}</td>
        <td ...>
            <input name="data-index" value="{data-index}" type="checkbox"
             onchange="custCtrl.select('{data-index}', this.checked);" ...>
        </td>
        <td {onclick} {ondblclick} ...>{CUST_ID}</td>
        <td {onclick} {ondblclick} ...>{CUST_NAME}</td>
        <td {onclick} {ondblclick} ...>{PHONE_NO}</td>
        <td {onclick} {ondblclick} ...>{EMAIL}</td>
        <td {onclick} {ondblclick} ...>{REG_DT}</td>
    </tr></template>
    <%-- 조회결과가 없을 때 테이블에 표시하기 위한 템플릿 --%>
    <template name="customerNotFound">
        <tr><td colspan="7" ...>고객 정보를 찾지 못했습니다.</td></tr>
    </template>
</table>
...
<button class="enable-onfound">버튼 1</button>
<button class="enable-onselect">버튼 2</button>
</div>

TableSupport는 데이터를 갖는 DatasetControl과 연동하여 동작한다.

var custTable = new TableSupport({
    ctrl: custCtrl,                                     // 연동하는 DatasetControl 설정
    table: "[name='cust-table']",                       // 연동 table
    formatter: (tmpl, item) => tmpl                     // 템플릿 포맷 function
        .replace(/{onclick}=""/gi, 'onclick="custCtrl.setCurrent(\'{data-index}\');"')
        .replace(/{ondblclick}=""/gi, 'ondblclick="custCtrl.getCustomerInfo(\'{CUST_ID}\')"'),
    selectionToggler: "th [type='checkbox']",           // 전체 선택 / 선택 해제를 위한 체크박스
    refreshOnModify: ["CUST_NAME", "PHONE_NO", "EMAIL"] // 값이 변경됐을 때 테이블을 갱신해야하는 필드 이름
});

위 설정으로 생성한 TableSupport는 대상 table의 template 중

  • 첫번째를 데이터 행을 위한 템플릿
  • 두번째를 데이터가 없을 때를 위한 템플릿

간주한다.

TableSupport가 사용할 template을 식별해야할 경우 다음 설정을 추가한다.

var custTable = new TableSupport({
    ...
    tr: "[name='customerRow']",
    notFound: "[name='customerNotFound']",
    ...
});

위와 같이 생성한 TableSupport는 연동한 DatasetControl의 이벤트 핸들러에서 필요한 메소드를 호출한다.

// 데이터셋 변경
custCtrl.onDatasetChange = (obj, option) => {
    custTable.renderList(option); // table 표시
    ...
};
// 현재 데이터 설정
custCtrl.onCurrentChange = item => {
    custTable.setCurrentRow(item); // 현재 tr 설정
    ...
};
// 데이터 선택 변경 -> 체크박스 표시
custCtrl.onSelectionChange = selected => custTable.setSelections(selected);
// 데이터 정렬 -> table 헤더 표시
custCtrl.onSort = sorter => custTable.updateSortables(sorter);
// 데이터 추가 / 치환 -> table 표시
custCtrl.onAppend = custCtrl.onReplace = () => custTable.renderList();
// 필드 데이터 변경 -> table 표시
custCtrl.onModify = (changed, item) => {
    custTable.updateModified(changed);
    ...
};

PagingSupport

PagingSupport는 DatasetControl과 연동하여

  • 데이터 페이징을 위한 링크를 생성
  • 페이징 상태 정보를 나타내는 컨텐츠를 설정

한다.

PagingSupport는 다음과 같이 생성한다.

var custPaging = new PagingSupport({
        ctrl: custCtrl,                           // 연동하는 DatasetControl

        linkContainer: "[name='customerPaging']", // 생성하는 페이징 링크들의 부모 요소 selector
        func: "custCtrl.load({index})"            // 페이징 링크를 클랙했을 때 실행할 function
	});

위와 같이 생성한 PagingSupport는 연동한 DatasetControl의 이벤트 핸들러에서 필요한 메소드를 호출한다.

// 데이터셋 변경
custCtrl.onDatasetChange = (obj, option) => {
    ...
    custPaging.setPaging(option); // 페이징 표시
};

CurrentDataSupport

CurrentDataSupport는 DatasetControl과 연동하여

  • 현재 데이터를 UI 요소에 표시
  • UI 요소로 변경된 데이터를 DatasetControl의 데이터에 적용
  • 현재 데이터 상태에 따라 .enable-ondirtyitem, .enable-onnewitem 클래스를 갖는 UI 요소의 활성화/
  • 서버 전송 포맷으로 데이터 반환
  • UI 요소 바인딩을 위한 새 데이터 반환

을 지원한다.

CurrentDataSupport가 바인딩하는 UI 요소는

  • data-field 애트리뷰트로 DatasetControl 데이터의 컬럼이름을 지정한다.
  • name, 또는 id 애트리뷰트로 서버로 데이터를 전송할 때 자바 클래스의 프로퍼티로 매핑한다.

다음은 그 예다.

<form id="cust-form" ...>
    ...
    <input name="custId" type="text" required data-field="CUST_ID" class="enable-onnewitem ..."/>

    <input name="custName" type="text" required data-field="CUST_NAME" .../>

    <input name="address" type="text" data-field="ADDRESS" .../>

    <input name="phoneNo" type="text" required data-field="PHONE_NO" .../>

    <input name="email" type="text" data-field="EMAIL" .../>

    <button onclick="custCtrl.saveCustomer();" type="button" class="enable-ondirtyitem ...">저장</button>
    ...
</form>

위의 UI 요소에 바인딩하는 CurrentDataSupport는 다음과 같이 생성한다.

var currentCust = new CurrentDataSupport({
    ctrl: custCtrl,
    selector: "[data-field]"
});

위 예에서 currentCust는

  • custCtrl의 데이터와 연동
  • cust-form의 data-field 애트리뷰트를 갖는 요소와 바인딩

한다.

위와 같이 생성한 CurrentDataSupport는 연동한 DatasetControl의 이벤트 핸들러에서 필요한 메소드를 호출한다.

// 현재 데이터 설정
custCtrl.onCurrentChange = item => {
    ...
    currentCust.setCurrent(item);  // 현재 데이터 표시
};
// 필드 데이터 변경 -> table 표시
custCtrl.onModify = (changed, item) => {
    ...
    currentCust.updateModified(changed, item);
};

CurrentDataSupport.getData() 메소드는 현재 설정된 각 컬럼의 값을 자바 클래스의 프로퍼티로 매핑한 객체로 반환한다. 예를 들어 currentCust.getData()를 호출하면 다음 형태의 객체를 반환한다.

{
    "custId": "2",
    "custName": "고객 2",
    "address": "주소 2",
    "phoneNo": "010-2222-2222",
    "email": "cust-02@xit.co.kr",
    "useYN": "Y"
}

CurrentDataSupport.newData(function(data) {...}) 메소드는

  • data-field 애트리뷰트로 지정한 컬럼들로 된 새 객체를 생성하고
  • 초기화 함수를 적용

하여 반환한다.

다음은 위 예제 페이지의 data-field 컬럼으로 이루어진 새 객체를 만들고 DatasetControl의 데이터에 추가하는 예다.

var newData = currentCust.newData(data => {
        data.USE_YN = "Y";
        data.REG_DT = new Date();
    });
custCtrl.dataset.append(newData);

newData는 다음과 같은 형태의 객체를 반환한다.

{
    "CUST_ID": null,
    "CUST_NAME": null,
    "ADDRESS": null,
    "PHONE_NO": null,
    "EMAIL": null,
    "USE_YN": "Y",
    "REG_DT": "2024-08-19T08:47:04.711Z"
}