인터페이스 다운로드 변경사항 반영

This commit is contained in:
Boram
2026-08-14 11:30:55 +09:00
parent 09cc22e4a4
commit 45e2c148ab
2 changed files with 54 additions and 28 deletions

View File

@@ -11,7 +11,9 @@ import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Pattern;
import org.apache.poi.ss.usermodel.*;
@@ -45,6 +47,7 @@ public class DtoExcelDownloadController {
private static final String MCI_BASE_PACKAGE = "io.shinhanlife.dap.mcc.infra.itrf.mci";
private static final String INVALID_DTO_MESSAGE =
"dto의 내용이 엑셀파일 양식에 맞지않습니다 파일을 확인해주세요";
private static final Pattern VARIANT_SUFFIX = Pattern.compile("_[IO]$");
private final Map<String, String> dtoClasses;
@@ -54,8 +57,12 @@ public class DtoExcelDownloadController {
@GetMapping("/dto-download/options")
public List<String> options() {
// 클래스패스에서 자동 검색된 DTO 목록을 화면에 제공한다.
return List.copyOf(dtoClasses.keySet());
// DTO는 항상 _I/_O 한 쌍으로 존재하므로 접미사를 제거한 이름 단위로 묶어 화면에 제공한다.
Set<String> baseNames = new TreeSet<>();
for (String simpleName : dtoClasses.keySet()) {
baseNames.add(VARIANT_SUFFIX.matcher(simpleName).replaceFirst(""));
}
return List.copyOf(baseNames);
}
@GetMapping("/dto-download/{dtoName}")
@@ -328,7 +335,9 @@ public class DtoExcelDownloadController {
private Annotation telegramMetadata(Field field) {
for (Annotation annotation : field.getDeclaredAnnotations()) {
if (annotation.annotationType().getSimpleName().equals("GlowTrgmField")) {
String annotationName = annotation.annotationType().getSimpleName();
if (annotationName.equals("GlowTrgmField")
|| annotationName.equals("GlowMciFieldInfo")) {
return annotation;
}
}

View File

@@ -94,7 +94,7 @@
const select = $('toolSelect'), args = $('arguments'), result = $('result');
async function loadDtoOptions() {
// 서버가 itrf/mci/**/io 패키지에서 찾은 DTO 목록으로 셀렉트박스를 구성한다.
// 서버가 itrf/mci/**/io 패키지에서 찾은 DTO를 _I/_O 접미사 없이 이름 단위로 묶어 셀렉트박스를 구성한다.
const dtoSelect = $('dtoSelect');
try {
const response = await fetch('/dto-download/options', { headers: { 'Cache-Control': 'no-cache' } });
@@ -104,7 +104,7 @@
for (const dtoName of dtoNames) {
const option = document.createElement('option');
option.value = dtoName;
option.textContent = `${dtoName}.java`;
option.textContent = dtoName;
dtoSelect.appendChild(option);
}
if (!dtoNames.length) dtoSelect.innerHTML = '<option value="">DTO 파일이 없습니다.</option>';
@@ -115,34 +115,51 @@
function openDtoModal() { $('dtoDownloadModal').classList.remove('hidden'); loadDtoOptions(); $('dtoSelect').focus(); }
function closeDtoModal() { $('dtoDownloadModal').classList.add('hidden'); $('dtoDownloadButton').focus(); }
async function downloadSelectedDto() {
// 선택한 DTO의 엑셀 생성 API를 호출하고 응답 Blob을 브라우저 다운로드로 저장한다.
const dtoName = $('dtoSelect').value;
if (!dtoName) return alert('다운로드할 DTO를 선택해주세요.');
// 선택한 DTO 이름의 _I/_O 엑셀 생성 API를 순차 호출해 존재하는 파일을 각각 브라우저 다운로드로 저장한다.
const baseName = $('dtoSelect').value;
if (!baseName) return alert('다운로드할 DTO를 선택해주세요.');
const confirmButton = $('dtoConfirmButton');
confirmButton.disabled = true;
const errors = [];
let successCount = 0;
try {
const response = await fetch(`/dto-download/${encodeURIComponent(dtoName)}`);
if (!response.ok) {
// 422는 GlowTrgmField 메타데이터가 없어 설계서 양식으로 변환할 수 없는 경우다.
const message = (await response.text()).trim();
throw new Error(response.status === 422
? 'dto의 내용이 엑셀파일 양식에 맞지않습니다 파일을 확인해주세요'
: message || (response.status === 404
? `${dtoName}.xlsx 파일을 찾을 수 없습니다.`
: `다운로드에 실패했습니다. (HTTP ${response.status})`));
for (const variant of ['I', 'O']) {
const dtoName = `${baseName}_${variant}`;
try {
const response = await fetch(`/dto-download/${encodeURIComponent(dtoName)}`, {
cache: 'no-store',
headers: { 'Cache-Control': 'no-cache' }
});
if (response.status === 404) continue; // _I/_O 중 한쪽만 존재하는 DTO는 정상 케이스이므로 건너뛴다.
if (!response.ok) {
// 422는 GlowTrgmField 메타데이터가 없어 설계서 양식으로 변환할 수 없는 경우다.
const message = (await response.text()).trim();
throw new Error(response.status === 422
? `${dtoName}: dto의 내용이 엑셀파일 양식에 맞지않습니다 파일을 확인해주세요`
: message || `${dtoName} 다운로드에 실패했습니다. (HTTP ${response.status})`);
}
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `${dtoName}.xlsx`;
document.body.appendChild(link);
link.click();
link.remove();
URL.revokeObjectURL(url);
successCount += 1;
// 브라우저가 연속 다운로드를 한 번에 차단하지 않도록 다음 요청 전에 짧은 간격을 둔다.
await new Promise(resolve => setTimeout(resolve, 300));
} catch (error) {
errors.push(error.message);
}
}
const blob = await response.blob();
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `${dtoName}.xlsx`;
document.body.appendChild(link);
link.click();
link.remove();
URL.revokeObjectURL(url);
if (successCount === 0) {
alert(errors.length ? errors.join('\n') : `${baseName} 파일을 찾을 수 없습니다.`);
return;
}
if (errors.length) alert(errors.join('\n'));
closeDtoModal();
} catch (error) {
alert(error.message);
} finally {
confirmButton.disabled = false;
}