점검 유형 4종의 목록 화면을 Datagrid2 대신 HTMLSnippet 3개와 JS로 다시 만들었다. 조회는 JSON 한 번으로 받고, 필터와 정렬은 브라우저에서 처리하며, 저장과 삭제는 Microflow를 호출한다.
문제
같은 목록을 점검 유형별로 조금씩 다르게 보여줘야 하고, 상세 검색, 행 펼치기, 편집 팝업, 엑셀 다운로드까지 필요했음
원인
Datagrid2로는 페이지마다 컬럼과 필터를 따로 구성해야 해서, 요청 하나에 페이지 4개를 고쳐야 했음
해결
위젯을 한 벌만 만들고 페이지마다 설정 스니펫만 다르게 둠
구조
Mendix 페이지
① 설정 스니펫페이지마다 다른 값만 담음
② HTML, CSS 스니펫화면 틀과 스타일, 모든 페이지 공통
③ JS 스니펫조회, 필터, 정렬, 팝업, 엑셀, 이벤트 처리
조회 1회
저장, 삭제
Mendix 런타임
Java Action목록 전체를 JSON 문자열로 반환
Microflow저장, 삭제
DB
- ①은 페이지마다 다르고, ②와 ③은 모든 페이지에 같은 내용을 붙임
- 스니펫 순서는 ① → ② → ③. ③이 실행될 때 ①의 설정값이 있어야 함
- HTMLSnippet의 Content type은 ①과 ③은 JavaScript, ②는 HTML
설정 스니펫
페이지마다 다른 값만 전역 객체 하나에 담는다. 위젯 코드는 이 값을 읽어서 화면을 그린다.
① 설정 스니펫 (JavaScript)
window.LIST_PAGE_CONFIG = {
pageType: 'typeA', // 서버 조회 조건에 쓰는 구분값
title: '외부 점검',
columns: ['detectDate', 'site', 'title', 'team', 'dueDate', 'status'],
canEdit: true
};목록 조회는 JSON 한 번
Java Action이 목록 전체를 JSON 문자열로 돌려주고, 이를 Microflow로 감싸서 JS에서 호출한다. 화면에 필요한 컬럼만 담는다.
Java Action: JA_GetIssueList_JSON (반환 String)
IContext ctx = Core.createSystemContext(); // 사용자 세션 캐시에 쌓이지 않게
JSONArray arr = new JSONArray();
int batch = 300, offset = 0, max = 10000;
while (offset < max) {
List<IMendixObject> list = Core.createXPathQuery("//Issue.InspectionIssue")
.addSort("DetectDate", false)
.setAmount(batch)
.setOffset(offset)
.execute(ctx);
if (list.isEmpty()) break;
for (IMendixObject o : list) {
JSONObject j = new JSONObject();
j.put("guid", String.valueOf(o.getId().toLong()));
j.put("title", o.getValue(ctx, "Title"));
j.put("team", o.getValue(ctx, "Team"));
// 날짜는 epoch(ms)로 넘기고 화면에서 변환
Date due = o.getValue(ctx, "DueDate");
j.put("dueDate", due == null ? JSONObject.NULL : due.getTime());
Date clear = o.getValue(ctx, "ClearDate");
j.put("clearDate", clear == null ? JSONObject.NULL : clear.getTime());
arr.put(j);
}
offset += batch;
}
return arr.toString();시스템 컨텍스트는 권한을 확인하지 않는다createSystemContext로 조회하면 엔티티 접근 규칙이 적용되지 않는다. 사용자별로 보여줄 범위가 다르면 XPath 조건에 직접 넣어야 한다.
③ JS 스니펫: 목록 불러오기
function loadList(onDone) {
mx.data.action({
params: { actionname: 'Issue.ACT_GetIssueList_JSON' },
callback: function (json) {
var rows = [];
try { rows = JSON.parse(json || '[]'); } catch (e) { console.error(e); }
onDone(rows);
},
error: function (err) {
showMessage('목록을 불러오지 못했습니다. 새로고침 후 다시 시도하세요.');
console.error(err);
}
});
}필터, 정렬, 페이지는 브라우저에서
받은 배열을 상태 객체에 두고, 조건이 바뀔 때마다 걸러서 다시 그린다. 상태 값처럼 계산이 필요한 항목은 받아 온 직후 한 번만 만든다.
③ JS 스니펫: 상태 계산과 필터
var state = { rows: [], keyword: '', status: '', page: 1, size: 20 };
function withStatus(r) {
var today = new Date().setHours(0, 0, 0, 0);
if (r.clearDate) r.status = '조치완료';
else if (r.dueDate && r.dueDate < today) r.status = '지연';
else r.status = '조치예정';
return r;
}
function filtered() {
var kw = state.keyword.toLowerCase();
return state.rows.filter(function (r) {
if (state.status && r.status !== state.status) return false;
return !kw || (r.title || '').toLowerCase().indexOf(kw) > -1;
});
}
function pageRows(list) {
var start = (state.page - 1) * state.size;
return list.slice(start, start + state.size);
}
loadList(function (rows) {
state.rows = rows.map(withStatus);
render(); // filtered() → pageRows() 결과로 표를 그림
});저장과 삭제는 Microflow로
JS에서 객체를 직접 커밋하지 않고 Microflow를 호출한다. 목록 JSON에 담아 둔 guid를 넘긴다.
③ JS 스니펫: 삭제
function removeIssue(guid, onDone) {
mx.data.action({
params: {
actionname: 'Issue.ACT_DeleteIssue',
applyto: 'selection',
guids: [guid]
},
callback: function () { onDone(); },
error: function (err) {
showMessage('삭제하지 못했습니다.');
console.error(err);
}
});
}관련 글: 4편 Mendix mx.data.commit이 조용히 실패할 때
페이지를 이동하면 JS 스니펫이 다시 실행된다Mendix는 페이지를 이동해도 브라우저 문서를 새로 읽지 않는다. document에 건 이벤트가 이동할 때마다 쌓이므로 정리 코드가 필요하다. 3편에서 다룬다.
이 방식이 맞는 경우
| 상황 | Datagrid2 유지 | JS 위젯 전환 |
|---|---|---|
| 컬럼과 필터 | 단순하고 페이지마다 같음 | 페이지마다 조금씩 다름 |
| 상호작용 | 정렬, 검색 정도 | 행 펼치기, 커스텀 팝업, 일괄 처리 |
| 데이터 양 | 많음, 서버 페이징 필요 | 수천 건 이하 또는 사전 집계 가능 |
| 유지보수 | Studio Pro만 다룰 수 있음 | JS를 고칠 사람이 있음 |
전체를 한 번에 받는 구조라서 데이터가 1만 건을 넘기면서 서버 메모리 문제가 생겼다. 조회 컬럼을 줄이고, 결과를 미리 만들어 두는 방식으로 바꿨다.
관련 글: 5편 Java heap OOM 추적기, 7편 Mendix 첫 접속 지연 줄이기
전환 전 확인할 것
- 목록 JSON에 이미지나 긴 본문 같은 큰 컬럼을 넣지 않았는가
- 시스템 컨텍스트로 조회한다면 사용자별 조회 범위를 직접 걸었는가
- 저장과 삭제를 Microflow로 처리하는가
- 페이지 이동 시 이벤트를 정리하는 코드가 있는가
- 예상 데이터 양이 브라우저에서 처리할 수 있는 수준인가
시리즈 목차 16편
- PI 담당자가 로우코드로 사내 시스템을 직접 만들고 운영한 기록
- Mendix Datagrid2 대신 HTMLSnippet과 JS로 목록 화면 만들기 (현재 글)
- Mendix SPA에서 HTMLSnippet JS가 꼬이는 이유: 이벤트 중복과 DOM 공존
- Mendix mx.data.commit이 조용히 실패할 때
- Mendix Java heap OOM 추적기: base64 이미지 컬럼
- Mendix 커밋 롤백, 용량 문제가 아니었다: FileDocument 전환기
- Mendix 첫 접속 지연 줄이기: 워밍업과 캐시
- Mendix SAML SSO 자동 로그인과 딥링크 유지
- Mendix Studio Pro에서 안 바뀌는 스타일 main.scss로 바꾸기
- Mendix MPK 파일 열어서 마이크로플로우 구조 보기
- SSO 로그 테이블로 MAU와 재방문율 구하는 SQL
- Mendix DateTime이 BI 도구에서 하루 빠르게 보이는 이유
- 대시보드 숫자가 틀렸을 때 확인하는 순서
- 보기 좋은 대시보드에서 할 일을 알려주는 대시보드로
- 여러 점검 시스템을 하나로 합치는 데이터 모델
- 데이터 비전공자에게 분석 결과 보고하기