파일 업로드 및 대용량 엑셀 다운로드
프로젝트에서 파일 업로드 및 대용량 엑셀 다운로드를 구현하기 위한 가이드로 각 기능의 사용 방법과 코드 예제 및 설정 방법을 제공합니다.
파일 업로드는 REST API와 FormData를 활용하며 다양한 저장소(WAS/NAS, FTP, AWS S3, Azure Blob Storage) 옵션을 제공합니다.
대용량 엑셀 다운로드는 최신 MyBatis Cursor 기능을 활용한 대용량 데이터 처리 효율성을 극대화 합니다.
파일 업로드
기본 사용 흐름
Plugin으로 제공하는 restApi.ts에서 uploadPost를 이용해 파일 업로드를 처리하고, 업로드할 파일을 FormData에 추가하여 서버로 전송합니다.
필드 컴포넌트(fields.tsx)에서 Upload, UploadBox를 사용해 파일의 확장자와 용량 제한을 검증하여 안정성을 확보할 수 있습니다.
저장소 옵션은 WAS/NAS, FTP, AWS S3, Azure Blob Storage 중 선택하여 저장소를 구성합니다.
@Configuration @Bean으로 저장소 옵션에 설정한 정보로 upload 메소드를 정의하여 @Qualifier("uploader") 으로 사용합니다.
Component를 사용한 유효성 검증 예시
export default function Upload() {
const { setValue } = useFormContext();
const validator = <T extends File>(file: T) => {
const fileType = file.type;
const fileSize = file.size;
const validTypes = Object.keys(UPLOAD_IMAGE_ACCEPT);
if (!validTypes.includes(fileType)) {
return { message: '확장자가 올바르지 않습니다.', code: 'ERR_TYPE' };
}
if (GOODS_UPLOAD.MAX_IMAGE_SIZE < fileSize) {
return { message: '최대 용량을 초과하였습니다.', code: 'ERR_SIZE' };
}
return null;
};
const onUpload = (files: File[]) => {
setValue('File', files);
};
const onDelete = () => {
setValue('File', null);
};
return (
<Field.Upload
name="fileUpload"
accept={UPLOAD_IMAGE_ACCEPT}
validator={validator}
onDelete={onDelete}
onDrop={onUpload}
/>
);
}업로드 관련 상수 예시
export const UPLOAD_IMAGE_ACCEPT = {
'image/png': [],
'image/jpg': [],
'image/jpeg': [],
'image/gif': []
};
export const UPLOAD_VIDEO_ACCEPT = {
'video/mp4': [],
'video/avi': [],
'video/mov': []
};
// 전시
export const DISPLAY_UPLOAD = {
MAX_IMAGE_SIZE: 10485760,
MAX_VIDEO_SIZE: 104857600
};
// 상품
export const GOODS_UPLOAD = {
MAX_IMAGE_SIZE: 10485760,
MAX_VIDEO_SIZE: 104857600
};(원본: upload-constants.ts)
API 저장소 설정 (Spring Boot applcation.yml 예시)
upload:
type: s3 # file : WAS / NAS, ftp : FTP, s3 : AWS S3, azure : Azure Blob Storage
root-path: files/ # 파일업로드 시 저장될 경로를 설정합니다.
file:
base-path: /data/ # was의 기본 업로드 경로 또는 mount 되는 nas의 기본 경로를 설정합니다.
base-path-video: /data/video
ftp:
host: 192.168.2.247
user: test
password: test
azure:
connection: DefaultEndpointsProtocol=https;AccountName=testblob;AccountKey=....
container: test
# aws의 경우에는 스프링에서 정한 설정 변수를 사용함.
cloud:
aws:
s3:
bucket: x2bee-stg-pri-attachment-s3
s3-video-origin:
bucket: x2bee-stg-pri-attachment-s3
region:
static: ap-northeast-2
stack:
auto: falseSpring Uploader Bean 설정 예시 (UploaderConfig.java)
package com.x2bee.api.common.base.config;
import com.x2bee.common.base.upload.UploaderCommonImpl;
import com.x2bee.common.base.upload.Uploader;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
@Configuration
public class UploaderConfig {
@Autowired
private Environment env;
@Value("${upload.file.base-path:#{null}}")
private String basePath;
@Value("${upload.file.base-path-video:#{null}}")
private String videoBasePath;
@Value("${upload.ftp.host:}")
private String ftpHost;
@Value("${upload.ftp.user:}")
private String ftpUser;
@Value("${upload.ftp.password:}")
private String ftpPassword;
@Value("${cloud.aws.region.static:#{null}}")
private String awsRegion;
@Value("${cloud.aws.s3.bucket:#{null}}")
private String bucket;
@Value("${cloud.aws.s3-video-origin.bucket:#{null}}")
private String videoBucket;
@Value("${upload.azure.connection:#{null}}")
private String azureConnection;
@Value("${upload.azure.container:#{null}}")
private String azureContainer;
@Bean(name = "uploader")
// apllication.yml 설정값으로 bean 모듈을 생성하여 파일업로드를 합니다.
public Uploader uploader() {
return new UploaderCommonImpl.Builder(env)
.basePath(basePath)
.ftpHost(ftpHost)
.ftpUser(ftpUser)
.ftpPassword(ftpPassword)
.awsRegion(awsRegion)
.bucket(bucket)
.azureConnection(azureConnection)
.azureContainer(azureContainer)
.build();
}
@Bean(name = "videoUploader")
public Uploader videoUploader() {
return new UploaderCommonImpl.Builder(env)
.basePath(videoBasePath)
.ftpHost(ftpHost)
.ftpUser(ftpUser)
.ftpPassword(ftpPassword)
.awsRegion(awsRegion)
.bucket(videoBucket)
.build();
}
}(원본: UploaderConfig.java)
SampleServiceImpl.java 예시 (서버 업로드 처리)
@Service
@Slf4j
@RequiredArgsConstructor
public class SampleServiceImpl implements SampleService {
@Qualifier("uploader")
private final Uploader uploader;
@Qualifier("videoUploader")
private final Uploader videoUploader;
@Qualifier("fileuUploader")
private final Uploader fileuUploader;
@Override
public String fileUpload(HttpServletRequest httpServletRequest) throws Exception {
AtomicReference<String> result = new AtomicReference<>("");
MultipartHelper.handle(httpServletRequest, (fieldName, fileName, fileSize, inputStream, multipartFile) -> {
UploadReqDto uploadReqDto = new UploadReqDto();
uploadReqDto.setAttacheFileKind(AttacheFileKind.SYSTEM);
uploadReqDto.setTempPathYn(false);
uploadReqDto.setCustomPath("");
uploadReqDto.setTypeCd("10");
// 기존 AWS에 업로드
Map<String, Object> retMap = uploader.upload(multipartFile, uploadReqDto); // UploaderConfig Bean 모듈 호출
log.debug("fileUpload : {}", retMap);
UploadResDto uploadResDto = (UploadResDto)((Map<String, Object>)retMap.get("data")).get("data");
result.set(uploadResDto.getUrl());
log.debug("url : {}", uploadResDto.getUrl());
// 기존 AWS 비디오 공간에 업로드
videoUploader.upload(multipartFile, uploadReqDto);
// 파일 시스템에 업로드
fileUploader.upload(multipartFile, uploadReqDto);
});
return result.get();
}
}대용량 엑셀 다운로드
대용량 엑셀 다운로드 구현 시 메모리 효율성을 위해 MyBatis(3.2.4 이상)의 Cursor를 활용하고, @ExcelDownLoad 어노테이션과 ExcelUtil.createCursorExcel 메서드를 사용합니다. Postman을 활용해 테스트를 지원하며, TypeScript 유틸도 제공합니다.
MyBatis XML 쿼리 예시 (Sample.xml)
<!--Dto, Xml 예제-->
<select id="getZipNoList" parameterType="SampleZipNoMgmtRequest" resultType="SampleZipNoMgmtResponse" >
WITH TMP1 AS (
SELECT ZIP_NO_SEQ, ZIP_NO, CTP_NM, SIG_NM, HEMD_NM, LNBR_MNNM, LNBR_SLNO, ROAD_NM
FROM ST_ZIP_NO
WHERE USE_YN = 'Y'
<if test="ctpNmParam != null and ctpNmParam != ''">
AND CTP_NM = #{ctpNmParam}
</if>
<if test="sigNmParam != null and sigNmParam != ''">
AND SIG_NM LIKE '%' || #{sigNmParam}
</if>
)
SELECT ZIP_NO_SEQ, ZIP_NO, CTP_NM, SIG_NM, HEMD_NM, LNBR_MNNM, LNBR_SLNO, ROAD_NM
FROM TMP1
ORDER BY 1
LIMIT 1000000
</select>Service 구현 예시 (ExcelUtil.createCursorExcel)
/* SampleServiceImpl.java */
@ExcelDownLoad
public void exceldown(SampleZipNoMgmtRequest sampleZipNoMgmtRequest) {
// sample 1
ExcelUtil.createCursorExcel(() -> sampleMapper.getZipNoList(sampleZipNoMgmtRequest));
// sample 2 (추가 옵션)
ExcelUtil.createCursorExcel(
() -> sampleMapper.getZipNoList(zipNoMgmtRequest),
ExcelEntity.builder()
.fileName("TRGMN_LIST")
.sheetName("SHEET1")
.build()
);
}참고: Postman 샘플파일 [Sample].postman_collection.json
Postman에서 Excel 다운로드 테스트 시 Send and Download 옵션을 사용합니다.
대용량 엑셀 다운로드 시, 제공되는 download-utils.ts의 downloadStaticFile을 사용합니다:
downloadStaticFile({
method: 'get',
fileUrl: '/api/bo/v1/sample/exceldown',
downloadedFileName: '파일명'
});Attachments
Document generated by Confluence on 2025-12-18 03:31 오전
Atlassian: http://www.atlassian.com/
마지막 업데이트