[Nexacro N] Java 어댑터 심화
Nexacro N Java 어댑터의 내부 구조와 DataSet 조작 API를 상세히 설명합니다. NexacroServlet, PL 파서, ServiceDispatcher의 동작 원리와 DataSet 읽기/쓰기 패턴을 다룹니다.
지난 글에서 Spring Boot와 Nexacro N을 연동하는 전체 설정 방법을 살펴보았다. 이번에는 Java 어댑터가 내부적으로 어떻게 동작하는지, 그리고 DataSet API를 실제 업무 코드에서 어떻게 사용하는지 더 깊이 들어간다.
Java 어댑터는 Nexacro N의 공식 서버 SDK다. 클라이언트가 전송한 PL 스트림을 Java 객체로 변환하고, Spring 컨텍스트에서 서비스 빈을 찾아 메서드를 호출하는 전 과정을 담당한다.
어댑터 내부 처리 흐름
요청이 들어오면 NexacroServlet이 HTTP 요청을 수신한다. 이어 PL 파서가 요청 본문을 파싱해 DataSet과 VariableList Java 객체를 생성한다. ServiceDispatcher는 서비스 ID(SVC::ClassName::methodName)를 파싱해 Spring 컨텍스트에서 해당 서비스 빈을 찾고 리플렉션으로 메서드를 호출한다.
DataSet API 상세
입력 DataSet에서 데이터 읽기
public void search(DataSet dsIn, DataSet dsOut, VariableList vl) {
// 단일 값 읽기
String userId = dsIn.getStringColumn(0, "USER_ID");
int pageNo = dsIn.getIntColumn(0, "PAGE_NO");
double amount = dsIn.getDoubleColumn(0, "AMOUNT");
// 날짜는 String으로 받아 변환
String startDt = dsIn.getStringColumn(0, "START_DT");
LocalDate date = LocalDate.parse(startDt, DateTimeFormatter.ofPattern("yyyyMMdd"));
// 복수 행 처리
for (int i = 0; i < dsIn.getRowCount(); i++) {
String nm = dsIn.getStringColumn(i, "ITEM_NM");
int qty = dsIn.getIntColumn(i, "QTY");
// 처리 로직
}
}
출력 DataSet 구성
// 방법 1: NexacroUtils 사용 (권장 — MyBatis Map 결과와 자동 매핑)
List<Map<String, Object>> rows = mapper.selectUsers(param);
NexacroUtils.copyToDataSet(rows, dsOut);
// 방법 2: 직접 컬럼·행 추가
dsOut.addStringColumn("USER_ID");
dsOut.addStringColumn("USER_NM");
dsOut.addIntColumn("AGE");
int row = dsOut.newRow();
dsOut.set(row, "USER_ID", "U001");
dsOut.set(row, "USER_NM", "홍길동");
dsOut.set(row, "AGE", 30);
NexacroUtils.copyToDataSet()을 사용하면 List<Map<String, Object>> 형태의 MyBatis 결과를 DataSet으로 자동 변환한다. Map의 키가 컬럼명이 되고, 값 타입에 따라 String/Int/Double 컬럼이 자동으로 생성된다.
RowType 기반 CRUD 처리
클라이언트에서 전송된 DataSet의 각 행에는 RowType이 설정되어 있다. 서버에서는 이를 읽어 INSERT/UPDATE/DELETE를 분기한다.
public void save(DataSet dsUser, DataSet dsResult, VariableList vl)
throws NexaServiceException {
int successCnt = 0;
for (int i = 0; i < dsUser.getRowCount(); i++) {
int rowType = dsUser.getRowType(i);
Map<String, Object> row = NexacroUtils.rowToMap(dsUser, i);
switch (rowType) {
case DataSet.ROW_TYPE_INSERTED:
userMapper.insertUser(row);
successCnt++;
break;
case DataSet.ROW_TYPE_UPDATED:
userMapper.updateUser(row);
successCnt++;
break;
case DataSet.ROW_TYPE_DELETED:
userMapper.deleteUser((String) row.get("USER_ID"));
successCnt++;
break;
}
}
vl.addVariable("errCode", "0");
vl.addVariable("errMsg", successCnt + "건 처리되었습니다.");
}
ROW_TYPE_NORMAL(1), ROW_TYPE_INSERTED(2), ROW_TYPE_UPDATED(4), ROW_TYPE_DELETED(8)이 주로 사용된다. dsUser.getRowCount()는 삭제된 행도 포함하므로 getRowType으로 상태를 반드시 확인해야 한다.
VariableList 활용
VariableList는 Dataset이 아닌 단순 key-value 쌍을 전송하는 데 사용한다. 페이지 정보, 오류 코드, 집계값 등을 전달하기 좋다.
// 서버: VariableList에서 읽기
String searchType = vl.getStr("SEARCH_TYPE");
int pageNo = vl.getInt("PAGE_NO");
// 서버: VariableList에 쓰기
vl.addVariable("errCode", "0");
vl.addVariable("errMsg", "정상 처리");
vl.addVariable("TOTAL_CNT", String.valueOf(totalCount));
vl.addVariable("PAGE_TOTAL", String.valueOf(pageTotalCount));
클라이언트에서는 콜백 함수 인자나 this.getVariable("TOTAL_CNT")로 받는다.
트랜잭션 예외 처리
public void search(DataSet dsIn, DataSet dsOut, VariableList vl)
throws NexaServiceException {
try {
// 비즈니스 로직
List<Map<String, Object>> list = mapper.selectUsers(dsIn);
NexacroUtils.copyToDataSet(list, dsOut);
} catch (DataAccessException e) {
// DB 오류 → 어댑터가 PL 오류 응답으로 변환
throw new NexaServiceException("DB_ERR", "데이터 조회 중 오류: " + e.getMessage());
}
}
NexaServiceException을 throw하면 어댑터가 클라이언트에 오류 코드와 메시지를 담은 PL 응답을 반환한다. 클라이언트 콜백의 errCode와 errMsg로 전달된다.
지난 글: Spring Boot 연동
다음 글: Node.js 어댑터
읽어주셔서 감사합니다. 😊