Ongo Framework삼카 확장개발툴 홈코어 개발가이드개발툴 사용가이드배치실행

개발툴 사용가이드 · 삼카 확장

화면 구성요소에 12… 번호를 붙여 설명해요. 프레임워크 설정·SPI는 코어 개발가이드, 데모 확장·커스텀(Redis·MQ·ES 목업 포함)은 데모 확장·커스텀 카탈로그를 보세요. 접속: /framework-devtools(local·dev 프로파일).

화면 전체 구성

Ongo Framework Devtools — 코드생성 1코어 개발가이드 사용가이드
2대상 JDK
3VO/DTO 생성 · 업무단위 Controller·Service·Repository · 배치 잡 스켈레톤 · 전문 테스트
1
가이드 링크 — 코어 개발가이드(프레임워크 설정·SPI)와 사용가이드(이 문서)로 이동해요.
2
대상 JDK — 8/11은 클래스·javax, 17/21은 record·jakarta로 생성해요.
3
탭 — 주소의 #vo, #csr, #batch, #tg로 바로 열 수 있어요.

① VO/DTO 생성 — 상단 툴바

1정의서로 코드 생성 2 3 4종류 5 6
7 입력 유형
1
코드 생성 툴바 — 「새 정의서」 바로 위로 올라왔어요. 정의서를 편집하면서 스크롤 없이 바로 생성할 수 있어요.
2
전문용 VO — 필드에 전문 길이·타입 어노테이션을 붙여요. 입력 유형이 sc-excel이면 자동으로 체크돼요.
3
고정길이 — 체크하면 ARRAY 앞 건수(cnt) 필드를 검사해요. ④ 참고.
4
종류(kind) — 클래스명 접미사(Vo/Dto). 중첩 VO 이름에도 쓰여요.
5
미리보기 / 소스에 쓰기 — 미리보기는 파일을 만들지 않고 화면에만 보여줘요. 소스에 쓰기는 codegen.output-dir에 저장(local 전용).
6
덮어쓰기 — 끄면 같은 파일이 있을 때 건너뛰어요.
7
새 정의서 — 빈 정의서로 시작. 불러오기는 JSON·엑셀 정의서를 읽어요.

② I/O 정의서 편집 표

I/O 탭 — I/O가 1개여도 표 위에 탭이 보여요. 다른 탭은 클릭으로 전환, 선택된 탭을 한 번 더 클릭(또는 더블클릭)하면 그 자리에서 I/O 이름을 고칠 수 있어요. Enter=저장, Esc=취소, 바깥 클릭=저장 — JSON 정의서의 "name"에 바로 반영돼요.

I/O필드 1필드명타입 2길이소수 34
INamount금액15✕
INcnt건수3✕
INitems목록↳+ ✕
IN  └ itemCd품목10✕
1
필드 — 자바 필드명(camelCase).
2
타입 선택박스 — STRING, NUMBER, DECIMAL, INT, LONG, DATE, BOOLEAN, ARRAY, OBJECT 중 선택. 예전 GRID는 ARRAY로 통일됐어요(GRID 정의서·엑셀도 불러올 때 ARRAY로 바뀌어요).
3
소수(scale) — NUMBER·DECIMAL에서만 보여요. 0/빈칸이면 Long, 1 이상이면 BigDecimal로 생성돼요.
4
↳+ / ✕ — ARRAY·OBJECT에 하위 필드 추가 / 삭제. 「기존 타입 참조」를 채우면 ↳+는 숨겨져요(③-1).

③ 중첩 VO(SubNN) 규칙

ARRAY·OBJECT의 하위 필드는 같은 패키지의 별도 파일로 생성되고, 이름은 {메인 이름}Sub01{접미사}, Sub02… 순서예요. 하위 안의 하위도 같은 번호를 이어 써요.

ApcMIss00021InVo.java                  ← 메인
├─ List<ApcMIss00021InSub01Vo> items   → ApcMIss00021InSub01Vo.java
└─ ApcMIss00021InSub02Vo addr          → ApcMIss00021InSub02Vo.java
접미사는 종류(Vo/Dto)에서 정해요. 클래스명이 Vo/VO/Dto/DTO로 끝나면 그 앞에 SubNN을 끼워 넣어요. 클래스 어노테이션은 메인 파일에만 붙어요. 전문 테스트의 동적 VO는 기존처럼 한 파일(내부 클래스)로 컴파일돼요.

③-1 기존 타입 참조(생성 대신 참조)

ARRAY·OBJECT 타입은 타입 선택박스 옆에 「기존 타입 참조」 입력칸이 생겨요. 이미 있는 VO/DTO를 그대로 쓰고 싶을 때 단순명이나 FQCN을 넣으세요.

필드타입 1편집
cnt+ ✕
items 2+ ✕ 3
1
ARRAY 또는 OBJECT 선택.
2
기존 타입 참조 — 채우면 SubNN 파일을 만들지 않고 ARRAY는 List<ItemVo>, OBJECT는 ItemVo로 생성돼요(FQCN이면 import 추가). 하위 항목이 있었다면 지울지 물어보고, 취소하면 정의서에 보존만 되고 생성엔 안 쓰여요.
3
참조 중엔 ↳+(하위 추가)가 숨고 하위 행도 안 보여요. 칸을 비우면 다시 하위 항목 편집·SubNN 생성으로 돌아가요.
{"name":"items","type":"ARRAY","ref":"com.acme.common.ItemVo"}   // = "attributes":{"ref":"..."}
전문용 VO에서도 참조 필드는 @TelegramField(kind=GRID|OBJECT)로 순번이 매겨지고, 고정길이면 ARRAY 앞 건수(cnt) 검사도 똑같이 해요. 참조 타입 자체의 레이아웃 어노테이션은 그 클래스에 있어야 해요.

④ 고정길이·건수(cnt) 검사

고정길이 전문은 반복부 길이를 알 수 없어서 ARRAY 바로 앞 필드가 반복 건수(cnt)여야 해요. 「고정길이」를 체크하면:

  1. 타입을 ARRAY로 바꿀 때 앞 필드가 숫자 타입(NUMBER·INT·LONG·DECIMAL)이 아니면 알림이 떠요.
  2. 미리보기/소스에 쓰기 직전 전체 정의서를 검사해, 위반 필드 경로(예: ApcMIss00021.in.items)를 보여주고 생성을 멈춰요.
JSON API처럼 길이 정보가 필요 없는 VO는 체크를 끄세요. 검사하지 않아요.

⑤ 엑셀 불러오기

1입력 유형 2 3
1
sc-excel을 고르면 전문용 VO·고정길이가 자동 체크돼요(일반 양식은 excel).
2
정의서 엑셀 선택(/api/excel/template에서 양식 다운로드).
3
불러오면 표에 채워지고, GRID 타입은 ARRAY로 바뀌어요.

⑥ 업무단위 Controller·Service·Repository

업무명·패키지를 넣고 레이어를 고르면 규칙(/api/generate-layer/rules)에 맞는 이름으로 생성해요. 미리보기로 경로를 먼저 확인하세요.

Content-Type (메소드 행 선택)본문(in 타입) 어노테이션매핑
JSON (기본)@RequestBodyPOST/PUT/PATCH 에 consumes = MediaType.… 자동
Form (x-www-form-urlencoded)DTO면 @ModelAttribute, 없으면 @RequestParam Map
Multipart (form-data)@RequestPart("body") + 추가 part 파라미터

추가 파라미터 칸에 쉼표로 여러 개를 붙일 수 있어요: param page:Integer, header X-User-Id, path id:Long, part file → @RequestParam·@RequestHeader·@PathVariable(URI의 {id} 타입 지정)·@RequestPart MultipartFile. 타입 생략 시 String(part는 MultipartFile). GET에서 in 타입은 @ModelAttribute 쿼리 객체예요.

⑦ 배치 잡 스켈레톤

잡 이름·Reader/Writer 유형을 고르면 Job/Step 설정 골격을 만들어요. 실행·이력은 /framework-devtools/batch.

⑧ 전문 테스트

1 2문자셋: UTF-8
3코덱인터페이스ID값(JSON)방향HEADER(JSON)MESSAGE(JSON)
4
1
채널 — 처음 열면 MCI가 기본 선택돼요.
2
채널 문자셋 표시.
3
입력 영역 — 라벨이 줄바꿈되지 않도록 2열 그리드로 정렬했어요. 값(JSON)은 필드명:값, HEADER/MESSAGE는 표준전문 공통부 덮어쓰기(설정 기본값보다 우선).
4
전문 송수신 — 인코딩된 바이트, 응답, 디코딩 결과를 보여줘요. loopback이면 대외 연결 없이 테스트돼요.

상세 레퍼런스(docs/DEVTOOLS-GUIDE.md 전문)

위 화면 설명과 같은 내용을 정본 수준으로 펼쳐 볼 수 있어요. 섹션을 눌러 펼치세요.

대상: 프레임워크를 쓰는 업무 개발자. 화면을 위→아래 순서로 따라가며 단계별·필드별로 설명하고, 같은 기능의 REST API 를 §12 에 정리한다. 설정 키는 ongo.framework.devtools.*(→ CUSTOMIZATION.md §14), 확장 코드는 CUSTOMIZATION-EXAMPLES.md §D. 기준 코드: 화면 ongo-framework-devtools/src/main/resources/ongo/devtools/index.html, API DevtoolsController·DynamicTelegramController(devtools), BatchDevtoolsController(batch). 예시 값(카드번호·주문번호 등)은 모두 임의 placeholder 다.

목차 0. 접속·켜는 조건 · 1. 전체 흐름 · 2. 공통 I/O 정의서 카드(유형·옵션·생성) · 3. 샘플 JSON → 정의서 · 4. 기존 VO·엑셀·유형별 본문 파싱 · 5. JSON 정의서 ↔ 펼친 표(양방향) · 6. 암호화/마스킹 · 7. 생성 결과 편집·저장 · 8. 업무단위 C/S/R + 메소드 폼 · 9. 타입 색인 · 10. 배치 스켈레톤 · 11. 전문 테스트(실송신/시뮬레이션) · 12. REST API · 13. 모바일 UX · 14. 트러블슈팅


0. 접속·켜는 조건
환경 URL 비고
로컬(데모) ./gradlew :ongo-framework-sc-demo:bootRun → http://localhost:8080/framework-devtools/ 삼카 확장 포함(데모가 samsung-card 의존)
로컬(일반 데모) 삼카 비의존 — 코어 유형만
로컬(custom 프로필) --args='--spring.profiles.active=custom' sample-csv 유형·sample-layout 데코레이터 추가
개발계(일반) https://devfw21.ongosoft.com/framework-devtools/ 쓰기 금지 → zip 다운로드 사용
개발계(삼카) https://devfw21-sc.ongosoft.com/framework-devtools/ 삼카 유형·표준전문 코덱 포함
배치 실행 화면 /framework-devtools/batch ongo-framework-batch 의존 앱에서만

켜는 조건(모두 yml):

ongo:
  framework:
    devtools:
      enabled: true                 # 기본 true
      profiles: [local, dev]        # 이 중 하나가 활성일 때만 화면·API 노출 (prod 에는 아무 것도 없음)
      codegen:
        profiles: [local]           # "소스에 쓰기"가 의미 있는 프로필. dev 서버는 전문 테스트·미리보기·zip 위주
        output-dir: src/main/java
        write-enabled: true         # false 면 쓰기 요청 403 devtools.codegen.write-disabled
  • 화면은 정적 리소스가 아니라 컨트롤러(GET /framework-devtools)가 내준다. 프로필이 안 맞으면 404 이다(보안상 정상).
  • 공통 I/O 정의서 카드 상단(입력 유형 옆) 정보줄에 데코레이터: … · 출력: … · 쓰기 허용/금지 가 표시된다(GET /api 결과). 쓰기 버튼을 누르기 전에 먼저 확인한다.
1. 전체 흐름

화면은 hash 탭 4개: #vo VO/DTO 생성 · #csr 업무단위 Controller·Service·Repository · #batch 배치 잡 스켈레톤 · #tg 전문 테스트. 공통 I/O 정의서 카드는 #vo에서 편집하고(숨겨도 내용은 유지) #tg 전문 테스트가 그 정의서를 그대로 쓴다. 생성 미리보기(#files)와 하단 로그(pre#out)는 탭마다 따로 둔다. 미리보기·로그는 그 작업을 시작한 탭에만 붙고, 다른 탭으로 갔다가 돌아오면 그 탭에서 보던 파일(고친 경로·본문 포함)과 로그만 다시 보인다. 전문 테스트 탭에는 VO/DTO 탭에서 만든 소스가 남지 않는다. [이 탭 로그 지우기]는 보고 있는 탭의 로그만 비우고 미리보기는 그대로다.

[입력]  샘플 JSON │ 기존 VO(FQCN) │ 정의서 본문(유형별) │ 엑셀(.xlsx)
          └──────────────► JSON 정의서(공통 I/O) ◄──► 펼친 항목 표 (양방향 동기화)
                                     │  암호화/마스킹 체크·정책명, 스웨거 on/off
                    ┌────────────────┼──────────────────────┐
                    ▼                ▼                      ▼
             [미리보기] VO 생성   [전문 송수신] 동적 VO     (업무단위 C/S/R / 배치 는 별도 입력)
                    ▼
        파일별 경로·내용 편집 → [수정본 소스에 쓰기] / [zip 다운로드]

원칙: 생성·전문 테스트는 항상 "공통 I/O 정의서" 카드 안의 "JSON 정의서" 칸 본문 + 같은 카드 상단의 입력 유형 으로 동작한다. 표는 그 본문을 편하게 고치는 편집기일 뿐이다.

화면 배치 이후): 예전 최상단 툴바(유형·덮어쓰기·스웨거·미리보기/소스에 쓰기)는 없어졌고, 모두 강조 테두리(.speccard)의 "공통 I/O 정의서 — 입력 유형 선택 → 불러오기·펼침·편집 → 생성" 카드 하나로 합쳐졌다.

┌ 공통 I/O 정의서 카드 ───────────────────────────────┐
│ 입력 유형 [select]  · 정보줄(데코레이터/출력/쓰기 허용) │
│ ① 샘플 JSON 칸(유형=샘플 JSON 입력일 때)              │
│ 기존 VO · 엑셀 · JSON 정의서 위 [본문 파싱]           │
│ JSON 정의서 칸 ↔ 펼친 항목 표                         │
│ 정의서로 코드 생성: □덮어쓰기 □스웨거 [미리보기][소스에 쓰기] │
└──────────────────────────────────────────────────────┘
업무단위 C/S/R 카드 · 배치 카드 · 전문 테스트 카드(정의서·유형 = 위 카드)
2. 공통 I/O 정의서 카드 — 유형·옵션·생성
요소 설명 주의
입력 유형 드롭다운(카드 상단) 정의서 파서(SpecParseService.type()) 선택. 맨 위 샘플 JSON 입력 은 가상 유형(샘플 칸 표시용) 처음 로드 시 json 이 기본 선택
덮어쓰기(카드 하단 "정의서로 코드 생성") 같은 경로 파일이 있으면 덮어씀. 끄면 SKIPPED_EXISTS 로 건너뜀 업무단위·배치·[수정본 저장]에도 같은 체크가 적용
스웨거(OpenAPI) 주석(카드 하단) 전체 on/off. 켜면 OpenApiCodeGenDecorator 가 클래스·필드에 @Schema(description=…) 부여 필드별 on/off 는 없다. 설명(description/label)이 비면 주석도 비어 있음
미리보기(카드 하단) POST /api/generate/{type}?write=false — 디스크 미변경 결과는 §7 편집 영역에 펼쳐짐
소스에 쓰기(카드 하단) write=true — output-dir 아래에 기록 write-enabled=false 면 403. 출력 루트 밖 경로·.. 는 거부

덮어쓰기·스웨거·미리보기/소스에 쓰기는 이 카드의 DTO/VO 생성 전용이다. 업무단위 C/S/R·배치는 각 카드의 별도 미리보기/쓰기를 쓴다(덮어쓰기 체크는 공유). 폭 700px 이하에서는 카드 안 체크박스는 한 줄씩, 버튼은 전체 폭으로 쌓인다.

2.1 정의서 유형

유형 ID 모듈 입력 형태 화면 흐름
sample-json (화면 전용) 실제 요청/응답 데이터 §3 — 샘플 칸에서 파싱 → json 으로 전환
json 코어 JSON 정의서(§5) 본문 직접 편집 ↔ 표. 기본 유형
excel 코어 코어 기본 양식 .xlsx §4.2 — [기본 양식(xlsx)] 으로 템플릿 받기
sc-excel 삼카 삼카 인터페이스정의서(다중 시트, IN/OUT 시트 짝) §4.2 + IN/OUT 시트 패턴 지정
samsung-card-snippet 삼카 레거시 코드조각(@ChannelAnnotation 필드 / WebtUtil.getString(buf,off,len)) §4.3 — 본문 붙여넣기 → [본문 파싱]
samsung-card-io 삼카 | 구분 텍스트(placeholder 양식) §4.3
sample-csv 데모 custom CSV(첫 줄 이름,kind,패키지) 커스텀 파서 예제

generic/삼카 분기: 화면은 /api 의 decorators·specTypes 를 보고 samsung-card 데코레이터가 있을 때만 sc-excel·삼성 유형 안내와 배치 「삼카」 체크를 보여 준다. 일반 데모(ongo-framework-generic-demo)에서는 코어 유형(json·excel·sample-json)만 보인다. API 경로는 같다.

kind(DTO/VO): 「정의서로 코드 생성」의 kind 칸에서 DTO/VO를 고르거나 직접 입력(codegen.suffixes 키)하면 정의서 JSON의 kind(배열이면 문서마다)가 바뀐다. 삼카 sc-excel은 이 칸을 숨기고 기본 VO로 두며, 클래스명 In/Out 등은 vo-naming 의 prefix·in-suffix·out-suffix(예 InVo/OutVo)로 맞춘다.

유형별 "어떤 입력칸을 쓰는가":

정의서 종류 쓰는 입력칸 버튼
공통 I/O(JSON) JSON 정의서 칸 (자동 동기화) → 미리보기
샘플 데이터 샘플 칸(유형=샘플 JSON 입력 일 때만 보임) [샘플 파싱 → 정의서 생성]
기존 VO 라벨 기존 VO + FQCN [불러오기]
엑셀 계열 파일 선택 + (선택) 매핑 JSON [시트·컬럼 분석] → [엑셀 펼치기]
텍스트 계열(snippet/io/csv) JSON 정의서 칸에 원문 그대로 본문 파싱
업무단위 C/S/R "업무단위" fieldset 미리보기/쓰기(§8)
배치 "배치" fieldset 스켈레톤/프로젝트 미리보기(§10)
전문 정의서 칸 + 전문 테스트 fieldset 전문 송수신
3. 샘플 요청/응답 JSON → JSON 정의서
  1. 유형에서 샘플 JSON 입력 선택 → "① 샘플 요청/응답 JSON" 칸이 나타난다(다른 유형일 때는 숨김).
  2. 실제 데이터(또는 그 모양의 임의 값)를 붙여 넣는다. 요청/응답은 in/out 키로 구분: json {"in":{"cardNo":"1234567812345678","amount":10000}, "out":{"resultCode":"0000","items":[{"seq":1,"name":"a"}]}} 구분 없이 주면 전체가 하나의 VO: {"cardNo":"1234567812345678"}
  3. 이름(CARD01 — 클래스명·인터페이스ID 기준) 입력 → [샘플 파싱 → 정의서 생성] (POST /api/import/sample-json?name=CARD01).
  4. 성공: JSON 정의서·표가 채워지고 샘플 칸은 비워지며 안내 ✔ 정의서 생성 — 샘플은 비웠어요. 이후 편집은 정의서/표에서 한다(샘플은 일회성).
  5. 실패: 브라우저에서 먼저 JSON.parse 검사 → ⚠ JSON 오류: …. 서버 오류는 VO/DTO 탭 하단 로그(pre#out)에 응답 전체.

타입 추론 규칙:

샘플 값 추론 type 보정 포인트
int 범위 정수 INT 금액이면 LONG/DECIMAL 로
큰 정수 LONG
소수 DECIMAL length/scale 지정
true/false BOOLEAN
문자열 STRING 숫자처럼 보이는 문자열(카드번호)은 STRING 유지, length 필수 지정
객체 OBJECT + 하위 fields
배열 ARRAY(첫 원소 기준) 원소가 섞여 있으면 첫 원소 모양만 반영

추론값은 기본값일 뿐이다. 길이·필수·설명·필드명(한글)은 표에서 반드시 채운다.

4. 기존 VO · 엑셀 · 유형별 본문 파싱

4.1 기존 VO 불러오기

  • 라벨 기존 VO (어노테이션/POJO, className)(선택지가 하나라 셀렉트가 아닌 고정 라벨) + FQCN(com.example.card.CARD01In) → [불러오기] = POST /api/import/vo?className=….
  • 앱 클래스패스에서 로드한다. @ScField(order·length), @Encrypted/@Masked, POJO 필드·레코드 컴포넌트를 읽어 정의서로 만든다.
  • 없는 클래스: 404 devtools.import.class-not-found.

4.2 엑셀

  1. 파일 선택(.xlsx). 유형이 excel/sc-excel 가 아니면 자동으로 excel 로 처리한다.
  2. [시트·컬럼 분석(매핑 제안)] (POST /api/excel/inspect/{type}) → 시트별 헤더 행·헤더 목록·자동 매핑 제안이 드롭다운으로 표시된다.
  3. 드롭다운으로 필드 키(name·label·type·length·scale·required·description·encrypted·masking·io·level 등)↔엑셀 컬럼을 확정하면 매핑 JSON 칸이 자동 갱신된다. - IN 시트/OUT 시트 패턴(예 *_IN/*_OUT)과 기본 타입(예 STRING)도 지정 가능.
  4. [엑셀 펼치기] → 정의서·표. (업로드한 .xlsx 를 읽는다. JSON 정의서 칸 본문을 읽는 [본문 파싱]과 구분)

매핑 JSON 직접 입력 예(컬럼은 헤더명 또는 열 문자):

{"headerRow":1,"columns":{"name":"항목명","label":"한글명","length":"D","encrypted":"암호화여부","masking":"마스킹여부"},
 "ioSheets":{"in":"*_IN","out":"*_OUT"},"defaults":{"type":"STRING"}}

코어 기본 양식 헤더: 항목명·한글명·타입·길이·소수·필수·설명·암호화여부·마스킹여부·구분(IN/OUT)·레벨. 레벨 2 이상은 바로 위 OBJECT/ARRAY 의 하위 필드.

4.3 텍스트 계열 정의서(삼카 snippet·io, CSV 등)

  1. 공통 I/O 정의서 카드 상단 입력 유형을 고른다(예 samsung-card-snippet).
  2. JSON 정의서 칸에 원문을 그대로 붙여 넣는다(JSON 아니어도 된다 — 이때 표 동기화는 ⚠ JSON 오류 를 띄우지만 무시).
  3. [본문 파싱] (POST /api/import/{type}) → 공통 I/O JSON 으로 바뀌어 칸이 덮어써지고 표가 펼쳐진다. 이후 유형은 json 으로 다뤄진다.
#interface|CARD01|Card01In|DTO|com.example.card            ← samsung-card-io (placeholder 양식)
cardNo|String|16|Y|카드번호
#interface|CARD01|Card01Out|DTO|com.example.card|RESPONSE
resultCode|String|4|Y|결과코드
// #interface|CARD01|Card01Out|DTO|com.example.card|RESPONSE   ← samsung-card-snippet
@ChannelAnnotation(order = 1, dataType = IIMSDataType.NUMERIC, length = 8, scale = 0) private long msgLength;
// 또는 오프셋 패턴(블록당 한 패턴만): sysCode = WebtUtil.getString(byteArray, 8, 4);

snippet 규칙: (A) 어노테이션 패턴은 order 정렬, NUMERIC → LONG/DECIMAL. (B) 오프셋 패턴은 offset 정렬, 빈틈·겹침은 오류, parseInt/parseLong → LONG. ARRAY(그리드)는 아직 미지원(오류).

새 유형 추가: SpecParseService 빈 등록 → 드롭다운에 자동 노출(EXAMPLES §D1).

5. JSON 정의서 ↔ 펼친 항목 표 (양방향 동기화)
{"name":"Card01","kind":"DTO","package":"com.example.card","description":"카드 조회",
 "in":{"fields":[{"name":"cardNo","label":"카드번호","type":"STRING","length":16,"required":true,"encrypted":"card","masking":"card"}]},
 "out":{"fields":[{"name":"items","type":"ARRAY","fields":[{"name":"seq","type":"LONG"},{"name":"amount","type":"DECIMAL","length":15,"scale":2}]}]}}
속성 의미 / 생성 결과
name(정의서) 클래스 기본명. in/out 이 있으면 {name}In/{name}Out(접미사 규칙)
kind DTO/VO/… → codegen.suffixes 로 접미사
package 비면 codegen.default-package
필드 name Java 식별자 검사(위반 시 400 devtools.codegen.invalid)
type STRING·INT·LONG·DECIMAL·NUM·BOOLEAN·DATE·DATETIME·OBJECT·ARRAY 또는 FQCN
length/scale 문자열 length → @Size(max)(validation 데코레이터), 전문 레이아웃 길이
required @NotNull
label/description Javadoc·@Schema 설명. 표에서 "필드명"/"설명" 칸
encrypted/masking §6
fields OBJECT/ARRAY 하위(표에서 · 들여쓰기로 표시)
attributes 고객사 확장 자유 속성(interfaceId, offset 등) — 데코레이터가 읽음

동기화 규칙(코드 기준): - 정의서 → 표: 입력 후 300ms 디바운스로 JSON.parse → 표 재구성, ✔ 정의서 → 표 반영. JSON 이 깨지면 ⚠ JSON 오류(표는 마지막 정상값 유지) — 표는 바뀌지 않는다. - 표 → 정의서: 필드명·설명·암호·마스킹 칸을 고치면(change) 즉시 정의서 전체를 다시 직렬화하고 유형을 json 으로 전환, ✔ 표 → 정의서 반영. - 표에서 고칠 수 있는 것: 필드명(label)·설명·암호·마스킹. name·type·length·구조(추가/삭제)는 정의서에서 고친다. - 값을 비우면 그 속성은 삭제된다(빈 문자열로 남지 않음). - 정의서를 표에서 고친 뒤 원문(텍스트 유형)으로 되돌릴 수는 없다 — 원문이 필요하면 따로 보관.

6. 암호화·마스킹 체크와 정책 입력
표 조작 정의서 결과 생성 코드
체크만 "encrypted": true @Encrypted
옆 칸에 card 입력(체크 자동) "encrypted": "card" @Encrypted("card")
체크 해제 속성 삭제 없음
마스킹 동일 "masking": true / "규칙" @Masked / @Masked("규칙")
  • 정책·규칙명은 ColumnAttributeService.ColumnMeta.encrypted()/masking() 문자열로 그대로 전달된다 → 정책별 분기 구현은 EXAMPLES §E4.
  • 런타임 동작: DB VO 의 @Encrypted 컬럼은 MyBatis 저장 시 암호화·조회 시 복호화, 응답 DTO 의 @Masked 는 응답 직렬화 시 마스킹, 전문 코덱은 encode 전/decode 후 ColumnAttributeService 훅(CUSTOMIZATION §14.3).
  • 엑셀 암호화여부/마스킹여부 컬럼 값 Y → true, 그 외 문자열 → 정책명.
7. 생성 결과 편집·저장

미리보기 후 파일마다 경로 입력칸 + 내용 textarea 가 펼쳐진다. 목록은 그 생성을 시작한 탭에만 붙는다. 다른 탭으로 옮기면 그 탭의 파일만 보이고, 돌아왔을 때 고친 경로·내용이 남아 있다. [이 탭 로그 지우기]는 이 목록을 지우지 않는다. - 경로·내용을 직접 고칠 수 있다(패키지 이동 시 경로와 package 선언을 함께 고칠 것). - [수정본 소스에 쓰기] POST /api/files/save?overwrite= — output-dir 기준 상대경로만. 절대경로·C:·.. 는 400. - [zip 다운로드] POST /api/files/download → generated-sources.zip. 서버에 쓰지 않으므로 개발계(쓰기 금지)에서도 동작. - 결과(쓰기 결과 WRITTEN/OVERWRITTEN/SKIPPED_EXISTS 등)는 그 작업을 시작한 탭의 하단 로그에 JSON 으로 남는다. 다른 탭 로그와 섞이지 않는다.

8. 업무단위 Controller + Service + Repository 생성
필드 예 규칙
업무 루트 패키지 com.example.order 하위 패키지는 codegen.layer.<유형>.package(기본 controller/service/repository)
업무명 Order 클래스명 = prefix + 업무명 + suffix → OrderController·OrderService·OrderRepository
설명 주문 Javadoc·스웨거 태그, 기본 메소드 설명
경로 /api/order 컨트롤러 @RequestMapping
스웨거 체크 @Tag/@Operation 부여(정의서 카드 하단 스웨거와 별개)

현재 규칙은 화면 하단 작은 글씨(GET /api/generate-layer/rules)로 표시된다.

8.1 메소드 폼

행 하나 = 메소드 하나: 메소드명 · HTTP(GET/POST/PUT/PATCH/DELETE) · URI · in 타입 · out 타입 · 설명 · [✕]

  1. [+ 메소드 추가] 로 행 추가, [✕] 로 삭제.
  2. 메소드명이 빈 행은 무시. 유효한 행이 하나도 없으면 기본 list(GET "")·get(GET /{id})·create(POST "") 생성 — 자세한 규칙은 §8.2.
  3. URI 를 비우면 베이스 경로 그대로. {orderId} 는 @PathVariable String orderId 로.
  4. in 타입: GET/DELETE 는 쿼리 파라미터 객체(@ModelAttribute), POST/PUT/PATCH 는 Content-Type 선택에 따름 — JSON(기본) @RequestBody, Form @ModelAttribute(DTO 없으면 @RequestParam Map), Multipart @RequestPart("body"). 본문 메소드에는 consumes = MediaType.… 가 붙음. out 타입: 반환형(List<OrderDto> 가능). - 추가 파라미터(쉼표 구분, 여러 개): param page:Integer, header X-User-Id, path id:Long, part file → @RequestParam/@RequestHeader/@PathVariable(URI 변수 타입 지정)/@RequestPart MultipartFile. 타입 생략 시 String(part 는 MultipartFile). Service 시그니처에도 같은 순서로 전달. - 한 줄 입력에서는 ct:json|form|multipart 토큰으로 Content-Type 지정(JSON 기본이면 생략), 추가 파라미터는 공백 없이 param:page:Integer header:X-User-Id path:id:Long part:file 토큰으로.
  5. in/out 칸은 입력 즉시 자동완성 후보(datalist)가 뜬다. 행을 한 번 클릭하면 "선택 행"이 된다 → 아래 타입 찾기 검색 후 [선택 행 in에 넣기]/[선택 행 out에 넣기].
  6. 한 줄 텍스트 ↔ 폼 양방향: 폼 행을 입력·수정·삭제하면 "한 줄 텍스트" 칸이 실시간으로 같은 내용으로 변환된다(Content-Type·추가 파라미터 포함, 복사해 공유·재사용 가능). 반대로 텍스트를 고치거나 붙여넣고 [폼으로 가져오기(폼 교체)] 를 누르면 폼 행이 텍스트 내용으로 교체된다: cancel PUT /{orderId}/cancel in:OrderCancelDto out:OrderDto 주문 취소 search GET /search in:OrderSearchDto out:List<OrderDto> 주문 검색 ping GET - 상태 확인 형식: 이름 HTTP URI(없으면 -) [in:타입] [out:타입] 설명….
  7. [미리보기] → 파일 3개(+경고). [쓰기] 는 경고가 하나라도 있으면 400 으로 중단(타입 확인 필요(쓰기 중단)).

타입 규칙: in/out 은 프로젝트에 이미 있는 클래스만(단순명·FQCN·제네릭). 없거나 단순명이 둘 이상 겹치면 ⚠ 타입 경고(생성 안 함) — 그 메소드는 빠진다. 먼저 VO 를 생성·저장하고 §9 재색인.

8.2 메소드를 하나도 정의하지 않으면 — 기본 뼈대(list·get·create)

메소드 폼에 유효한 메소드가 하나도 없으면 아래 세 API 가 자동으로 생성된다. 버그가 아니라 의도된 기본 뼈대(와꾸) 다.

메소드 HTTP URI(베이스 경로 기준) 컨트롤러 반환형 Service 본문 설명(Javadoc·@Operation)
list GET ""(베이스 경로 그대로) List<Map<String, Object>> return repository.findAll(); {설명} 목록
get GET /{id} (@PathVariable String id) Map<String, Object> return repository.findById(id); {설명} 단건
create POST "" (@RequestBody Map<String, Object> body) Map<String, Object> @Transactional + return repository.insert(body); {설명} 등록
  • {설명} 은 상단 설명 칸 값, 비우면 업무명(예 Order).
  • 경로 칸을 비우면 베이스 경로는 /api/{업무명 kebab}(예 OrderItem → /api/order-item).
  • 예) 업무명 Order, 경로 /api/order → GET /api/order, GET /api/order/{id}, POST /api/order.

"유효한 메소드가 없다"의 정확한 기준 1. 화면: 메소드 행이 0개이거나, 있더라도 메소드명 칸이 전부 비어 있으면 화면이 그 행들을 버리고(methodRows() 가 name 빈 행 제외) 빈 목록을 보낸다 → 서버가 기본 3개를 붙인다. 2. 서버(LayerCodeGenerator.generate): methods 가 null 이거나 빈 배열이면 defaultMethods(설명) 사용. 3. REST 로 직접 호출할 때(§12)는 화면 필터가 없으므로 methods 에 이름이 빈 항목을 넣으면 400(메소드명 오류/중복). 기본 뼈대를 원하면 methods 를 아예 빼거나 [] 로 보낼 것.

하나라도 정의하면 기본 3개는 붙지 않는다 - 메소드명이 채워진 행이 1개라도 있으면 그 행들만 생성된다(기본 list/get/create 와 합쳐지지 않음). - 기본 3개에 하나를 더하고 싶다면 list·get·create 행도 직접 넣는다. 한 줄 붙여넣기 예: list GET - 주문 목록 get GET /{id} 주문 단건 create POST - 주문 등록 cancel PUT /{orderId}/cancel in:OrderCancelDto out:OrderDto 주문 취소 - 직접 넣은 행이라도 이름이 list/get/create 이고 in/out 타입을 비우면 Service 가 위 표처럼 Repository 를 호출한다. in/out 타입을 지정하면 Service 본문은 // TODO + return null;. - in/out 타입 경고가 난 메소드는 빠지고, 경고가 하나라도 있으면 [쓰기]는 400 으로 중단(§8.1 7번, 타입 규칙) — 기존 규칙 그대로.

왜 기본 뼈대를 주나 — 생산성 - 메소드를 정하지 않았어도 Controller / Service / Repository 3 클래스, 패키지, import, 생성자 주입(DI), @RestController·@Service·@Repository·@Transactional 구조가 한 번에 완성된다. - 당장 쓰지 않는 API 라도, 나중에 메소드명·URI·타입만 바꾸거나 지우면 되므로 처음부터 타이핑할 필요가 없다. - Repository 는 findAll/findById/insert 를 빈 값으로 반환하는 임시 구현(TODO: MyBatis Mapper·JPA 등으로 교체)이다. 실제 저장소로 교체할 것. - 필요 없는 기본 API 는 생성 후 지우면 된다. 운영 전 남겨 둔 list/get/create 가 의도치 않게 노출되지 않는지 확인할 것.

미리보기 · 쓰기 · 수정본 저장 (§7 과 동일) - [미리보기]: 서버에 쓰지 않고 3개 파일(+경고)만 보여 준다. 기본 뼈대 여부를 여기서 먼저 확인. - [쓰기]: 현재 폼 값으로 다시 생성해서 디스크에 쓴다. 미리보기 textarea 에서 고친 내용은 반영되지 않는다. - 미리보기에서 직접 고친 내용은 §7 [수정본 소스에 쓰기](또는 [zip 다운로드])으로만 저장된다. 개발계(쓰기 금지)는 zip 만 가능.

9. 타입 색인 · 재색인
  • 색인 대상: @SpringBootApplication 패키지 + codegen.type-packages 의 클래스패스 .class + output-dir 의 .java 소스. 내부 클래스($) 제외. java.lang·java.util·java.time·BigDecimal 등 내장 타입은 항상 인식.
  • 첫 조회 때 한 번 스캔·메모리 캐시 → 이후 검색은 메모리 필터. 입력 250ms 디바운스, 최대 30건(API 는 limit 최대 200).
  • 입력할 때마다 resolve 로 검증: ✔ import FQCN 또는 ⚠ 경고.
  • [재색인] = refresh=true — 캐시를 비우고 다시 스캔. 방금 저장한 클래스를 후보에 넣을 때.
  • 전문 테스트의 동적 VO(런타임 컴파일)는 색인 대상이 아니다(디스크/클래스패스 파일 필요). 존재 확인만 클래스로더 리소스도 본다.
  • 클래스가 많아 첫 색인이 느리면 type-packages 를 업무 루트로 좁힌다.
ongo.framework.devtools.codegen:
  type-packages: [com.example.order, com.example.common]
  layer:
    controller: { package: web,        suffix: Controller }
    service:    { package: service,    suffix: Service }
    repository: { package: repository, prefix: "", suffix: Mapper }
10. 배치 잡 스켈레톤 · 배치 프로젝트
필드 예 설명
이름 cardLimitSync 영문 시작 영숫자. → CardLimitSyncJobConfig, 잡 cardLimitSyncJob
패키지 com.example.batch
chunk 100 커밋 단위
Reader list / custom list = ListItemReader 예제, custom = 직접 구현 자리(TODO)
item 타입 String Reader→Writer 항목 타입
전문 채널 / 인터페이스ID mci / SCDEMO01 둘 다 채우면 Writer 에 ChannelService.send(채널, ID, 값) 호출 코드
  • 생성 스텝은 BatchFailurePolicyService.apply(...) 로 skip/retry 정책을 적용한다(정책 교체: EXAMPLES §B).
  • [스켈레톤 미리보기] POST /api/generate-batch — 잡 설정 클래스 1개.
  • [프로젝트 미리보기] POST /api/generate-batch-project — Gradle 단일 모듈(build.gradle·settings.gradle·application.yml·Application·잡). 프로젝트명·프레임워크 버전(0.1.0-SNAPSHOT)·Nexus URL(비우면 OnGoSoft 기본), 삼카 체크(삼카 데코레이터가 있을 때만 표시) 시 ongo-framework-samsung-card 의존 추가. zip 으로 받아 바로 빌드.
  • 참고 구현: ongo-framework-batch-demo/.../job/ScCardLimitSyncJobConfig.java(형식 오류 카드번호 skip, 삼카 레이아웃·loopback 전송).
  • 수동 실행: /framework-devtools/batch 화면 또는 POST /framework-devtools/api/batch/jobs/{name}/run(batch.launch.allowed-jobs 에 있는 잡만).
11. 전문 테스트 (동적 VO → encode → 송신 → decode)

정의서로 VO 를 즉석 컴파일·로드해 전문 바이트를 만들고 송신·응답을 파싱한다(코드생성 없이 레이아웃 검증).

송신 방식 - 시뮬레이션(기본): loopback — 서버 밖으로 나가지 않고 보낸 전문을 그대로 응답으로 받는다. MCI / EAI / FEP 버튼은 시뮬레이션에서도 그 채널 이름과 yml 문자셋(MCI·EAI UTF-8, FEP EUC-KR)으로 전문을 만든다. 누른 버튼은 밝은 배경·굵은 글씨·체크 표시로 남고, 다른 채널을 누르면 표시가 옮겨진다. 클릭과 키보드(Enter/Space) 동작은 같다. - 실송신: yml ongo.framework.channel.channels.<name> 의 transport·url·timeout 으로 실제 채널에 송신(데모 ChannelService 와 같은 엔드포인트). 채널 목록은 GET /api/dynamic/channels(문자셋 포함). 문자셋은 화면에서 고르지 않는다. channel.charset · charset-defaults · channels.<이름>.charset 을 바꾸고 서버를 재기동한다. - 안전장치: devtools 자체가 devtools.profiles(기본 local·dev)에서만 켜짐. 추가로 ongo.framework.devtools.dynamic.real-send-enabled=false 면 실송신 금지, real-send-channels: [mci-dev] 로 허용 채널 제한(비우면 전체).

  1. 공통 I/O 정의서 카드의 정의서 칸 본문 + 입력 유형(전문 테스트 카드에 "정의서·유형 = 위 공통 I/O 정의서 카드" 로 표시)(텍스트 유형이면 원문 그대로도 가능).
  2. 코덱 선택(GET /api/dynamic/codecs): 코어 고정길이(기본) / 삼카 표준전문(standard-telegram.enabled=true 일 때) 등 DynamicCodecOption 빈.
  3. 인터페이스ID(생략 시 첫 VO).
  4. 값(JSON): {"cardNo":"1234567812345678","amount":10000} — 없는 키는 공백/0 채움.
  5. 방향: 송신(요청 HEADER) / 수신 응답(타 시스템 요청에 대한 회신, @direction=response). 실송신 채널이 mci/eai 면 UTF-8, fep 면 EUC-KR(channel.charset-defaults, 채널별 channels.<이름>.charset 로 변경). 예: 채널 mci + HEADER {"rcvSysDvC":"MCI"} / 채널 eai + {"rcvSysDvC":"EAI"} / 채널 fep + {"rcvSysDvC":"FEP"}.
  6. 표준전문 코덱이면 HEADER·MESSAGE JSON 으로 헤더값 지정(설정 기본값보다 우선) → 내부적으로 header.이름/message.이름 키: {"sendSysDvC":"ONG","akRspDvC":"S"} / {"stdMsgPrntTpc":"1"} 값이 null/"" 면 설정·내장 기본값 유지, " "(length>0)는 공백 그대로. 우선순위 HEADER·MESSAGE 입력 > 업무단 ScTelegramContext > (수신 응답) 요청 HEADER 에코 > yml 기본값 > 내장 기본값 — 상세·수신 처리기 응답 맵 예시는 CUSTOMIZATION 「표준전문」절.
  7. [전문 송수신] → 결과: VO 목록(클래스·방향·총 길이·필드별 offset/length/type·existing=기존 VO 사용 여부), 교환 결과(요청/응답 문자열·바이트 수·디코드 값). 이 출력은 전문 테스트 탭 로그에만 남는다. VO/DTO·업무단위·배치 탭으로 이동하면 그 탭의 로그가 보이고, 다시 전문 테스트 탭으로 오면 이전 전문 로그가 있다. [이 탭 로그 지우기]는 현재 탭만 비운다.

sc-excel: 인터페이스ID는 원형(APC_M_ISS_00027)으로 attributes.interfaceId(@ScTelegram·전문 테스트 칸)에, VO 이름은 카멜(ApcMIss00027)+suffix. 소스/타겟 IO처럼 같은 클래스명의 정의가 여러 벌이면 [엑셀 펼치기] 후 「생성에 쓸 I/O」(attributes.ioRole=시트명)에서 하나를 골라 미리보기·소스에 쓰기.

VO 선택 규칙: prefer-existing-vo=true 면 정의서 FQCN 또는 vo-naming 규칙(기본 {package}.{prefix}{interfaceIdCamel}{suffix} — 예 APC_M_ISS_00027 → ApcMIss00027In/Out, prefix·in/out-suffix 설정, 생성 클래스명도 동일) 클래스가 있으면 그대로 사용, 없으면 동적 생성. 작업 디렉터리 devtools.dynamic.work-dir(기본 build/ongo-devtools/dynamic, 개발 서버는 쓰기 가능한 경로를 환경변수로), fat jar 실행 시 dynamic.extra-classpath 보강.

12. REST API (자동화·CI 용)

기본 경로 /framework-devtools. JSON 응답은 프레임워크 공통 응답 envelope 로 감싸진다, 단 xlsx·zip 은 @NoEnvelope.

메서드 경로 요청 응답 요약
GET /framework-devtools — 화면 HTML
GET /api — specTypes[{type,description}], decorators[], outputDir, writeEnabled
POST /api/parse/{type} 본문 = 정의서 원문(text) SpecModel[]
POST /api/import/{source} vo?className= · sample-json?name=(본문 샘플) · {유형}(본문 원문) · excel/sc-excel(본문 {"file":"data:…base64","mapping":{…}}) 편집용 공통 I/O JSON
POST /api/generate/{type}?write=&overwrite=&swagger= 정의서 원문 specs, files[{path,content}], written[]
POST /api/generate-layer?write=&overwrite= {"rootPackage","name","description","basePath","swagger","methods":[{"name","http","uri","description","paramType","returnType"}]} files, written, warnings
GET /api/generate-layer/types?q=&limit=30&refresh=false — FQCN 목록
GET /api/generate-layer/resolve?type=List<OrderDto> — {type, imports[], warnings[]}
GET /api/generate-layer/rules — {controller:{packageName,prefix,suffix},…}
POST /api/generate-batch?write=&overwrite= {"name","packageName","chunk","itemType","outputType","reader","channel","interfaceId"} files, written
POST /api/generate-batch-project?write=&overwrite= {"projectName","packageName","frameworkVersion","repositoryUrl","samsungCard","job":{…위와 동일}} files, written
POST /api/excel/inspect/{type} {"file":"data:…"} fieldKeys[], sheets[{name,headerRow,headers,suggested}]
GET /api/excel/template — ongo-spec-template.xlsx
POST /api/files/save?overwrite= {"files":[{path,content}]} 쓰기 결과
POST /api/files/download 동일 generated-sources.zip
GET /api/dynamic/codecs — [{name,label}]
POST /api/dynamic/{type} {"content","interfaceId","values":{},"codec","mode":"simulate|real","channel"} buildId, dir, vos[], exchange(mode·channel·transport 포함)
GET /api/dynamic/channels — 실송신 후보 [{name,type,transport,url}]
GET /api/batch/jobs — [{name,restartable,manualRunAllowed}]
POST /api/batch/jobs/{name}/run 잡 파라미터 맵 실행 정보
GET /api/batch/jobs/{name}/executions?limit= — 최근 실행

오류 코드: 미등록 유형 404 devtools.spec.unknown, 형식 오류 400 devtools.spec.invalid/devtools.codegen.invalid, 쓰기 금지 403 devtools.codegen.write-disabled, VO 없음 404 devtools.import.class-not-found, import 실패 400 devtools.import.invalid.

XSS: 개발툴 경로(/framework-devtools/**)는 XSS 파라미터·JSON 본문 정화를 건너뜁니다(ongo.framework.devtools.xss-skip=false 로 끔). 타입식 List<OrderDto> 가 &lt; 로 이스케이프돼 와도 검증 전에 원문으로 복원합니다.

curl 예(미리보기):

curl -s -X POST 'http://localhost:8080/framework-devtools/api/generate/json?swagger=true' \
  -H 'Content-Type: text/plain' \
  -d '{"name":"SampleOrder","kind":"DTO","package":"com.example.sample","fields":[{"name":"orderNo","type":"STRING","length":12,"required":true}]}'
13. 모바일 UX (폭 720px 이하)
  • 모든 입력·선택·버튼이 한 줄 전체 폭·최소 높이 44px, 글꼴 16px(iOS 확대 방지), safe-area 여백 적용, 가로 스크롤 없음.
  • I/O 표는 카드형: 한 필드 = 카드 한 장, 각 칸 앞에 라벨(I/O·필드·필드명·설명·타입·길이·암호·마스킹). 암호/마스킹 체크박스 26px 로 터치 가능, 정책명 칸이 옆에 붙는다.
  • JSON 정의서 칸은 펼친 표 바로 위에 있어 정의서↔표를 번갈아 보며 고칠 수 있다.
  • 메소드 폼 행은 세로 스택(메소드명→HTTP→URI→in→out→설명→✕).
  • <br> 은 숨겨지고 small 안내문은 블록으로 내려간다.
14. 트러블슈팅
증상 원인 / 조치
화면 404 활성 프로필이 devtools.profiles 에 없음 / devtools.enabled=false
[소스에 쓰기] 403 write-enabled=false(개발계) → [zip 다운로드]
체크했는데 @Encrypted 없음 생성은 정의서 기준. 정의서에 encrypted 가 들어갔는지, 유형이 json 인지 확인
표가 안 바뀜 정의서 JSON 오류(표는 마지막 정상값 유지). specinfo 메시지 확인
텍스트 유형 붙였더니 ⚠ JSON 오류 정상. 유형 선택 후 [본문 파싱]
in/out 타입 경고·쓰기 중단 클래스 없음/모호. VO 먼저 생성·저장 → [재색인] → FQCN 으로 지정
샘플 칸이 안 보임 유형이 샘플 JSON 입력 일 때만 보인다
동적 전문 컴파일 실패(개발 서버) dynamic.work-dir 쓰기 권한, fat jar 면 extra-classpath
표준전문 코덱이 목록에 없음 ongo.framework.samsung-card.standard-telegram.enabled=true 필요
모바일에서 표가 잘림 구버전 캐시 — 새로고침. 현재 버전은 카드형(§13)
화면 HTML 에 JS 코드가 본문으로 보임 인라인 <script> 안 문자열 리터럴에 </script> 가 들어가면 태그가 조기 종료된다. 예시 문자열은 <\/script> 또는 URL 인코딩(%3C%2Fscript%3E)으로 쓸 것(데모 쇼케이스 수정 사례: 97b0f4d)
한글 길이가 안 맞음 전문 길이는 문자셋 바이트 기준(MS949 한글 2바이트, UTF-8 3바이트) — channel.charset 확인

데모 확장·커스텀 카탈로그: 데모의 모든 SPI 교체 예시(DemoCustomizations·DevtoolsDemoCustomizations·DemoExtensionMocks)와 Redis·MQ(Rabbit/Kafka)·ES 목업은 코어 개발가이드 /framework-devtools/guide#demoext·#extmock 및 CUSTOMIZATION-EXAMPLES §M 참고. 코어에는 SPI 계약만 있고 클라이언트 의존성은 없다(일반 공통, 삼카 비의존).

자주 묻는 질문

Q. 소스에 쓰기가 실패해요

ongo.framework.devtools.codegen.profiles에 현재 프로파일(local)이 있는지 확인하세요.

Q. 공통 VO를 여러 전문에서 같이 쓰고 싶어요

ARRAY/OBJECT의 「기존 타입 참조」에 그 클래스를 넣으세요(③-1).

Q. 예전 GRID 정의서는?

그대로 불러오면 ARRAY로 바뀌어요.

Q. 중첩 VO를 한 파일로 받고 싶어요

API 직접 호출 시 POST /api/generate/{type}?separateNested=false.