넥사크로 화면을 Vue 로 옮기기 — 스크립트는 그대로 두고 변환기로
넥사크로 17 로 만든 개인 ERP(VERA)를 Vue 3 로 옮겼다. 결론부터 말하면 화면은 Vue 로 그리고, 업무 로직은 넥사크로 스크립트를 한 줄도 고치지 않고 그대로 돌린다. 화면 233장 가운데 229장을 이렇게 옮겼고, 화면 스크립트를 손으로 고친 곳은 없다.
- AS-IS: Nexacro 17 (넥사크로 N 17) + Spring Boot
- TO-BE: Vue 3 + 직접 만든 넥사크로 호환 런타임(이하 Nx) + Spring Boot 3
- 실제 화면: https://erp.victory.sarl/
먼저 라이선스부터 봤다
넥사크로 SDK 의 자바스크립트 라이브러리는 두 갈래로 나뉜다. 설치 폴더의 라이선스 고지문(Nexacro N public license readme) 기준으로 정리하면 이렇다.
| 구분 | 하는 일 | 라이선스 |
|---|---|---|
component (nexacrolib\component) |
그리드·콤보·데이터셋 같은 부품의 동작 | BSD 3-Clause. 저작권 고지만 남기면 고쳐 쓰고 배포할 수 있다 |
framework (nexacrolib\framework, resources) |
화면 그리기·이벤트·통신·창 관리 엔진 | 소스는 보이지만, 정식 구매자가 사용 허가서 범위 안에서만 수정·사용할 수 있다 |
그래서 처음엔 “component 는 옮겨 오고, framework 가 하던 렌더링과 통신만 새로 만들면 되겠다”고 생각했다. 라이선스 조건은 버전·계약에 따라 다를 수 있으니, 같은 일을 하려면 자기가 가진 SDK 의 고지문과 계약서를 직접 확인해야 한다.
실제로 새로 만든 것
막상 해 보니 framework 가 하던 일은 렌더링과 통신만이 아니었다. 아래를 모두 새로 만들었고, framework 소스는 한 줄도 가져오지 않았다. 동작만 넥사크로와 같게 맞췄다.
- 화면 그리기: Nx 컴포넌트 29종. API 모양은 넥사크로 그대로(
set_*,this.div.form.x같은 접근) - 좌표 배치:
left·right·45%·"stt_x:10"(다른 컴포넌트 기준 배치) 같은 넥사크로 배치 규칙 - 이벤트 순서: xfdl 에 적힌 처리기,
addEventHandler·insertEventHandler로 붙인 처리기의 실행 순서까지 - 폼 생명주기: 스크립트 실행 →
oninit→ 공통onload→ 화면onload - 거래와 콜백:
transaction을 서버 API 로 보내고, 오류 코드(-1 등)와 콜백 호출을 원본대로 - 팝업과 창: 모달·모덜리스, 메뉴 창 이동·크기·최대화
- 그리드 식:
expr:식 엔진, 머리 정렬, 체크, 트리, 셀 편집기 - 셸 전역: 전역 데이터셋, 메뉴 열기, 세션 타이머, 재로그인
열쇠는 변환기
화면을 사람이 옮기지 않는다. 변환기(xfdl2vue)가 xfdl 을 읽어서 두 파일로 나눈다.
.vue: 배치. 넥사크로 태그를 Nx 컴포넌트로 바꾸고, 좌표·이벤트 이름은 그대로 둔다.xjs: 스크립트. 원문에서 include 줄과 인자 타입 표기만 지운다

서버도 레거시의 서비스 규칙과 MyBatis 매퍼를 그대로 쓴다. AS-IS 화면을 고치면 변환기를 다시 돌리기만 하면 된다.
버튼 하나를 예로 들면 변환 전후가 이렇다.
<!-- xfdl (넥사크로 원본) -->
<Button id="btn_search" text="조회"
right="15" top="8" width="52" height="24"
onclick="btn_search_onclick"/>
<!-- .vue (변환 결과) -->
<NxButton id="btn_search" text="조회"
right="15" top="8" width="52" height="24"
@onclick="(o, e) => h('btn_search_onclick')(o, e)" />
h(...) 는 Nx 런타임이 그 화면의 스크립트에서 같은 이름의 함수를 찾아 주는 도우미다. 스크립트 쪽은 넥사크로 원문 그대로다.
// .xjs (원문 그대로, 일부 생략)
this.fn_search = function () {
this.submit({
id: "accountList",
url: "acc.accountList",
transaction: {
id: "selectAccountList",
recvDataset: "ds_list"
},
callback: function (svcId, errCode, errMsg) { /* ... */ }
});
};
여기서 this.submit 은 넥사크로 기본 API 가 아니라 원래 프로젝트의 공통 함수다. 공통 함수도 원본 스크립트를 그대로 가져와 같은 런타임 위에서 돈다.
결과
- 변환한 화면 229장 (233장 중 4장은 전용 부품으로 따로 만들었다)
- 손으로 고친 화면 스크립트 0줄. 보안 때문에 재로그인 함수 하나만 런타임 쪽에서 덮어썼다
- Nx 런타임 단위 시험 155개. 기대값은 넥사크로 원본의 동작
- AS-IS 와 TO-BE 가 같은 DB 를 함께 쓴다
빠진 기능이 있으면 화면마다 고치지 않고 Nx 한 곳에서 고친다. 한 번 고치면 229개 화면이 함께 좋아진다. 고정 콤보 목록(85곳)이나 그리드 머리 정렬이 그렇게 한 번에 해결됐다.
덤: 화면 디자이너
원본이 계속 xfdl 이라서 화면 디자이너도 만들 수 있었다. 변환된 실제 화면 위에서 끌어서 옮기고 손잡이로 크기를 바꾸면, 바뀐 속성만 xfdl 사본에 써 넣고(공백·줄 끝·스크립트는 그대로) 그 화면만 다시 변환한다. 45%, "stt_x:10" 같은 원본 배치 규칙도 그대로 쓴다. 공개된 곳(https://erp.victory.sarl/designer)에서는 보기 전용이다.
정리
원본은 계속 xfdl 이고, Vue 는 변환기가 만드는 결과물이다. 검증된 업무 로직은 그대로 두고 화면 기술만 바꾸므로 업무 로직을 다시 검증할 필요가 없다. 남은 일은 Jasper 출력물, 운영 디자이너 저장, 아직 못 만든 넥사크로 API 다.
다시 쓰며 바로잡은 점
- 원문은 발표 자료 그림만 있었다. 그림 속 글과 코드를 글자로 옮기고, 예제의 화면·서비스 이름은 일반적인 이름으로 바꿨다
- 라이선스 설명에 “버전·계약에 따라 다를 수 있으니 직접 확인”을 덧붙였다