From 91a156c6b0ef388c0adf0e2663de08e53a55f967 Mon Sep 17 00:00:00 2001 From: Rinjae Date: Tue, 11 Aug 2026 19:00:18 +0900 Subject: [PATCH] =?UTF-8?q?-=20Swagger=20spec=20=EC=84=9C=EB=B2=84=20?= =?UTF-8?q?=EC=A3=BC=EC=86=8C=20GW=20=EA=B8=B0=EC=A4=80=20=EC=B9=98?= =?UTF-8?q?=ED=99=98=20=ED=86=B5=ED=95=A9=20-=20UI/=EB=8B=A4=EC=9A=B4?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=20=EB=8F=99=EC=9D=BC=20=EA=B8=B0=EC=A4=80=20?= =?UTF-8?q?-=20ApiTesterFilter:=20gw=20=ED=98=B8=EC=B6=9C=20=EC=8B=9C=20ba?= =?UTF-8?q?se-url=20=EC=9E=AC=EC=A1=B0=EB=A6=BD=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20-=20admin=20=EC=8A=A4=ED=8E=99=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EC=8B=9C=20=EC=8B=A4=EC=A3=BC=EC=86=8C=20=EA=B3=A0?= =?UTF-8?q?=EC=A0=95=20=EC=A0=9C=EA=B1=B0=20-=20=EC=9E=AC=EC=B9=98?= =?UTF-8?q?=ED=99=98=20=EC=9C=A0=EC=97=B0=EC=84=B1=20=ED=99=95=EB=B3=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- WebContent/js/djb/apispec/app.js | 62 +++++++------------------------- 1 file changed, 13 insertions(+), 49 deletions(-) diff --git a/WebContent/js/djb/apispec/app.js b/WebContent/js/djb/apispec/app.js index 7a62ceb..9367991 100644 --- a/WebContent/js/djb/apispec/app.js +++ b/WebContent/js/djb/apispec/app.js @@ -29,10 +29,6 @@ // (enricher 는 oauth·api_key 를 모두 apiKey-in-header 로 모델링하므로 scheme.type 만으로 구분 불가) var djbAuthType = ''; - // 저장 스펙의 서버 자리표시자. 포탈이 제공 시점에 응답유형(sample/mock/gw)별 실주소로 치환한다. - // (포탈 DjbTestbedSpecServerRewriter.SERVER_SENTINEL 과 동일 문자열 — 양쪽을 같이 바꿔야 한다) - const SERVER_SENTINEL = 'http://swagger-server-url'; - // ============================================================ // 2. 유틸 // ============================================================ @@ -112,13 +108,7 @@ // ============================================================ // 3. OpenAPI 스펙 빌더 (state.data → OpenAPI 3.1 객체) // ============================================================ - /** - * state.data → OpenAPI 3.1 객체. - * - * @param resolveHost true 면 응답유형별 실주소를 서버에 부착(편집기 미리보기·다운로드용), - * false/생략이면 서버를 sentinel 로 남긴다(저장본 — 포탈이 제공 시점에 치환). - */ - function buildOpenApiSpec(resolveHost) { + function buildOpenApiSpec() { const d = state.data; syncResponseExamplesFromSchema(); // 응답 예제 = 응답 정보(스키마·예제값) 파생 — 저장/미리보기 일관 const spec = { @@ -201,23 +191,12 @@ } }); - // 경로(op.path.value)에는 어댑터경로가 이미 baking 됨(백엔드 applyAutoRules). 서버에는 응답유형별 호스트만 붙인다. - // - gw : djb.gateway.base-url (PortalProperty) → [gw호스트 + 어댑터경로 + 경로] - // - mock : Mock Server URL(전체 URL) → [mock URL 그대로] (어댑터/오퍼레이션 경로 미부착) - // - 그 외 : 호스트 없음 → [어댑터경로 + 경로] - // - // 단 이 실주소 부착은 "미리보기/다운로드" 표현에만 적용한다(resolveHost=true). - // 저장본은 서버를 sentinel 로 남겨 환경 독립을 유지하고, 포탈이 제공 시점에 응답유형별 실주소로 - // 치환한다(DjbTestbedSpecServerRewriter). 저장본에 실주소를 굳히면 포탈의 재치환이 무력화되고 - // 저장 환경(dev)의 GW 주소가 다른 환경까지 따라간다. - if (resolveHost) { - var _resolved = resolveSpecServer(); - if (_resolved.host) spec.servers = [{ url: _resolved.host }]; - spec.paths[_resolved.path] = { [op.method.value.toLowerCase()]: operation }; - } else { - spec.servers = [{ url: SERVER_SENTINEL }]; - spec.paths[op.path.value] = { [op.method.value.toLowerCase()]: operation }; - } + // spec 의 서버는 응답유형과 무관하게 항상 GW 주소로 고정한다 — spec 은 "이 API 를 실제로 어디로 + // 호출하는가"의 공개 계약이고, 그 기준은 게이트웨이다. sample/mock 은 테스트베드 동작(포탈 + // ApiTesterFilter 가 응답유형에 따라 sampleResponse 반환 / mockUrl forward)에서만 갈린다. + // 경로(op.path.value)에는 어댑터경로가 이미 baking 됨(백엔드 applyAutoRules). + spec.servers = [{ url: gatewayAddress() }]; + spec.paths[op.path.value] = { [op.method.value.toLowerCase()]: operation }; // 보안 스킴 d.securitySchemes.forEach(s => { @@ -989,22 +968,9 @@ } } - // 응답유형별 실주소 { host, path }. 편집기 미리보기·다운로드에서만 사용(저장본은 sentinel). - // - gw : DJB_CTX.gwAddress + [어댑터경로 + 경로] - // - mock : Mock Server URL 을 origin/path 로 분해 → 경로가 뒤에 덧붙지 않음 - // - 그 외 : 호스트 없음 + [어댑터경로 + 경로] - function resolveSpecServer() { - var d = state.data; - var rt = (d.portal && d.portal.responseType) || 'sample'; - var path = d.operation.path.value; - if (rt === 'gw') { - return { host: (window.DJB_CTX && window.DJB_CTX.gwAddress) || 'http://127.0.0.1:39310', path: path }; - } - if (rt === 'mock') { - var mk = splitFullUrl((d.portal && d.portal.mockUrl) || ''); - return { host: mk.origin, path: mk.path }; - } - return { host: '', path: path }; + // spec servers 에 넣을 게이트웨이 주소(단일 기준). 응답유형과 무관하게 항상 이 값을 쓴다. + function gatewayAddress() { + return (window.DJB_CTX && window.DJB_CTX.gwAddress) || 'http://127.0.0.1:39310'; } // 전체 URL → { origin, path }. Mock Server URL 을 서버+경로로 분해(경로를 별도로 덧붙이지 않기 위함). @@ -1129,8 +1095,7 @@ } function mountSwagger() { - // 미리보기는 실주소로 — sentinel 이 가짜 주소로 보이면 안 된다. - const spec = buildOpenApiSpec(true); + const spec = buildOpenApiSpec(); swaggerInstance = SwaggerUIBundle({ spec: spec, domNode: $('#preview-swagger'), @@ -1148,7 +1113,7 @@ function updateMonacoFromState() { if (!monacoReady) return; - const spec = buildOpenApiSpec(true); // YAML 미리보기도 실주소 표시 + const spec = buildOpenApiSpec(); const yaml = jsyaml.dump(spec, { noRefs: true, lineWidth: 120 }); if (monacoEditor.getValue() !== yaml) monacoEditor.setValue(yaml); } @@ -1647,7 +1612,7 @@ // 16. Export // ============================================================ function downloadSpec(format) { - const spec = buildOpenApiSpec(true); // 편집기 다운로드는 실주소 산출물 + const spec = buildOpenApiSpec(); if (format === 'yaml') { doDownload(jsyaml.dump(spec, { noRefs: true }), 'text/yaml', 'yaml'); return; } if (format === 'json') { doDownload(JSON.stringify(spec, null, 2), 'application/json', 'json'); return; } // html — 폐쇄망 자립: 벤더 swagger css/js 를 fetch 해 인라인 @@ -2050,7 +2015,6 @@ function saveSpec(temp) { var ctx = window.DJB_CTX || {}; if (!ctx.saveUrl || !ctx.eaiSvcName) { toast('저장 대상이 없습니다', 'error'); return; } - // 저장본은 sentinel 유지 — 실주소는 포탈이 제공 시점에 응답유형별로 치환한다. var spec = buildOpenApiSpec(); var d = state.data; var reqEx = (d.examples && d.examples.request && d.examples.request[d.requestBody && d.requestBody.mediaType || 'application/json'] || {}).default || '';