박정하 박정하 04-11
250411 박정하 오류 수정
@75e5f56c1d267089c36d9daab29b5320e5a4730a
client/resources/js/cmmnPlugin.js
--- client/resources/js/cmmnPlugin.js
+++ client/resources/js/cmmnPlugin.js
@@ -25,7 +25,53 @@
 
     // 정규식을 사용하여 모든 HTML 태그 제거
     Vue.config.globalProperties.$stripHtml = (html) => {
-      return html.replace(/<[^>]*>/g, '');
-    }
+      if (!html) return '';
+
+      // 환경 확인 (브라우저 vs 서버)
+      if (typeof window !== 'undefined' && window.DOMParser) {
+        try {
+          // 브라우저 환경에서는 DOMParser 사용
+          const doc = new DOMParser().parseFromString(html, 'text/html');
+          let text = doc.body.textContent || '';
+          return text.replace(/\s+/g, ' ').trim();
+        } catch (e) {
+          // DOMParser 실패시 정규식 방식으로 폴백
+          console.warn('DOMParser failed, falling back to regex', e);
+        }
+      }
+
+      // 서버 환경이거나 DOMParser 실패 시 정규식 사용
+      let text = html.replace(/<[^>]*>/g, '');
+
+      // 일반적인 HTML 엔티티 처리
+      const entityMap = {
+        '&nbsp;': ' ',
+        '&amp;': '&',
+        '&lt;': '<',
+        '&gt;': '>',
+        '&quot;': '"',
+        '&#39;': "'",
+        '&mdash;': '—',
+        '&ndash;': '–',
+        '&deg;': '°'
+      };
+
+      // 정의된 엔티티 변환
+      Object.keys(entityMap).forEach(entity => {
+        const regex = new RegExp(entity, 'g');
+        text = text.replace(regex, entityMap[entity]);
+      });
+
+      // 남은 숫자 엔티티 제거 (&#123; 형식)
+      text = text.replace(/&#[0-9]+;/g, '');
+
+      // 남은 이름 있는 엔티티 제거 (&hellip; 등)
+      text = text.replace(/&[a-zA-Z]+;/g, '');
+
+      // 연속된 공백 문자 하나로 처리
+      text = text.replace(/\s+/g, ' ').trim();
+
+      return text;
+    };
   }
 }
(파일 끝에 줄바꿈 문자 없음)
client/views/component/listLayout/CardStyleComponent.vue
--- client/views/component/listLayout/CardStyleComponent.vue
+++ client/views/component/listLayout/CardStyleComponent.vue
@@ -4,7 +4,7 @@
       <div class="result-box">
         <div class="main-img">
           <img v-if="name === 'M'" :src="getYouTubeThumbnail(item.link)" alt="영상 썸네일">
-          <img v-else-if="!$isEmpty(item.thumbnail)" :src="item.thumbnail.filePath" :alt="item.sj + ' 썸네일'">
+          <img v-else-if="!$isEmpty(item.thumbnail)" :src="item.thumbnail.filePath" :alt="item.sj + ' 썸네일'" loading="lazy">
           <img v-else src="client/resources/images/img6.png" alt="Not found image">
         </div>
         <div class="text-box">
client/views/component/modal/CategorySelectModal.vue
--- client/views/component/modal/CategorySelectModal.vue
+++ client/views/component/modal/CategorySelectModal.vue
@@ -6,8 +6,8 @@
         <button class="closebtn" @click="$emit('toggleModal')">✕</button>
       </div>
       <div class="modal-search flex-center mb-20">
-        <input type="text" placeholder="카테고리명을 입력하세요." v-model="searchReqDTO.searchText" @keyup.enter="fnFindAllCategory">
-        <button type="button" class="search-btn" @click="fnFindAllCategory">
+        <input type="text" placeholder="카테고리명을 입력하세요." v-model="searchReqDTO.searchText" @keyup.enter="fnChnageReqDTO">
+        <button type="button" class="search-btn" @click="fnChnageReqDTO">
           <img :src="searchicon" alt="">
           <p>검색</p>
         </button>
@@ -78,12 +78,20 @@
   },
 
   created() {
-    this.fnFindAllCategory(); // 목록 조회
+    this.fnSearch(); // 목록 조회
   },
 
   methods: {
+    // 검색 조건이 변경된 경우
+    fnChnageReqDTO() {
+      this.searchReqDTO.currentPage = 1;
+      this.$nextTick(() => {
+        this.fnSearch();
+      });
+    },
+
     // 목록 조회
-    async fnFindAllCategory() {
+    async fnSearch() {
       try {
         if (this.searchReqDTO.hasOwnProperty('ctgryIds')) {
           delete this.searchReqDTO.ctgryIds;
@@ -126,7 +134,7 @@
       this.searchReqDTO.currentPage = Number(currentPage);
 
       this.$nextTick(() => {
-        this.fnFindAllCategory();
+        this.fnSearch();
       });
     },
   }
client/views/pages/bbsMediaVido/MediaVideoInsert.vue
--- client/views/pages/bbsMediaVido/MediaVideoInsert.vue
+++ client/views/pages/bbsMediaVido/MediaVideoInsert.vue
@@ -26,7 +26,11 @@
         <div class="hr"></div>
         <dd>
           <label for="link">주소</label>
-          <div class="wfull"><input type="text" id="link" placeholder="URL 주소를 입력하세요" v-model="requestDTO.link"></div>
+          <input type="text" id="link" class="invalid-url" placeholder="유튜브 URL 주소를 입력하세요" v-model="requestDTO.link">
+          <div class="invalid-feedback border">
+            <img :src="erroricon" alt="">
+            <span>유튜브 URL만 입력 가능합니다.</span>
+          </div>
         </dd>
         <div class="hr"></div>
         <dd>
@@ -97,6 +101,7 @@
         ctgryIds: [], // 카테고리 정보
       },
 
+      isValidYoutubeURL: true,
       selectedCtgries: [], // 카테고리 목록
     };
   },
@@ -168,6 +173,13 @@
           return;
         }
       }
+      if (!this.$isEmpty(this.requestDTO.link)) {
+        const youtubeRegex = /^(https?:\/\/)?(www\.)?(youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/shorts\/|youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})(\S*)?$/;
+        if (!youtubeRegex.test(this.requestDTO.link)) {
+          alert("주소는 유튜브 URL만 입력 가능합니다.");
+          return;
+        }
+      }
 
       try {
         if (this.$isEmpty(this.pageId)) {
@@ -212,7 +224,12 @@
     // 생산연도 입력 제한
     onlyNumberInput() {
       this.requestDTO.prdctnYear = this.requestDTO.prdctnYear.replace(/[^0-9]/g, '');
-    }
+    },
   }
 };
-</script>
(파일 끝에 줄바꿈 문자 없음)
+</script>
+<style scoped>
+.invalid-url {
+  width: 50%;
+}
+</style>
(파일 끝에 줄바꿈 문자 없음)
client/views/pages/bbsNesDta/NewsReleaseInsert.vue
--- client/views/pages/bbsNesDta/NewsReleaseInsert.vue
+++ client/views/pages/bbsNesDta/NewsReleaseInsert.vue
@@ -286,6 +286,13 @@
           return;
         }
       }
+      if (!this.$isEmpty(this.requestDTO.link)) {
+        const urlRegex = /^(https?:\/\/)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$/;
+        if (!urlRegex.test(this.requestDTO.link)) {
+          alert("링크는 url만 입력 가능합니다.");
+          return;
+        }
+      }
 
       try {
         const formData = new FormData();
@@ -324,6 +331,11 @@
         // 파일 추가
         if (this.multipartFiles.length > 0) {
           formData.append("multipartFiles", this.multipartFiles[0]);
+
+          // 썸네일 정보 추가
+          if (this.selectedThumb !== null) {
+            formData.append('selectedThumb', this.multipartFiles[0].name);
+          }
         }
 
         // 기존파일 수정
@@ -331,11 +343,6 @@
           for (let file of this.requestDTO.files) {
             formData.append("files", file.fileOrdr);
           }
-        }
-
-        // 썸네일 정보 추가
-        if (this.selectedThumb !== null) {
-          formData.append('selectedThumb', this.multipartFiles[0].name);
         }
 
         // API 통신
Add a comment
List