Spring Boot과 AWS S3로 파일 업로드/다운로드 구현
Spring Boot 애플리케이션에서 AWS S3를 연동해 파일 업로드와 다운로드를 단계별 예제 코드로 설명하며 설정, 권한, 성능 고려사항까지 포함한 설명
목차
개요
이 글에서는 Spring Boot에서 AWS S3를 연동해 파일을 업로드하고 다운로드하는 실무적 구성과 코드를 다룬다. 처음 접하는 개발자도 이해할 수 있도록 의존성 추가부터 권한 설정, 업로드/다운로드 구현, 성능 및 보안 고려사항까지 순서대로 정리한다. 주요 키워드는 spring boot s3 업로드 예제, aws s3 spring boot 연동, spring boot upload s3 이다.
사전 준비
다음 항목이 준비되어야 한다.
- AWS 계정 및 S3 버킷 생성
- AWS IAM 사용자(또는 역할)와 S3 접근 권한 부여
- Spring Boot 프로젝트 생성(Maven/Gradle)
의존성
Spring Boot에서 AWS SDK v2를 사용하거나 SDK v1을 선택할 수 있다. 여기서는 비교적 간단한 AWS SDK for Java v1 예제를 사용한다.
com.amazonaws:aws-java-sdk-s3:1.12.XXX
org.springframework.boot:spring-boot-starter-web
설정(application.properties)
자격증명은 환경변수나 EC2 IAM 역할을 권장한다. 간단한 로컬 개발 예시는 properties에 넣을 수 있다.
cloud.aws.credentials.access-key=YOUR_ACCESS_KEY
cloud.aws.credentials.secret-key=YOUR_SECRET_KEY
cloud.aws.region.static=ap-northeast-2
app.s3.bucket=your-bucket-name
S3 클라이언트 설정
AmazonS3 빈을 생성해 재사용한다. Java 코드에서는 빈을 통해 업로드/다운로드를 처리한다.
package com.example.config;
import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class S3Config {
@Value("${cloud.aws.credentials.access-key}")
private String accessKey;
@Value("${cloud.aws.credentials.secret-key}")
private String secretKey;
@Value("${cloud.aws.region.static}")
private String region;
@Bean
public AmazonS3 amazonS3() {
BasicAWSCredentials creds = new BasicAWSCredentials(accessKey, secretKey);
return AmazonS3ClientBuilder.standard()
.withRegion(region)
.withCredentials(new AWSStaticCredentialsProvider(creds))
.build();
}
}
파일 업로드 서비스
MultipartFile을 받아 S3에 저장하고 URL이나 키를 반환한다. 스트리밍 업로드로 큰 파일도 안정적으로 처리한다.
package com.example.service;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.model.ObjectMetadata;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.UUID;
@Service
public class S3Service {
private final AmazonS3 amazonS3;
@Value("${app.s3.bucket}")
private String bucket;
public S3Service(AmazonS3 amazonS3) {
this.amazonS3 = amazonS3;
}
public String upload(MultipartFile file) throws IOException {
String key = UUID.randomUUID().toString() + "_" + file.getOriginalFilename();
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(file.getSize());
metadata.setContentType(file.getContentType());
amazonS3.putObject(bucket, key, file.getInputStream(), metadata);
return key;
}
public byte[] download(String key) {
try (var s3is = amazonS3.getObject(bucket, key).getObjectContent()) {
return s3is.readAllBytes();
} catch (IOException e) {
throw new RuntimeException("S3 다운로드 실패", e);
}
}
}
컨트롤러
간단한 REST API로 업로드와 다운로드 엔드포인트를 제공한다. 다운로드는 스트리밍 응답으로 구현한다.
package com.example.controller;
import com.example.service.S3Service;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
@RestController
@RequestMapping("/files")
public class FileController {
private final S3Service s3Service;
public FileController(S3Service s3Service) {
this.s3Service = s3Service;
}
@PostMapping("/upload")
public ResponseEntity<String> upload(@RequestParam("file") MultipartFile file) throws Exception {
String key = s3Service.upload(file);
return ResponseEntity.ok(key);
}
@GetMapping("/download/{key}")
public ResponseEntity<byte[]> download(@PathVariable String key) {
byte[] data = s3Service.download(key);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + key + "\"")
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(data);
}
}
권한과 보안
IAM 정책은 최소 권한으로 설정한다. 일반적으로 다음 권한만 허용한다.
- s3:PutObject
- s3:GetObject
- s3:ListBucket(필요 시)
자격증명은 코드에 하드코딩하지 않고 환경변수, 프로파일, 또는 AWS IAM 역할을 활용한다. 공개 버킷 사용은 피한다.
성능 및 비용 고려사항
- 큰 파일은 멀티파트 업로드를 사용하면 안정성과 속도를 개선할 수 있다.
- 빈번한 작은 파일 업로드는 비용 상승 요인이므로 합치기 전략 고려.
- 프리사인드 URL을 활용하면 서버 부하를 줄이고 직접 S3로 업로드 가능.
프리사인드 URL 예시
public String generatePresignedUrl(String key) {
java.util.Date expiration = new java.util.Date();
long expTimeMillis = expiration.getTime();
expTimeMillis += 1000 * 60 * 60; // 1 hour
expiration.setTime(expTimeMillis);
com.amazonaws.services.s3.model.GeneratePresignedUrlRequest generatePresignedUrlRequest =
new com.amazonaws.services.s3.model.GeneratePresignedUrlRequest(bucket, key)
.withMethod(com.amazonaws.HttpMethod.PUT)
.withExpiration(expiration);
return amazonS3.generatePresignedUrl(generatePresignedUrlRequest).toString();
}
요약 및 권장 흐름
핵심 흐름은 다음과 같다.
- 의존성 추가 및 AmazonS3 빈 등록
- IAM 권한 최소화
- MultipartFile로 업로드, 스트리밍으로 다운로드
- 대용량은 멀티파트/프리사인드 사용
위 구현을 바탕으로 테스트와 로깅을 추가하면 운영 환경에서도 안정적으로 S3 연동을 유지할 수 있다. spring boot s3 업로드 예제와 aws s3 spring boot 연동, spring boot upload s3 관련 요구사항을 충족하도록 설계되어 있다.