NetSuite SuiteScript 2.1: ERP를 미치지 않고 자동화하기
Source: Dev.to
NetSuite를 사용해 본 적이 있다면, 기본 설정만으로는 어느 정도까지만 할 수 있다는 것을 알게 될 것입니다. 그 이후에는 코딩이 필요합니다. 여기서 SuiteScript가 등장합니다 — 플랫폼의 런타임 JavaScript로, 프로세스를 자동화하고, 데이터를 검증하며, NetSuite를 외부 세계와 연결할 수 있게 해줍니다. 이 기사에서는 바로 시작할 수 있는 핵심 내용을 보여드리며, 오늘 바로 적용할 수 있는 예제들을 제공합니다.
왜 SuiteScript 2.1이고 1.0이 아닌가?
버전 1.0은 아직 오래된 인스턴스에 존재하지만, 전역 변수(globals)가 여기저기 흩어져 있고 실행 모델이 혼란스럽다. 2.0부터는 모든 것이 AMD 모듈(RequireJS 스타일)로 구성되어 코드가 훨씬 예측 가능해진다. 2.1 버전은 async/await와 기타 최신 기능을 지원한다. 가능한 한 항상 2.1을 사용하라.
모든 스크립트의 기본 구조
/**
* @NApiVersion 2.1
* @NScriptType ScheduledScript
*/
define(['N/search', 'N/log'], (search, log) => {
const execute = (context) => {
log.audit({ title: 'Hola', details: 'Script corriendo' });
};
return { execute };
});시작 부분의 @N 주석은 선택 사항이 아닙니다 — NetSuite가 스크립트 유형과 API 버전을 식별하기 위해 이를 읽습니다.
사용자 이벤트 스크립트
User Event Script는 레코드의 라이프사이클에 연결됩니다. 저장하기 전에 검증하고 싶으신가요? 로드 시 필드를 수정하고 싶으신가요? 바로 이 유형입니다.
클래식 예시: 중요한 필드가 없을 경우 저장 차단
/**
* @NApiVersion 2.1
* @NScriptType UserEventScript
*/
define(['N/error'], (error) => {
const beforeSubmit = (context) => {
if (context.type === context.UserEventType.DELETE) return;
const record = context.newRecord;
const codigo = record.getValue({ fieldId: 'custbody_codigo_aprobacion' });
if (!codigo) {
throw error.create({
name: 'MISSING_FIELD',
message: 'El código de aprobación es obligatorio.',
notifyOff: true
});
}
};
return { beforeSubmit };
});throw error.create()는 저장을 자동으로 취소하고 UI에 사용자에게 메시지를 표시합니다. 별도로 할 일이 없습니다.
Scheduled Scripts와 거버넌스 문제
Scheduled Scripts는 백그라운드에서 사용자 없이 실행됩니다. 야간 동기화, 자동 보고서, 데이터 정리에 완벽합니다.
핵심은: 각 작업은 governance units를 소모합니다. 레코드 로드에 10, 저장에 20, 검색에 10이 필요합니다. 실행당 한도는 10 000 유닛이며, 이를 초과하면 스크립트가 중간에 종료됩니다.
대규모 프로세스의 경우 Map/Reduce Script가 올바른 솔루션입니다. 작업을 병렬 단계로 나누고 각 단계마다 자체 거버넌스 풀을 가집니다. 작성은 더 복잡하지만 확장성이 좋습니다.
실시간으로 남은 유닛 확인
import runtime from 'N/runtime';
const restante = runtime.getCurrentScript().getRemainingUsage();RESTlets: NetSuite 안의 API
외부 시스템이 NetSuite에서 레코드를 조회하거나 생성해야 하나요? RESTlet은 기본적으로 플랫폼 내에 존재하는 HTTP 엔드포인트입니다.
/**
* @NApiVersion 2.1
* @NScriptType Restlet
*/
define(['N/search'], (search) => {
const get = (params) => {
const { email } = params;
if (!email) return { error: 'email requerido' };
const results = [];
search.create({
type: search.Type.CUSTOMER,
filters: [['email', search.Operator.IS, email]],
columns: ['companyname', 'phone']
}).run().each((r) => {
results.push({
nombre: r.getValue('companyname'),
telefono: r.getValue('phone')
});
return true;
});
return { clientes: results };
};
return { get };
});배포하고 deployment ID를 할당하면, 이제 외부 시스템 어디서든 OAuth 2.0으로 호출할 수 있는 엔드포인트가 생깁니다.
시작할 때 알았으면 좋았을 3가지
- SuiteCloud CLI를 설치하세요. NetSuite UI에서 스크립트를 업로드하는 것은 번거롭습니다. CLI를 사용하면
suitecloud file:upload로 일반 프로젝트처럼 스크립트를 관리할 수 있습니다. - 로그는 당신의 가장 좋은 친구이자 최악의 적입니다.
N/log에는 네 가지 레벨이 있습니다 —debug,audit,error,emergency. 프로덕션에서는debug를 비활성화하세요, 그렇지 않으면 로그가 빠르게 가득 찹니다. NetSuite는 스크립트당 최근 5 000개의 레코드만 보관합니다. - 결과 수를 모를 때는
search.runPaged()를 사용하고search.run().each()는 피하세요..each()는 4 000개의 결과 제한이 있습니다; 페이지네이션은 제한이 없습니다.
const paged = miSearch.runPaged({ pageSize: 1000 });
paged.pageRanges.forEach((range) => {
const page = paged.fetch({ index: range.index });
page.data.forEach((result) => {
// procesa cada resultado
});
});어디서 시작할까?
Oracle 공식 문서(SuiteAnswers)는 방대하지만 완전합니다. 실제 진입점은 필요한 모듈을 찾는 것입니다: N/record, N/search, N/email, N/https — 모두 동일한 패턴을 따릅니다.
특정 스크립트 유형이나 통합 사례에 대해 질문이 있으면 댓글에 남겨 주세요.