테마
업무패키지 개발 문서
대상: starj-portal 위에서 업무패키지(Business Package, 이하 BP)를 개발하는 개발자. 사내 개발자와 3rd-party를 구분하지 않는다 — 포탈에 내장된 관리자 업무패키지도 이 문서에 기술된 것과 같은 호스트 계약(연계 규약) 으로 개발되어 있다.
포탈 소스를 체크아웃하거나 포탈 인스턴스를 구동할 필요는 없다. 필요한 것은 Node 22 이상, 사설 레지스트리 접근, 그리고 아래 의존성이다.
@starj-portal 스코프에서 받는 것은 세 패키지다.
ini
# .npmrc — 설치에는 계정도 토큰도 필요 없다
@starj-portal:registry=https://npm.mdpert.com/bash
pnpm add -D @starj-portal/bp-backend-sdk # 서버 로직·API·DB가 있으면
pnpm add @starj-portal/bp-frontend-sdk # Portal Shell 안에 화면을 렌더링하는 경우
pnpm add -D @starj-portal/cli # 배포 전 점검과 패키지 아카이브를 만드는 starj CLI패키지별 최신 버전, 게시된 버전 목록, 개발 키트와 호스트 계약 버전의 관계는 패키지와 설치에 있다.
그러나 BP를 빌드하는 데 필요한 의존성은 이 셋이 전부가 아니다. npmjs에서 받는 일반 의존성이 더 필요하다 — @nestjs/common·reflect-metadata(포탈이 제공하지만 타입체크와 단독 실행에 필요하다), typescript(5.x로 고정), esbuild, react·react-dom·@types/react·@types/react-dom, DB를 쓰면 prisma·@prisma/client·어댑터. 정확한 목록과 버전은 시작하기 §1에 있다. 폐쇄망에서 개발한다면 이 의존성들의 사전 확보가 착수 조건이다.
가장 빠른 시작
bash
npx --registry https://npm.mdpert.com/ @starj-portal/cli bp create절차 전체는 업무패키지 생성 안내에 있다. 검증과 패키징에 쓰는 starj CLI가 프로젝트 생성도 맡으므로 도구를 따로 설치하지 않는다. CLI가 BP id·이름·App·메뉴·기본 권한 역할·자체 테이블 사용 여부를 묻고, 위 의존성과 버전 고정을 모두 갖춘 프로젝트를 만든다. 만든 직후 빌드·배포 전 점검·단독 스모크를 실행하고, 포탈에 설치해 권한을 부여하면 App 화면에 제목이 표시된다. 아래 정본 예제의 빌드·점검 스크립트를 범용으로 옮긴 것이므로 문서가 설명하는 규칙이 그대로 적용된다.
실행되는 정본 예제
문서에 실린 코드 조각은 실행되지 않은 채 낡기 쉽다. 실제로 그런 상태에서 차단 결함 여러 건이 남아 있었다. 그래서 이 문서들은 긴 코드를 문서에 옮겨 적지 않고 정본 예제를 가리킨다.
examples/task-bp/ 일감 관리 BP — 업무 화면 2개, REST API 4개, 자체 테이블 1개,
sqlite/postgresql 두 방언, 읽기 전용·편집 두 역할, csr-widget 화면빌드·타입체크·배포 전 점검·마이그레이션 실적용·패키지 아카이브와 그 검증·단독 스모크가 한 명령(pnpm run verify)으로 전부 실행된다. 문서가 "예제의 어느 파일을 보라"고 지시하면 그 파일이 정본이다.
읽는 순서
- 시작하기 — 빈 디렉터리에서 시작해 포탈에 등록되는 BP 하나를 처음부터 끝까지 개발한다. 나머지 문서는 여기서 등장한 개념을 상세히 다룬다.
- 호스트 계약 — 포탈과 BP 사이의 경계(책임 분리선). 무엇을 제공받고 무엇을 지켜야 하는가. 이 문서의 규정을 위반하면 오류 메시지 없이 오작동한다.
- 그다음은 개발 대상에 따라 구분된다.
- 서버 로직·API·DB → 백엔드 가이드
- 알림·공통코드·사용자 조회·외부 시스템 호출·BP 간 연계·감사로그·첨부 파일(계약 1.1.0의 포탈 자원 접근 표면) → 백엔드 가이드 §13~§19
- Portal Shell 안의 화면 → 프론트엔드 가이드
- 선언 파일의 정확한 스키마 → 매니페스트 레퍼런스
- 개발이 끝나면 빌드와 패키징 → 포탈에 설치하기.
- 동작하지 않는데 오류 메시지도 표시되지 않으면 트러블슈팅. 이 플랫폼의 실패는 대부분 예외가 아니라 오류 메시지 없는 오작동으로 나타난다.
포탈이 제공하지 않는 것
찾다가 시간을 쓰지 않도록 먼저 확정해 둔다. 아래는 현재 계약에 존재하지 않으며, 각 항목의 대안은 링크한 절에 있다.
| 없는 것 | 대안 |
|---|---|
| 주기 작업(스케줄러) 등록 수단 | backend-guide.md §9 — 사용자 조작 또는 외부 스케줄러가 BP의 REST를 호출한다 |
| 포탈 트랜잭션 참여, 요청 단위 트랜잭션 | backend-guide.md §6 — BP 자신의 클라이언트 안에서만 원자성을 얻는다 |
| 포탈 사용자 목록·검색 | backend-guide.md §15 — 단건 조회만 제공한다. 사람을 찾는 화면은 조직 정보를 소유한 BP의 서비스를 호출한다 |
| 다른 BP의 데이터 직접 조회 | backend-guide.md §17 — 상대 BP가 services.provide()로 공개한 오퍼레이션만, dependsOn[]에 선언한 경우에만 호출한다 |
| 포탈 공통코드 쓰기(런타임) | backend-guide.md §14 — 쓰기는 onInstall의 설치 컨텍스트에만 있다 |
| 포탈이 보관하는 BP 설정값 저장소 | backend-guide.md §10 — BP 자기 테이블에 보관한다. 다만 외부 시스템 접속 정보는 Destination으로 다룬다(§16) |
| claim한 첨부 파일의 포탈 범용 다운로드 경로 | backend-guide.md §19 — 열람 권한 판정은 소유 BP의 책임이다 |
| 위젯의 App 단위보다 세밀한 권한 | frontend-guide.md §3 — canWrite는 App 전체에 대한 판정 하나다 |
| 매니페스트를 검증하는 CLI·JSON Schema | build-and-package.md §6 — 예제의 scripts/preflight.mjs를 복사해 쓴다 |
| 조직·부서·상위자 정보 | backend-guide.md §15 — BpUserProfile은 다섯 필드다. 조직 구조가 필요하면 그 정보를 소유한 BP의 서비스를 §17로 호출한다 |
create · delete 단위의 권한 판정 | backend-guide.md §4 — 판정 축은 read/update 둘이다. 생성·삭제는 update로 판정하고 세분화는 BP의 업무 규칙으로 구현한다 |
| 발행한 알림의 전달 상태 조회 | backend-guide.md §13 — 발행 시점의 반환값이 전부다 |
| BP가 자기 감사 기록을 조회하는 수단 | backend-guide.md §18 — 기록만 가능하며 조회는 포탈 감사 화면이 담당한다 |
| 공통코드의 부가 속성 | backend-guide.md §14 — BpCodeEntry는 네 필드다. 코드에 딸린 값(통화별 소수 자리수 등)은 BP 상수로 보유한다 |
| 첨부 파일의 스트리밍 읽기 | backend-guide.md §19 — read()는 파일 전체를 메모리에 적재해 반환한다 |
| 제공 측 BP가 소비 측에 상태 변화를 통지하는 통로 | backend-guide.md §17-2 — 완결 지점을 소유 BP 하나로 고정한다 |
| 설치 시점의 도메인별 시딩 자리 | backend-guide.md §8-1 — onInstall에는 도메인이 없다. 첫 사용 시점 시딩으로 대체한다 |
| 호출한 API 클라이언트를 식별하는 수단 | backend-guide.md §9-1 — 유효한 토큰을 보유한 클라이언트는 전부 같게 취급된다 |
| 요청 본문 크기 상한의 런타임 조회 | backend-guide.md §12-1 — 값 자체는 문서에 명시되어 있다 |
계약 1.1.0에서 열린 것 — 이전 판 문서가 "없다"고 기술했던 항목
| 이제 있는 것 | 어디에 |
|---|---|
| 포탈 알림 발행 | backend-guide.md §13 |
| 포탈 공통코드 런타임 읽기 | backend-guide.md §14 |
| 포탈 사용자 프로필 단건 조회 | backend-guide.md §15 |
| 등록된 Destination 경유 외부 호출 | backend-guide.md §16 |
| BP 간 in-process 서비스 연계 | backend-guide.md §17 |
| 포탈 감사로그 기록 채널 | backend-guide.md §18 |
| 업무 첨부 파일 저장·조회 | backend-guide.md §19 |
| 위젯이 사용자·쓰기 권한을 읽는 경로 | frontend-guide.md §3 — viewer / canWrite |
| 위젯의 토스트·확인 대화상자·다른 App으로의 이동·오류 보고 | frontend-guide.md §6·§7·§8 |
| 런타임에 포탈의 계약 버전과 BP 적재 결과를 조회하는 API | host-contract.md §5 — GET /bp/runtime-info |
| 카드 payload 타입과 규모 상한 상수 | backend-guide.md §5-3 — SDK가 export한다 |
| 위젯이 파일을 포탈 저장소에 업로드하는 경로 | frontend-guide.md §6 — uploadFile |
| 기계 대 기계 인증(외부 스케줄러가 BP의 REST를 호출하는 수단) | backend-guide.md §9-1 — 계약 1.1.0과 무관하게 포탈이 제공하던 것이며 이번 판에서 문서화되었다 |
이 문서들이 하지 않는 것
- 포탈 내부 구현을 설명하지 않는다. 로더가 클래스를 합성하는 방식이나 레지스트리의 upsert 방식은 포탈 저장소의 담당 범위다. 여기서는 BP 쪽에 노출되는 인터페이스와 BP 개발자가 관측할 수 있는 결과만 다룬다.
- 포탈 운영자 절차를 설명하지 않는다. 권한 부여·도메인 활성화·사용자 관리는 포탈 관리자 매뉴얼에 있다. 이 문서에서 다루는 설치(포탈에 설치하기)는 "개발자가 만든 산출물이 어떤 형태여야 하는가"까지다.
용어
이 문서들이 축약해서 쓰는 표현의 정의다. 본문에서 처음 나올 때 한 번 풀어 쓰고, 그 뒤로는 축약형을 쓴다.
| 용어 | 뜻 |
|---|---|
| BP(업무패키지, Business Package) | 포탈에 설치하는 독립 배포 단위의 업무 기능 하나. 업무 화면(App) 여러 개, API, 자체 DB 스키마를 가질 수 있다 |
| Portal Shell(포탈 셸) | 사용자가 로그인해 메뉴와 업무 화면을 이용하는 프론트엔드 애플리케이션. 포탈의 서버 측 구성요소(API 서버·로더·레지스트리)와 구분하기 위한 이름이며, 위젯을 마운트하고 홈 카드를 렌더링하는 주체가 이것이다 |
| App(업무 화면) | BP가 소유하는 개별 실행 화면. 권한 부여와 메뉴 배치의 단위다 |
| 메뉴 배치 | 해당 App을 메뉴 트리의 어느 위치에 노출할지에 대한 선언. App 선언과 분리되어 있고, 같은 App을 여러 위치에 배치할 수 있다 |
| 도메인(업무 구분 단위) | 한 조직 내부의 업무 단위 구분(고객사 격리 개념이 아니다). 같은 BP가 도메인 A에서는 활성, B에서는 비활성일 수 있다 |
| 호스트 계약(연계 규약) | 포탈이 BP에 보장하는 런타임 인터페이스와 공유 모듈의 집합. "계약"은 설계 의도와 준수 사항을 함께 규정한 규약이라는 뜻이며, 버전이 부여된다 |
| 경계(책임 분리선) | 포탈과 BP가 서로 침범하지 않도록 정한 책임의 구분선. 예를 들어 권한 판정은 포탈이, 업무 데이터는 BP가 담당한다 |
| 등록 정보(능력 표면) | BP가 register() 반환값으로 포탈에 등록하는 화면·메뉴·카드·권한 목록 |
| csr-widget | BP 화면을 iframe 없이 Portal Shell 화면 안에 직접 마운트하는 배포 방식. 제3자 BP가 실제로 쓸 수 있는 유일한 화면 방식이다(bp-manifest.md §frontend) |
| 홈 인사이트 카드 | Portal Shell 홈 화면에 배치되는 지표 카드. BP는 형태(kind)와 데이터 경로만 선언하고, 렌더링은 Portal Shell이 담당한다(backend-guide.md §5) |
| 포탈 자원 접근 표면 | 호스트 계약 1.1.0에서 호스트 런타임에 추가된 7종의 오퍼레이션 묶음 — 알림·공통코드·사용자 조회·Destination·BP 간 연계·감사로그·첨부 파일. 오퍼레이션만 열려 있고 포탈 서비스 객체나 데이터 저장소 핸들은 열려 있지 않다(backend-guide.md §13~§19) |
| Destination(외부 연결처) | 운영자가 포탈에 등록하는 외부 시스템 접속 정의. 주소·자격증명·인증 방식·허용 경로가 포함되며, BP는 이름만 알고 호출한다(backend-guide.md §16) |
| 런타임 정보 API | GET /bp/runtime-info. 포탈이 제공하는 계약 버전, 운영 DB 방언, 적재된 BP와 건너뛴 BP의 사유를 반환한다. 업무패키지 관리 권한을 요구한다(host-contract.md §5) |
버전 정책
SDK의 major 버전은 호스트 계약 버전(HOST_CONTRACT_VERSION)과 함께 움직인다. 게시된 패키지 버전의 major·minor는 그 SDK가 공표하는 계약 버전과 일치한다. patch는 계약을 바꾸지 않는 SDK 수정(테스트 더블 보완 등)에만 올린다. 레지스트리에 게시하기 전까지는 번호를 다시 올리지 않고, 마지막 게시 이후의 변경을 다음 버전 하나에 모은다(저장소의 docs/releasing.md §규칙. 이 문서는 SDK 유지보수자용이라 사이트에는 싣지 않는다).
- 설치한 SDK가 공표하는 계약 버전은
HOST_CONTRACT_VERSION이 단일 원천이다. 대상 포탈이 제공하는 계약 버전은 그것과 별개이며GET /bp/runtime-info로 조회한다. - 이 SDK가 공표하는 계약 버전은
1.3.0이다.bp-manifest.yaml의runtime.hostContract에는 실제로 사용하는 표면에 맞는 범위를 선언한다 —files.create()나 설치 컨텍스트의onExisting을 사용하면"^1.3.0",files.claim()을 사용하면"^1.2.0"(1.2.0에서 인자가 바뀌었다), 그 밖에 1.1.0에서 추가된 표면을 사용하면"^1.1.0", 1.0.0의 표면만 사용하면"^1.0.0"이다. 1.3.0에서 늘어난 Destination 사용자 토큰 차단 사유를 처리하는 것만으로는 범위를 올리지 않는다(bp-manifest.md §runtime). @starj-portal/bp-backend-sdk@^1을 찾을 필요가 없다. 그런 버전은 게시되어 있지 않다.
bash
node -e "import('@starj-portal/bp-backend-sdk').then(m => console.log(m.HOST_CONTRACT_VERSION))"이 명령이 출력하는 것은 내가 설치한 SDK가 공표하는 계약 버전이며, 대상 포탈이 제공하는 계약 버전이 아니다. 대상 포탈의 값은 GET /bp/runtime-info로 조회한다 — 업무패키지 관리 권한이 필요하며, 없으면 운영자에게 요청한다(host-contract.md §5).