@@ -11,6 +11,46 @@ class QueryParser {
1111 this . fileUtils = FileUtils ;
1212 }
1313
14+ /**
15+ * 시트명 유효성 검증
16+ * @param {string } sheetName - 검증할 시트명
17+ * @param {number } sheetIndex - 시트 인덱스 (에러 메시지용)
18+ * @returns {Object } { valid: boolean, errors: string[] }
19+ */
20+ validateSheetName ( sheetName , sheetIndex = 0 ) {
21+ const errors = [ ] ;
22+
23+ // Excel 시트명에 사용할 수 없는 문자
24+ const invalidChars = [ '\\' , '/' , '*' , '?' , '[' , ']' , ':' ] ;
25+
26+ // 1. 빈 문자열 체크
27+ if ( ! sheetName || sheetName . trim ( ) === '' ) {
28+ errors . push ( '시트명이 비어있습니다.' ) ;
29+ return { valid : false , errors } ;
30+ }
31+
32+ // 2. 최대 길이 체크 (31자)
33+ if ( sheetName . length > 31 ) {
34+ errors . push ( `시트명이 너무 깁니다 (최대 31자, 현재: ${ sheetName . length } 자)` ) ;
35+ }
36+
37+ // 3. 허용되지 않는 문자 체크
38+ const foundInvalidChars = invalidChars . filter ( char => sheetName . includes ( char ) ) ;
39+ if ( foundInvalidChars . length > 0 ) {
40+ errors . push ( `허용되지 않는 문자 포함: ${ foundInvalidChars . join ( ', ' ) } ` ) ;
41+ }
42+
43+ // 4. 시트명 시작/끝 공백 체크
44+ if ( sheetName !== sheetName . trim ( ) ) {
45+ errors . push ( '시트명 앞뒤에 공백이 있습니다.' ) ;
46+ }
47+
48+ return {
49+ valid : errors . length === 0 ,
50+ errors
51+ } ;
52+ }
53+
1454 /**
1555 * XML 파일에서 쿼리 로드
1656 * @param {string } xmlPath - XML 파일 경로
@@ -148,6 +188,17 @@ class QueryParser {
148188 }
149189 }
150190
191+ // 시트명 검증
192+ const sheetNameValidation = this . validateSheetName ( s . $ . name , i ) ;
193+ if ( ! sheetNameValidation . valid ) {
194+ console . error ( `\n❌ 시트명 검증 실패 (시트 #${ i + 1 } ):` ) ;
195+ console . error ( ` 시트명: "${ s . $ . name } "` ) ;
196+ sheetNameValidation . errors . forEach ( error => {
197+ console . error ( ` - ${ error } ` ) ;
198+ } ) ;
199+ throw new Error ( `시트명 검증 실패: "${ s . $ . name } "` ) ;
200+ }
201+
151202 // queryRef 속성이 있으면 쿼리 정의에서 참조
152203 if ( s . $ . queryRef ) {
153204 const queryRef = s . $ . queryRef ;
@@ -202,10 +253,21 @@ class QueryParser {
202253 const dynamicVars = queries . dynamicVars || [ ] ;
203254
204255 // JSON 시트에서 queryRef 처리
205- const sheets = ( queries . sheets || [ ] ) . map ( sheet => {
256+ const sheets = ( queries . sheets || [ ] ) . map ( ( sheet , i ) => {
206257 let query = sheet . query || '' ;
207258 let sheetParams = sheet . params || { } ;
208259
260+ // 시트명 검증
261+ const sheetNameValidation = this . validateSheetName ( sheet . name , i ) ;
262+ if ( ! sheetNameValidation . valid ) {
263+ console . error ( `\n❌ 시트명 검증 실패 (시트 #${ i + 1 } ):` ) ;
264+ console . error ( ` 시트명: "${ sheet . name } "` ) ;
265+ sheetNameValidation . errors . forEach ( error => {
266+ console . error ( ` - ${ error } ` ) ;
267+ } ) ;
268+ throw new Error ( `시트명 검증 실패: "${ sheet . name } "` ) ;
269+ }
270+
209271 // queryRef가 있으면 쿼리 정의에서 참조
210272 if ( sheet . queryRef ) {
211273 if ( queryDefs [ sheet . queryRef ] ) {
0 commit comments