DJB OpenAPI 편집기 초기 코드 업로드:

- 앱 상태 관리 및 기본 렌더 함수 추가 (vanilla JS)
- OpenAPI 스펙 빌더 초기 구현 (v3.1 대응)
- 스텝별 렌더 함수 및 UI 구축
This commit is contained in:
Rinjae
2026-07-02 18:17:58 +09:00
parent 18489e2440
commit 6c0fca22f3
16 changed files with 3453 additions and 0 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,122 @@
/*!
* djb-swagger-i18n.js
* Swagger UI 영문 라벨을 한글로 치환하는 i18n 패치 (방법 B - DOM patch)
* 배포 경로 권장: static/plugins/swaggerUI/djb-swagger-i18n.js
* index.html 의 swagger-initializer.js 직전 또는 직후에 <script> 로 로드
*
* ?lang=en 쿼리스트링이 있으면 패치 비활성화 (원문 보기)
* 매핑 카탈로그는 01-i18n-mapping/i18n-strings.csv 와 동기 유지
*/
(function (window) {
'use strict';
// ── 비활성화 옵션 ──
if (/[?&]lang=en\b/.test(window.location.search)) return;
// ── 매핑 (key 는 디버깅용, 실제 비교는 EN 문자열 일치) ──
var I18N_MAP = {
// P0 — OAuth2 모달 본문
'auth.modal.title': { en: 'Available authorizations', ko: '사용 가능한 인증' },
'auth.scopes.description': {
en: 'Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes.',
ko: '스코프는 최종 사용자를 대신하여 애플리케이션이 데이터에 접근할 수 있는 권한 수준을 부여합니다. 각 API는 하나 이상의 스코프를 선언할 수 있습니다.'
},
'auth.scopes.swagger_grant': {
en: 'API requires the following scopes. Select which ones you want to grant to Swagger UI.',
ko: '이 API 는 아래 스코프가 필요합니다. Swagger UI 에 부여할 스코프를 선택하세요.'
},
'auth.flow.clientCredentials': { en: 'OAuth2 client credentials flow', ko: 'OAuth2 클라이언트 자격증명 흐름' },
'auth.flow.implicit': { en: 'OAuth2 implicit flow', ko: 'OAuth2 암묵적 흐름' },
'auth.flow.password': { en: 'OAuth2 password flow', ko: 'OAuth2 비밀번호 흐름' },
'auth.flow.authorizationCode': { en: 'OAuth2 authorization code flow', ko: 'OAuth2 인가 코드 흐름' },
'auth.field.tokenUrl': { en: 'Token URL:', ko: '토큰 URL:' },
'auth.field.flow': { en: 'Flow:', ko: '흐름:' },
'auth.field.clientId': { en: 'client_id:', ko: '클라이언트 ID (client_id):' },
'auth.field.clientSecret': { en: 'client_secret:',ko: '클라이언트 시크릿 (client_secret):' },
'auth.field.scopes': { en: 'Scopes:', ko: '스코프:' },
'auth.action.selectAll': { en: 'select all', ko: '모두 선택' },
'auth.action.selectNone': { en: 'select none', ko: '선택 해제' },
'auth.action.authorize': { en: 'Authorize', ko: '인증' },
'auth.action.close': { en: 'Close', ko: '닫기' },
'auth.action.logout': { en: 'Logout', ko: '로그아웃' },
'auth.modal.authorized': { en: 'authorized', ko: '인증됨' },
// P1 — 기타 인증 흐름
'auth.scheme.apiKey': { en: 'API key', ko: 'API 키' },
'auth.field.name': { en: 'name:', ko: '이름:' },
'auth.field.in': { en: 'in:', ko: '위치:' },
'auth.field.value': { en: 'Value:', ko: '값:' },
'basic.field.username': { en: 'Username:',ko: '사용자 ID:' },
'basic.field.password': { en: 'Password:',ko: '비밀번호:' }
// P2 (오퍼레이션/응답) 는 필요 시 추가
};
// 빠른 lookup 을 위해 EN → KO 인덱스 사전 생성
var EN_TO_KO = Object.create(null);
Object.keys(I18N_MAP).forEach(function (k) {
var e = I18N_MAP[k];
EN_TO_KO[e.en] = e.ko;
});
// ── 치환 대상 노드 식별 + 치환 ──
function translateNode(node) {
if (!node) return;
if (node.nodeType === Node.TEXT_NODE) {
var raw = node.nodeValue;
if (!raw) return;
var trimmed = raw.trim();
if (!trimmed) return;
// 1) 완전 일치 (가장 안전)
if (EN_TO_KO[trimmed]) {
node.nodeValue = raw.replace(trimmed, EN_TO_KO[trimmed]);
return;
}
// 2) 라벨 colon 패턴: "Token URL:" 같이 끝이 ":" 인 짧은 라벨
// (불필요 — 1번 완전 일치로 대부분 커버)
return;
}
if (node.nodeType !== Node.ELEMENT_NODE) return;
if (node.tagName === 'SCRIPT' || node.tagName === 'STYLE') return;
// input placeholder / button title 도 일부 케이스에 대비
if (node.tagName === 'INPUT' && node.placeholder && EN_TO_KO[node.placeholder.trim()]) {
node.placeholder = EN_TO_KO[node.placeholder.trim()];
}
var i, child = node.childNodes, len = child.length;
for (i = 0; i < len; i++) translateNode(child[i]);
}
function translateAll() {
var root = document.getElementById('swagger-ui') || document.body;
translateNode(root);
}
// ── MutationObserver — SwaggerUI 가 동적으로 DOM 을 다시 그릴 때마다 재적용 ──
var observer = new MutationObserver(function (mutations) {
for (var i = 0; i < mutations.length; i++) {
var m = mutations[i];
if (m.addedNodes && m.addedNodes.length) {
for (var j = 0; j < m.addedNodes.length; j++) translateNode(m.addedNodes[j]);
}
if (m.type === 'characterData') {
translateNode(m.target);
}
}
});
function start() {
translateAll();
observer.observe(document.body, {
childList: true,
subtree: true,
characterData: true
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', start);
} else {
start();
}
})(window);
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long