넥사크로를 쓰며 불편했던 점과 피해 가는 방법
넥사크로로 여러 프로젝트를 하면서 쌓인 불편을 적어 둔다. 대부분은 피해 갈 방법이 있어서 그 방법도 함께 적었다. 버전에 따라 달라지는 부분이 있으니, 쓰고 있는 버전의 릴리스 노트를 함께 확인하기를 권한다.
1. 전용 스튜디오의 한계
국산 UI 도구마다 개발 환경이 다르다.
| 도구 | 개발 환경 |
|---|---|
| eXBuilder6 | 이클립스에 플러그인 설치 |
| WebSquare | 이클립스에 플러그인 설치 |
| 넥사크로 | 전용 스튜디오(Nexacro Studio) |
전용 스튜디오는 화면 편집과 스크립트가 한곳에 있다는 장점이 있지만, 범용 IDE 에 익숙하면 아쉬운 점이 보인다. 제가 쓴 버전에서 겪은 것들이다.
- 자주 멈춘다. Design 탭에서 그리드를 더블클릭할 때, Source 탭에서 컴포넌트 이름을 일괄 변경한 뒤 저장할 때 스튜디오가 종료되는 일이 잦았다. 큰 변경 전에는 저장·커밋을 먼저 해 두는 습관이 필요하다.
- 자동완성이 약하다.
this.ds_main.처럼 객체에 바로 접근하면 속성·메서드가 나오지만, 한 번 변수에 담고 나면 자동완성이 되지 않는다. - 변수 타입을 알 수 없다. 그래서
nRow,strColId처럼 접두어로 타입을 드러내는 이름 규칙을 팀에서 정해 쓰곤 한다. 함수 인자에는obj:nexacro.NormalDataset같은 타입 표기를 스튜디오가 붙여 주지만, 지역 변수까지 추론해 주지는 않는다. - 편집기 분할이 불편하다. 좌우·세 칸으로 나란히 띄워 놓고 쓰기가 범용 IDE 만큼 매끄럽지 않다.
스크립트를 오래 다듬을 때는 생성된 .xfdl 의 스크립트 부분을 VS Code 같은 편집기에서 함께 열어 보는 것도 방법이다. 다만 저장은 한쪽에서만 해야 덮어쓰기 사고가 없다.
2. 최신 문법 지원이 늦고, async 이벤트 함수가 어긋난다
넥사크로는 오랫동안 ES5 수준의 문법만 쓸 수 있었다. 제가 알기로는 넥사크로 N(V24) 의 2024년 하반기 배포판(24.0.0.400 무렵)부터 async/await 를 쓸 수 있다. 정확한 지원 범위는 사용하는 버전의 릴리스 노트로 확인하는 것이 안전하다.
그런데 써 보니 두 가지가 걸렸다.
① async 를 붙인 이벤트 함수를 스튜디오가 찾지 못한다. 아래처럼 쓰면 Design 탭의 이벤트 목록에서 이 함수로 이동하지 못했다.
this.ds_main_oncolumnchanged = async function (obj, e) {
// ...
};
② can 이벤트에서 async 를 써도 기다려 주지 않는다. cancolumnchange 안에서 await 로 확인 작업을 해도, 결과를 기다리지 않고 곧바로 oncolumnchanged 가 실행됐다.
②는 자바스크립트 동작으로 설명이 된다. async 함수는 호출되는 즉시 Promise 를 반환한다. can 이벤트는 핸들러의 반환값으로 진행 여부를 판단하는데, Promise 는 false 가 아닌 객체이므로 “진행해도 된다”로 처리되고, 함수 안의 await 뒤쪽은 그 다음에 실행된다. 즉 can 이벤트처럼 반환값을 바로 쓰는 이벤트에는 async 를 쓰면 안 된다. 비동기 확인이 필요하면 can 이벤트에서는 일단 false 로 막고, 확인이 끝난 뒤 스크립트로 값을 다시 넣는 식으로 흐름을 바꿔야 한다.
3. this 가 가리키는 대상
도구마다 화면 스크립트의 중심이 되는 키워드가 있다. eXBuilder6 는 app, WebSquare 는 scwin, 넥사크로는 this 다.
넥사크로의 화면 함수 안에서 this 는 대체로 현재 Form 이다. 문제는 자바스크립트의 this 가 함수를 어떻게 호출했는지에 따라 정해진다는 점이다. 콜백으로 넘긴 일반 함수 안에서는 this 가 Form 이 아니게 된다(엄격 모드가 아니면 전역 객체, 엄격 모드면 undefined).
자주 부딪히는 곳은 배열 메서드와 Promise 콜백이다. ES5 범위에서도 해결 방법이 있다.
this.fn_sumAmount = function (rows) {
// ① map, filter, forEach 등은 두 번째 인자로 this 를 넘길 수 있다
var names = rows.map(function (row) {
return this.fn_format(row.name);
}, this);
// ② reduce 에는 thisArg 인자가 없다 → bind 로 묶는다 (ES5)
var total = rows.reduce(function (sum, row) {
return sum + this.fn_toNumber(row.amount);
}.bind(this), 0);
return { names: names, total: total };
};
this.fn_load = function () {
var self = this; // ③ 가장 단순한 방법: 바깥에서 변수에 담아 둔다
this.fn_request("search").then(function (result) {
self.ds_main.loadXML(result);
});
};
화살표 함수를 쓸 수 있는 버전이라면 화살표 함수는 바깥 this 를 그대로 쓰므로 bind 나 self 가 필요 없다.
넥사크로의 transaction 은 콜백을 함수 이름 문자열로 받는다. 가독성 때문에 이것을 Promise 로 감싸 쓰는 프로젝트도 있는데, 그때 then 콜백 안에서 위의 this 문제가 생긴다. 감싸는 쪽에서 위 방법 중 하나로 Form 을 묶어 두면 된다.
4. 고치지 않은 xfdl 파일이 형상관리에서 바뀐 것으로 나온다
유독 .xfdl 파일이, 내가 손대지 않은 부분까지 형상관리 비교에서 수정된 것으로 나오는 일이 잦았다. 원인은 끝내 확인하지 못했다. 스튜디오가 저장할 때 파일 전체를 다시 쓰기 때문이 아닐까 짐작만 하고 있다.
차이가 실제 내용인지 줄바꿈·공백인지는 먼저 가려 볼 수 있다. Git 을 쓴다면 다음처럼 비교해 본다.
git diff --ignore-cr-at-eol -- path/to/Form.xfdl # 줄끝(CRLF/LF) 차이 무시
git diff -w -- path/to/Form.xfdl # 공백 차이 무시
여기서 차이가 사라지면 저장소의 줄바꿈 설정(.gitattributes)을 맞추는 것으로 상당 부분 줄일 수 있다.
정리
- 스튜디오는 자주 저장·커밋하고, 긴 스크립트는 다른 편집기를 함께 쓴다
async는 반환값을 바로 쓰는 can 이벤트에 쓰지 않는다- 콜백 안의
this는thisArg,bind,self변수(또는 화살표 함수)로 Form 에 묶는다 - xfdl 의 이상한 변경은 줄끝·공백 차이부터 의심한다
다시 쓰며 바로잡은 점
- 원문의 “ES6 미지원” 은 정확하지 않은 표현이라 “최신 문법 지원이 늦다”로 고쳤다.
async/await는 ES2017 문법이다. - can 이벤트에서 async 가 기다려 주지 않는 이유(async 함수는 즉시 Promise 를 반환)를 덧붙였다.
- 배열 메서드·Promise 콜백의
this문제에 ES5 에서도 쓸 수 있는 해결 방법을 추가했다.