diff options
| author | joonhoekim <26rote@gmail.com> | 2025-10-17 18:46:22 +0900 |
|---|---|---|
| committer | joonhoekim <26rote@gmail.com> | 2025-10-17 18:46:22 +0900 |
| commit | 7349504c6a92fdc115cf59d9deef76880a8287c0 (patch) | |
| tree | b6a2e4fb8936aaf1a9e917c31556723378816367 /lib/soap | |
| parent | ab8b726fee9a82ba98c550a151988c86dc096efd (diff) | |
(김준회) MDG 벤더 송신 테스트페이지 개선, SWP 페이지 텍스트 수정
Diffstat (limited to 'lib/soap')
| -rw-r--r-- | lib/soap/mdg/send/vendor-master/action.ts | 119 | ||||
| -rw-r--r-- | lib/soap/sender.ts | 58 |
2 files changed, 118 insertions, 59 deletions
diff --git a/lib/soap/mdg/send/vendor-master/action.ts b/lib/soap/mdg/send/vendor-master/action.ts index d95f630c..bdb2d579 100644 --- a/lib/soap/mdg/send/vendor-master/action.ts +++ b/lib/soap/mdg/send/vendor-master/action.ts @@ -19,6 +19,7 @@ import { import { eq, sql, desc } from "drizzle-orm"; import { CSV_FIELDS } from './csv-fields'; import { sendSoapXml, type SoapSendConfig, type SoapLogInfo } from "@/lib/soap/sender"; +import { debugLog, debugError, debugSuccess } from '@/lib/debug-utils'; // SAP XI 엔드포인트 URL const MDG_ENDPOINT_URL = "http://shii8dvddb01.hec.serp.shi.samsung.net:50000/sap/xi/engine?type=entry&version=3.0&Sender.Service=P2038_Q&Interface=http%3A%2F%2Fshi.samsung.co.kr%2FP2_MD%2FMDZ%5EP2MD3007_AO&QualityOfService=ExactlyOnce"; @@ -39,6 +40,10 @@ async function sendVendorMasterToMDGInternal(supplierMaster: Record<string, stri success: boolean; message: string; responseText?: string; + requestXml?: string; + responseHeaders?: Record<string, string>; + statusCode?: number; + requestHeaders?: Record<string, string>; }> { try { // SOAP Body Content 생성 @@ -63,26 +68,42 @@ async function sendVendorMasterToMDGInternal(supplierMaster: Record<string, stri interface: 'IF_MDZ_EVCP_VENDOR_MASTER' }; - console.log(`📤 VENDOR 마스터 전송 시작`); - console.log(`🔍 SUPPLIER_MASTER 데이터: ${Object.keys(supplierMaster).length}개 필드`); + debugLog(`📤 VENDOR 마스터 전송 시작`); + debugLog(`🔍 SUPPLIER_MASTER 데이터: ${Object.keys(supplierMaster).length}개 필드`); // SOAP XML 전송 const result = await sendSoapXml(config, logInfo); + // 응답 상세 정보 로깅 + debugLog('📊 SOAP 전송 결과 상세:', { + success: result.success, + message: result.message, + statusCode: result.statusCode, + hasResponseText: !!result.responseText, + responseTextLength: result.responseText?.length, + hasRequestXml: !!result.requestXml, + requestXmlLength: result.requestXml?.length, + headers: result.headers + }); + if (result.success) { - console.log(`✅ VENDOR 마스터 전송 성공`); + debugSuccess(`✅ VENDOR 마스터 전송 성공`); } else { - console.error(`❌ VENDOR 마스터 전송 실패, 오류: ${result.message}`); + debugError(`❌ VENDOR 마스터 전송 실패, 오류: ${result.message}`); } return { success: result.success, message: result.success ? '전송 성공' : result.message, - responseText: result.responseText + responseText: result.responseText, + requestXml: result.requestXml, + responseHeaders: result.headers, + statusCode: result.statusCode, + requestHeaders: result.requestHeaders }; } catch (error) { - console.error('❌ VENDOR 마스터 전송 중 오류 발생:', error); + debugError('❌ VENDOR 마스터 전송 중 오류 발생:', error); return { success: false, message: error instanceof Error ? error.message : 'Unknown error' @@ -148,7 +169,7 @@ async function fetchVendorData(vendorCode: string) { }; } catch (error) { - console.error(`VENDOR ${vendorCode} 데이터 조회 실패:`, error); + debugError(`VENDOR ${vendorCode} 데이터 조회 실패:`, error); throw error; } } @@ -206,13 +227,13 @@ export async function sendVendorMasterToMDG(vendorCodes: string[]): Promise<{ results?: Array<{ vendorCode: string; success: boolean; error?: string }>; }> { try { - console.log(`🚀 VENDOR_MASTER 송신 시작: ${vendorCodes.length}개 벤더`); + debugLog(`🚀 VENDOR_MASTER 송신 시작: ${vendorCodes.length}개 벤더`); const results: Array<{ vendorCode: string; success: boolean; error?: string }> = []; for (const vendorCode of vendorCodes) { try { - console.log(`📤 VENDOR ${vendorCode} 데이터 조회 중...`); + debugLog(`📤 VENDOR ${vendorCode} 데이터 조회 중...`); const vendorData = await fetchVendorData(vendorCode); @@ -226,18 +247,18 @@ export async function sendVendorMasterToMDG(vendorCodes: string[]): Promise<{ } const supplierMaster = buildSupplierMasterData(vendorData); - console.log(`📄 VENDOR ${vendorCode} 데이터 생성 완료`); + debugLog(`📄 VENDOR ${vendorCode} 데이터 생성 완료`); const result = await sendVendorMasterToMDGInternal(supplierMaster); if (result.success) { - console.log(`✅ VENDOR ${vendorCode} MDG 전송 성공`); + debugSuccess(`✅ VENDOR ${vendorCode} MDG 전송 성공`); results.push({ vendorCode, success: true }); } else { - console.error(`❌ VENDOR ${vendorCode} 전송 실패: ${result.message}`); + debugError(`❌ VENDOR ${vendorCode} 전송 실패: ${result.message}`); results.push({ vendorCode, success: false, @@ -246,7 +267,7 @@ export async function sendVendorMasterToMDG(vendorCodes: string[]): Promise<{ } } catch (error) { - console.error(`❌ VENDOR ${vendorCode} 전송 실패:`, error); + debugError(`❌ VENDOR ${vendorCode} 전송 실패:`, error); results.push({ vendorCode, success: false, @@ -258,7 +279,7 @@ export async function sendVendorMasterToMDG(vendorCodes: string[]): Promise<{ const successCount = results.filter(r => r.success).length; const failCount = results.length - successCount; - console.log(`🎉 VENDOR_MASTER 송신 완료: 성공 ${successCount}개, 실패 ${failCount}개`); + debugSuccess(`🎉 VENDOR_MASTER 송신 완료: 성공 ${successCount}개, 실패 ${failCount}개`); return { success: failCount === 0, @@ -267,7 +288,7 @@ export async function sendVendorMasterToMDG(vendorCodes: string[]): Promise<{ }; } catch (error) { - console.error('❌ VENDOR_MASTER 송신 중 전체 오류 발생:', error); + debugError('❌ VENDOR_MASTER 송신 중 전체 오류 발생:', error); return { success: false, message: error instanceof Error ? error.message : 'Unknown error' @@ -314,14 +335,17 @@ export async function sendTestVendorDataToMDG(formData: Record<string, string>): message: string; responseData?: unknown; generatedXML?: string; + responseHeaders?: Record<string, string>; + statusCode?: number; + requestHeaders?: Record<string, string>; }> { try { - console.log('🚀 테스트용 VENDOR 데이터 송신 시작'); + debugLog('🚀 테스트용 VENDOR 데이터 송신 시작'); // 필수 필드 검증 const validation = validateMandatoryFields(formData); if (!validation.isValid) { - console.error('❌ 필수 필드 누락:', validation.missingFields); + debugError('❌ 필수 필드 누락:', validation.missingFields); return { success: false, message: validation.errorMessage, @@ -342,21 +366,38 @@ export async function sendTestVendorDataToMDG(formData: Record<string, string>): supplierMaster[f.field] = formData[f.field] ?? ''; }); - console.log('📄 SUPPLIER_MASTER 데이터 생성 완료'); + debugLog('📄 SUPPLIER_MASTER 데이터 생성 완료'); const result = await sendVendorMasterToMDGInternal(supplierMaster); + // 응답 정보 상세 로깅 + debugLog('📊 테스트 송신 결과:', { + success: result.success, + statusCode: result.statusCode, + responseHeaders: result.responseHeaders, + hasResponseText: !!result.responseText, + responseTextLength: result.responseText?.length, + hasRequestXml: !!result.requestXml, + requestXmlLength: result.requestXml?.length + }); + return { success: result.success, message: result.success ? '테스트 송신이 완료되었습니다.' : result.message, - responseData: result.responseText + responseData: result.responseText, + generatedXML: result.requestXml, + responseHeaders: result.responseHeaders, + statusCode: result.statusCode, + requestHeaders: result.requestHeaders }; } catch (error) { - console.error('❌ 테스트 송신 실패:', error); + debugError('❌ 테스트 송신 실패:', error); return { success: false, - message: error instanceof Error ? error.message : 'Unknown error' + message: error instanceof Error ? error.message : 'Unknown error', + responseData: undefined, + generatedXML: undefined }; } } @@ -386,14 +427,14 @@ export async function sendAllVendorsToMDG() { }; } - console.log(`⚠️ 전체 VENDOR 송신 요청: ${vendorCodes.length}개`); + debugLog(`⚠️ 전체 VENDOR 송신 요청: ${vendorCodes.length}개`); const batchSize = 10; const results: Array<{ vendorCode: string; success: boolean; error?: string }> = []; for (let i = 0; i < vendorCodes.length; i += batchSize) { const batch = vendorCodes.slice(i, i + batchSize); - console.log(`📦 배치 ${Math.floor(i / batchSize) + 1} 처리 중... (${batch.length}개)`); + debugLog(`📦 배치 ${Math.floor(i / batchSize) + 1} 처리 중... (${batch.length}개)`); const batchResult = await sendVendorMasterToMDG(batch); if (batchResult.results) { @@ -415,7 +456,7 @@ export async function sendAllVendorsToMDG() { }; } catch (error) { - console.error('전체 VENDOR 송신 중 오류:', error); + debugError('전체 VENDOR 송신 중 오류:', error); return { success: false, message: error instanceof Error ? error.message : 'Unknown error' @@ -430,7 +471,7 @@ export async function sendModifiedVendorsToMDG(): Promise<{ results?: Array<{ vendorCode: string; success: boolean; error?: string }>; }> { try { - console.log('🔍 수정된 VENDOR 데이터 조회 중...'); + debugLog('🔍 수정된 VENDOR 데이터 조회 중...'); const modifiedVendors = await db .select({ @@ -446,21 +487,21 @@ export async function sendModifiedVendorsToMDG(): Promise<{ const vendorCodes = modifiedVendors.map(v => v.VNDRCD); if (vendorCodes.length === 0) { - console.log('📝 수정된 VENDOR 데이터가 없습니다.'); + debugLog('📝 수정된 VENDOR 데이터가 없습니다.'); return { success: true, message: '수정된 VENDOR 데이터가 없습니다.' }; } - console.log(`📋 수정된 VENDOR ${vendorCodes.length}개 발견:`, vendorCodes); + debugLog(`📋 수정된 VENDOR ${vendorCodes.length}개 발견:`, vendorCodes); const batchSize = 10; const results: Array<{ vendorCode: string; success: boolean; error?: string }> = []; for (let i = 0; i < vendorCodes.length; i += batchSize) { const batch = vendorCodes.slice(i, i + batchSize); - console.log(`📦 수정 데이터 배치 ${Math.floor(i / batchSize) + 1} 처리 중... (${batch.length}개)`); + debugLog(`📦 수정 데이터 배치 ${Math.floor(i / batchSize) + 1} 처리 중... (${batch.length}개)`); const batchResult = await sendVendorMasterToMDG(batch); if (batchResult.results) { @@ -475,7 +516,7 @@ export async function sendModifiedVendorsToMDG(): Promise<{ const successCount = results.filter(r => r.success).length; const failCount = results.length - successCount; - console.log(`🎯 수정된 VENDOR 송신 완료: 성공 ${successCount}개, 실패 ${failCount}개`); + debugSuccess(`🎯 수정된 VENDOR 송신 완료: 성공 ${successCount}개, 실패 ${failCount}개`); return { success: failCount === 0, @@ -484,7 +525,7 @@ export async function sendModifiedVendorsToMDG(): Promise<{ }; } catch (error) { - console.error('❌ 수정된 VENDOR 송신 중 오류:', error); + debugError('❌ 수정된 VENDOR 송신 중 오류:', error); return { success: false, message: error instanceof Error ? error.message : 'Unknown error' @@ -509,7 +550,7 @@ export async function sendNVendorsToMDG( }; } - console.log(`🧪 테스트용 VENDOR 송신: ${count}건 (${startFrom}번째부터)`); + debugLog(`🧪 테스트용 VENDOR 송신: ${count}건 (${startFrom}번째부터)`); const vendors = await db .select({ VNDRCD: VENDOR_MASTER_BP_HEADER.VNDRCD }) @@ -526,11 +567,11 @@ export async function sendNVendorsToMDG( }; } - console.log(`📋 테스트 대상 VENDOR ${vendorCodes.length}개:`, vendorCodes); + debugLog(`📋 테스트 대상 VENDOR ${vendorCodes.length}개:`, vendorCodes); const result = await sendVendorMasterToMDG(vendorCodes); - console.log(`🧪 테스트 송신 완료: ${vendorCodes.length}개 처리`); + debugSuccess(`🧪 테스트 송신 완료: ${vendorCodes.length}개 처리`); return { ...result, @@ -538,7 +579,7 @@ export async function sendNVendorsToMDG( }; } catch (error) { - console.error('❌ 테스트 송신 중 오류:', error); + debugError('❌ 테스트 송신 중 오류:', error); return { success: false, message: error instanceof Error ? error.message : 'Unknown error' @@ -555,7 +596,7 @@ export async function sendRecentModifiedVendorsToMDG( results?: Array<{ vendorCode: string; success: boolean; error?: string }>; }> { try { - console.log(`🕒 최근 수정된 VENDOR ${count}건 조회 중...`); + debugLog(`🕒 최근 수정된 VENDOR ${count}건 조회 중...`); const recentVendors = await db .select({ @@ -578,12 +619,12 @@ export async function sendRecentModifiedVendorsToMDG( }; } - console.log(`📋 최근 수정된 VENDOR ${vendorCodes.length}개:`, + debugLog(`📋 최근 수정된 VENDOR ${vendorCodes.length}개:`, recentVendors.map(v => `${v.VNDRCD}(${v.updatedAt?.toISOString()})`)); const result = await sendVendorMasterToMDG(vendorCodes); - console.log(`🕒 최근 수정 데이터 송신 완료`); + debugSuccess(`🕒 최근 수정 데이터 송신 완료`); return { ...result, @@ -591,7 +632,7 @@ export async function sendRecentModifiedVendorsToMDG( }; } catch (error) { - console.error('❌ 최근 수정 데이터 송신 중 오류:', error); + debugError('❌ 최근 수정 데이터 송신 중 오류:', error); return { success: false, message: error instanceof Error ? error.message : 'Unknown error' @@ -644,7 +685,7 @@ export async function getVendorSendStatistics(): Promise<{ }; } catch (error) { - console.error('통계 조회 실패:', error); + debugError('통계 조회 실패:', error); throw error; } } diff --git a/lib/soap/sender.ts b/lib/soap/sender.ts index 1dfc8730..d12665cb 100644 --- a/lib/soap/sender.ts +++ b/lib/soap/sender.ts @@ -2,6 +2,7 @@ import { withSoapLogging } from "@/lib/soap/utils"; import { XMLBuilder } from 'fast-xml-parser'; +import { debugLog, debugError, debugWarn, debugSuccess } from '@/lib/debug-utils'; // 기본 인증 정보 타입 export interface SoapAuthConfig { @@ -97,6 +98,7 @@ export async function sendSoapXml( logInfo: SoapLogInfo, auth?: SoapAuthConfig ): Promise<SoapSendResult> { + let xmlData: string | undefined; try { // 인증 정보 설정 (기본값 사용) const authConfig = auth || getDefaultAuth(); @@ -109,10 +111,10 @@ export async function sendSoapXml( config.prefix ); - const xmlData = await generateSoapXml(soapEnvelope); + xmlData = await generateSoapXml(soapEnvelope); - console.log('📤 SOAP XML 전송 시작'); - console.log('🔍 전송 XML (첫 500자):', xmlData.substring(0, 500)); + debugLog('📤 SOAP XML 전송 시작'); + debugLog('🔍 전송 XML (첫 500자):', xmlData.substring(0, 500)); // 요청 헤더 및 fetch 옵션을 사전에 구성 const requestHeaders: Record<string, string> = { @@ -124,9 +126,9 @@ export async function sendSoapXml( if (authConfig.username && authConfig.password) { const credentials = Buffer.from(`${authConfig.username}:${authConfig.password}`).toString('base64'); requestHeaders['Authorization'] = `Basic ${credentials}`; - console.log('🔐 Basic Authentication 헤더 추가 완료'); + debugSuccess('🔐 Basic Authentication 헤더 추가 완료'); } else { - console.warn('⚠️ SOAP 인증 정보가 설정되지 않았습니다.'); + debugWarn('⚠️ SOAP 인증 정보가 설정되지 않았습니다.'); } const fetchOptions: RequestInit = { @@ -136,14 +138,18 @@ export async function sendSoapXml( }; // Body 루트 요소(p1:MT_...)에 기본 네임스페이스를 부여하여 하위 무접두사 요소들도 동일 네임스페이스로 인식되도록 처리 - const envelopeObj = soapEnvelope as Record<string, any>; - const bodyObj = envelopeObj['soap:Envelope']?.['soap:Body'] as Record<string, any> | undefined; + const envelopeObj = soapEnvelope as Record<string, unknown>; + const bodyObj = envelopeObj['soap:Envelope'] as Record<string, unknown> | undefined; if (bodyObj && typeof bodyObj === 'object') { - const rootKeys = Object.keys(bodyObj); - if (rootKeys.length > 0) { - const rootKey = rootKeys[0]; - if (bodyObj[rootKey] && typeof bodyObj[rootKey] === 'object') { - bodyObj[rootKey]['@_xmlns'] = namespace; + const soapBody = bodyObj['soap:Body'] as Record<string, unknown> | undefined; + if (soapBody && typeof soapBody === 'object') { + const rootKeys = Object.keys(soapBody); + if (rootKeys.length > 0) { + const rootKey = rootKeys[0]; + const rootValue = soapBody[rootKey]; + if (rootValue && typeof rootValue === 'object') { + (rootValue as Record<string, unknown>)['@_xmlns'] = namespace; + } } } } @@ -183,8 +189,19 @@ export async function sendSoapXml( const response = result as Response; const responseText = await response.text(); - console.log('📥 SOAP 응답 수신:', response.status, response.statusText); - console.log('🔍 응답 XML (첫 500자):', responseText.substring(0, 500)); + // 응답 헤더 수집 (디버깅용) + const responseHeadersDebug: Record<string, string> = {}; + response.headers.forEach((value, key) => { + responseHeadersDebug[key] = value; + }); + + debugLog('📥 SOAP 응답 수신:', { + status: response.status, + statusText: response.statusText, + headers: responseHeadersDebug, + bodyLength: responseText.length + }); + debugLog('🔍 응답 바디 (전체):', responseText); // HTTP 상태 코드가 비정상이거나 SOAP Fault 포함 시 실패로 처리하되 본문을 그대로 반환 if (!response.ok || responseText.includes('soap:Fault') || responseText.includes('SOAP:Fault')) { @@ -222,10 +239,11 @@ export async function sendSoapXml( }; } catch (error) { - console.error('❌ SOAP XML 전송 실패:', error); + debugError('❌ SOAP XML 전송 실패:', error); return { success: false, - message: error instanceof Error ? error.message : 'Unknown error' + message: error instanceof Error ? error.message : 'Unknown error', + requestXml: xmlData // 에러가 발생해도 생성된 XML이 있다면 반환 }; } } @@ -241,7 +259,7 @@ export async function sendSoapXmlWithRetry( for (let attempt = 1; attempt <= maxRetries; attempt++) { try { - console.log(`🔄 SOAP 전송 시도 ${attempt}/${maxRetries}`); + debugLog(`🔄 SOAP 전송 시도 ${attempt}/${maxRetries}`); const result = await sendSoapXml(config, logInfo, auth); @@ -251,12 +269,12 @@ export async function sendSoapXmlWithRetry( // 마지막 시도가 아니면 재시도 if (attempt < maxRetries) { - console.log(`⏳ ${retryDelay}ms 후 재시도...`); + debugLog(`⏳ ${retryDelay}ms 후 재시도...`); await new Promise(resolve => setTimeout(resolve, retryDelay)); } } catch (error) { - console.error(`❌ SOAP 전송 시도 ${attempt} 실패:`, error); + debugError(`❌ SOAP 전송 시도 ${attempt} 실패:`, error); if (attempt === maxRetries) { return { @@ -266,7 +284,7 @@ export async function sendSoapXmlWithRetry( } // 마지막 시도가 아니면 재시도 - console.log(`⏳ ${retryDelay}ms 후 재시도...`); + debugLog(`⏳ ${retryDelay}ms 후 재시도...`); await new Promise(resolve => setTimeout(resolve, retryDelay)); } } |
