public String saveImgAndReturn(MultipartFile image){
//이미지 파일인지 검증
String contentType = image.getContentType();
if(contentType == null || contentType.startsWith("image/")){
throw new IllegalArgumentException("Only Image files can be uploaded");
}
String fileName = "jobPost/" + LocalDate.now() + "/" + UUID.randomUUID();
try {
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.key(fileName)
.bucket(bucket)
.contentType(image.getContentType())
.build();
s3Client.putObject(putObjectRequest,RequestBody.fromInputStream(image.getInputStream(), image.getSize()));
String url = s3Client.utilities().getUrl(a->a.bucket(bucket).key(fileName)).toString();
//이미지나 첨부파일 테이블에 저장로직
//Attachment attachment = Attachment.builder.~~~.builde();
// attachmentRepository.save(attachment);
return url;
} catch (IOException e){
throw new RuntimeException("image does not saved");
}
1. 이 파일이 이미지인지 아닌지 검증하려면 MIME 타입을 검증해야한다.
String contentType = image.getContentType();
if(contentType == null || !contentType.startsWith("image/")){
throw new IllegalArgumentException("Only Image files can be uploaded");
}
*MIME (Multipurpost Internet Mail Extensions) 타입이란?
원래는 이메일에서 첨부파일 구분용으로 시작되었지만, 현재는 HTTP통신에서 첨부되는 파일의 종류를 나타내는 표준이다.
이 MIME타입은 HTTP 요청/응답의 헤더에 Contet-Type 키값에 대한 Value로 들어온다.
ex.
- Content - Type : application/json ->JSON 데이터
- Content - Type : image/jpg(혹은 png,jpeg,gif) ->이미지
- Content - Type : text/plain ->일반 텍스트
- Content - Type : application/pdf ->PDF 파일
다시 본론으로 돌아와서 말하자면,
사용자가 파일을 업로드하면 브라우저가 자동으로 인식하여 해당 파일에 알맞는 Content-Type을 보내준다.
사실 파일의 확장자로도 검증을 할 수 있으나, 사용자가 파일명을 .jpg로 바꾸면 원래 .exe였던 악의적인 파일도 이미지 확장자가 되니까 확장자로만 검증하는 것은 신뢰할 수 없는 방법이다.
그래서 해당 파일에 MIME타입으로 검증하는 코드를 심어놓은 것이다.
2.S3 버킷에 폴더별로 저장하려면 prefix를 사용하면 된다.
String fileName = "jobPost/" + LocalDate.now() + "/" + UUID.randomUUID();
try {
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.key(fileName)
.bucket(bucket)
.contentType(image.getContentType())
.build();
s3Client.putObject(putObjectRequest,RequestBody.fromInputStream(image.getInputStream(), image.getSize()));
s3에 저장하는 key값으로 fileName 문자열을 사용했고,
그 문자열은 jobPost/ 와 LocalDate.now()/로 prefix(접두어?)를 사용했다.
그러면 S3 버킷에는 아래처럼 jobPost 폴더가 생기고 또 그안에는 날짜별로 폴더에 저장될 수 있다.


3.S3에 파일을 전송할 때는 스트리밍 방식을 사용하는 것이 효율적이다.
s3Client.putObject(putObjectRequest,RequestBody.fromInputStream(image.getInputStream(), image.getSize()));
파일을 읽어 S3에 전송하는 방식에는 위 코드처럼 RequestBody.fromInputStream()방식도 있고,
또 RequestBody.fromBytes(image.getBytes()) 방식도 있다.
첫번째 방법은 InputStream을 통해 파일 데이터를 조금씩 읽어 네트워크로 바로 전송하는 방식이다.
이 방식은 메모리 효율적이라 대용량 파일에도 안전하다.
두번째 방법은 파일 전체를 byte[]로 메모리에 로드 하여 S3에 한 방에 업로드한다.
이 경우에는 파일 크기 만큼 메모리를 즉시 점유한다.
예를 들어, 100MB 파일 업로드 시, 서버 메모리에 그대로 100MB가 그대로 올라간다.
그렇다면 수많은 유저가 동시에 이 메서드를 호출할 경우 OutOfMemory 에러가 날 리스크가 존재하는 것이다.
따라서, 실 서비스에서 안전한 방식인 InputStream방식을 택하는 것을 권한다
'스프링부트 > Code Tip' 카테고리의 다른 글
| Presigned URL을 통한 S3 업로드 (1) | 2026.02.02 |
|---|---|
| [Spring] 커스텀 어노테이션 만들기 - 2.메서드 실행시간 측정 어노테이션 (1) | 2025.12.22 |
| [Spring] 커스텀 어노테이션 만들기- 1.AOP란? (5) | 2025.12.22 |
| TMDB API를 통한 드라마/영화 정보 받아오기 (1) | 2025.09.15 |