yona#37 파일 업로드 클라이언트, 서버단 사전 검증으로 적용

This commit is contained in:
Rinjae
2025-12-01 11:03:58 +09:00
parent 6501ace1af
commit 609984ae94
12 changed files with 231 additions and 57 deletions
+4 -5
View File
@@ -40,11 +40,6 @@ spring:
cache: false
main:
allow-bean-definition-overriding: true
servlet:
multipart:
max-file-size: 8MB
max-request-size: 8MB
security:
basic:
enabled: false
@@ -71,6 +66,10 @@ gateway:
entity-package: com.eactive.eai.data.entity.onl
portal:
file:
max-size: 10MB
allowed-extensions: pdf,doc,docx,xls,xlsx,ppt,pptx,hwp,gif,jpg,jpeg,png
logging:
log-path: /Log/App/eapim/
+80
View File
@@ -64,6 +64,86 @@ $(function(){
});
});
/**
* 파일 검증 유틸리티
* PORTAL_CONFIG.file 설정값 사용
*/
const FileValidator = {
/**
* 파일 크기 검증
* @param {File} file - 검증할 파일
* @returns {boolean} 유효 여부
*/
validateFileSize: function(file) {
const maxSizeBytes = window.PORTAL_CONFIG?.file?.maxSizeBytes || 8388608;
if (file.size > maxSizeBytes) {
const maxSize = window.PORTAL_CONFIG?.file?.maxSize || '8MB';
customPopups.showAlert('파일 크기는 ' + maxSize + '를 초과할 수 없습니다.\n선택한 파일: ' + this.formatFileSize(file.size));
return false;
}
return true;
},
/**
* 파일 확장자 검증
* @param {File} file - 검증할 파일
* @returns {boolean} 유효 여부
*/
validateFileExtension: function(file) {
const allowedExtensions = (window.PORTAL_CONFIG?.file?.allowedExtensions || '').split(',');
const fileExt = file.name.split('.').pop().toLowerCase();
if (!allowedExtensions.includes(fileExt)) {
customPopups.showAlert('허용되지 않는 파일 형식입니다.\n허용된 형식: ' + allowedExtensions.join(', '));
return false;
}
return true;
},
/**
* 파일 전체 검증 (크기 + 확장자)
* @param {File} file - 검증할 파일
* @returns {boolean} 유효 여부
*/
validateFile: function(file) {
return this.validateFileExtension(file) && this.validateFileSize(file);
},
/**
* 여러 파일 검증
* @param {FileList} files - 검증할 파일 목록
* @returns {boolean} 모두 유효 여부
*/
validateFiles: function(files) {
for (let i = 0; i < files.length; i++) {
if (!this.validateFile(files[i])) {
return false;
}
}
return true;
},
/**
* 바이트를 읽기 쉬운 형식으로 변환
* @param {number} bytes
* @returns {string}
*/
formatFileSize: function(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
},
/**
* 최대 허용 용량 반환 (표시용)
* @returns {string}
*/
getMaxSizeDisplay: function() {
return window.PORTAL_CONFIG?.file?.maxSize || '8MB';
}
};
class FormValidator {
constructor(selector, validateFunction, message, extraValidation) {
this.form = document.querySelector(selector);
@@ -62,7 +62,7 @@
</div>
<div class="list-dot">
<p>첨부파일은 10MB 이내로 등록해 주세요.</p>
<p th:text="'첨부파일은 ' + ${@portalProperties.file.maxSize} + ' 이내로 등록해 주세요.'">첨부파일은 8MB 이내로 등록해 주세요.</p>
<p>문서파일과 이미지 파일만 등록 가능합니다. (pdf, doc, docx, xls, xlsx, ppt, pptx, hwp, gif, jpg,
png)</p>
</div>
@@ -123,11 +123,8 @@
const file = fileInput.files[0];
if (file) {
const allowedExtensions = ['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.hwp', '.gif', '.jpg', '.jpeg', '.png'];
const fileExt = '.' + file.name.split('.').pop().toLowerCase();
if (!allowedExtensions.includes(fileExt)) {
customPopups.showAlert("허용된 파일 형식이 아닙니다.\n문서파일(pdf, doc, docx, xls, xlsx, ppt, pptx, hwp)과 이미지 파일(gif, jpg, png)만 등록 가능합니다.");
// FileValidator를 사용한 파일 검증
if (!FileValidator.validateFile(file)) {
fileInput.value = '';
fileNameInput.value = '';
fileNameInput.classList.remove('i_trash', 'after');
@@ -61,7 +61,7 @@
</div>
<div class="list-dot">
<p>첨부파일은 10MB 이내로 등록해 주세요.</p>
<p th:text="'첨부파일은 ' + ${@portalProperties.file.maxSize} + ' 이내로 등록해 주세요.'">첨부파일은 8MB 이내로 등록해 주세요.</p>
<p>문서파일과 이미지 파일만 등록 가능합니다. (pdf, doc, docx, xls, xlsx, ppt, pptx, hwp, gif, jpg,
png)</p>
</div>
@@ -127,32 +127,15 @@
const fileInput = document.getElementById('file');
const fileNameInput = document.querySelector('.upload-name');
const allowFileExtention = ['pdf','doc','docx','xls','xlsx','ppt','pptx','hwp','gif','jpg','png'];
function isValidFileExtention(filename) {
const extention = filename.split('.').pop().toLowerCase();
return allowFileExtention.includes(extention);
}
function isValidFileSize(filesize) {
const maxSize = 10 * 1024 * 1024;
return filesize <= maxSize;
}
fileInput.addEventListener('change', function () {
const file = fileInput.files[0];
if (file) {
if(!isValidFileExtention(file.name)) {
const errorMsg = '허용된 파일 형식이 아닙니다. <br>문서파일과 이미지 파일만 등록 가능합니다. <br> (pdf, doc, docx, xls, xlsx, ppt, pptx, hwp, gif, jpg, png)';
const formattedMsg = errorMsg.replace(/\n/g,'<br>');
$('#customAlertMessage').html(formattedMsg);
$('#customAlert').css('display','flex');
return;
}
if(!isValidFileSize(file.size)) {
customPopups.showAlert('첨부파일은 10MB 이내로 등록해 주세요.')
// FileValidator를 사용한 파일 검증
if (!FileValidator.validateFile(file)) {
fileInput.value = '';
fileNameInput.value = '첨부파일을 등록 해 주세요';
fileNameInput.classList.remove('i_trash', 'after');
fileNameInput.classList.add('before');
return;
}
fileNameInput.value = file.name;
@@ -265,7 +265,7 @@
<span th:errors="*{compRegFile}"></span>
</div>
<div class="list-dot">
<p>첨부파일은 10MB 이내로 등록해 주세요.</p>
<p th:text="'첨부파일은 ' + ${@portalProperties.file.maxSize} + ' 이내로 등록해 주세요.'">첨부파일은 8MB 이내로 등록해 주세요.</p>
<p>문서파일과 이미지 파일만 등록 가능합니다. (pdf, doc, docx, xls, xlsx, ppt, pptx, hwp, gif, jpg, png)</p>
</div>
</div>
@@ -354,28 +354,18 @@
fileInput.addEventListener('change', function() {
const files = fileInput.files;
if (files.length > 0) {
let totalSize = 0;
let fileNames = [];
const allowedExtensions = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'hwp', 'gif', 'jpg', 'png'];
for (let i = 0; i < files.length; i++) {
const file = files[i];
totalSize += file.size;
const fileExtension = file.name.split('.').pop().toLowerCase();
if (!allowedExtensions.includes(fileExtension)) {
customPopups.showAlert(`'${file.name}'은(는) 허용되지 않는 파일 형식입니다.`);
fileInput.value = '';
return;
}
fileNames.push(file.name);
// FileValidator를 사용한 파일 검증
if (!FileValidator.validateFiles(files)) {
fileInput.value = '';
fileNameInput.value = '';
fileNameInput.classList.remove('i_trash', 'after');
fileNameInput.classList.add('before');
return;
}
if (totalSize > 10 * 1024 * 1024) { // 10MB
customPopups.showAlert('총 파일 크기는 10MB를 초과할 수 없습니다.');
fileInput.value = '';
return;
let fileNames = [];
for (let i = 0; i < files.length; i++) {
fileNames.push(files[i].name);
}
fileNameInput.value = fileNames.join(', ');
@@ -35,6 +35,18 @@
<script th:src="@{/plugins/jquery/jquery-3.7.1.min.js}"></script>
<script th:src="@{/js/lodash.js}"></script>
<!-- Portal 설정값 전역 노출 -->
<script th:inline="javascript">
window.PORTAL_CONFIG = {
file: {
maxSize: /*[[${@portalProperties.file.maxSize}]]*/ '8MB',
maxSizeBytes: /*[[${@portalProperties.file.maxSizeBytes}]]*/ 8388608,
allowedExtensions: /*[[${@portalProperties.file.allowedExtensions}]]*/ 'pdf,doc,docx,xls,xlsx,ppt,pptx,hwp,gif,jpg,jpeg,png'
}
};
</script>
<script th:src="@{/js/common.js}"></script>
<script th:src="@{/js/moment.min.js}"></script>
<script th:src="@{/js/daterangepicker.js}"></script>