코드의 여백

Spring Boot와 AWS S3 & CloudFront로 파일 관리 설계하기

by rowing0328

Intro

이번 글에서는 Spring Boot를 중심으로 AWS S3와 CloudFront를 연동해 파일 관리를 설계한다.

 

 

AWS S3란

대규모 파일 저장을 위한 객체 스토리지 서비스로, 확장성과 안정성이 높다.

업로드된 파일은 S3 버킷(bucket)에 저장되며, 객체(object) 단위로 관리된다.

 

간단히 객체 스토리지 이해하기

  • 버킷
    파일을 저장하는 논리적 컨테이너로, 고유한 이름을 가진다.
  • 객체
    파일 데이터와 메타데이터의 조합으로 객체는 버킷 내에서 키(Key)로 식별된다.
  • 스토리지 클래스
    Standard, Intelligent-Tiering, Glacier 등 비용 효율성을 고려한 여러 저장 옵션이 있다.
  • ACL 및 권한 제어
    S3는 AWS IAM(Identity and Access Management)과 연동되어 파일 접근을 세밀히 제어할 수 있다.

 

파일 버전 관리와 수명 주기
S3는 버전 관리를 활성화하면 파일 변경 이력을 보존할 수 있다.

수명 주기 규칙을 통해 오래된 파일을 자동으로 Galacier로 이동하거나 삭제할 수 있다.

 

 

CloudFront란

CDN(Content Delivery Network) 서비스로, 사용자와 가까운 엣지 로케이션(edge location)을 통해 파일 전송 속도를 최적화한다.

CloudFront와 S3를 함께 사용하면 전송 성능을 극대화하고, HTTPS를 통한 보안 강화를 손쉽게 구현할 수 있다.

 

간단히 알아보는 CloudFront의 주요 장점

  • 전송 속도 향상
    엣지 로케이션을 통해 전 세계 사용자와 가까운 서버에서 콘텐츠를 제공한다.
  • 캐싱 및 TTL
    CloudFront는 캐싱된 파일의 TTL(Time to Live)을 설정해 요청 횟수를 줄이고 비용을 절감한다.
  • 보안 강화
    HTTPS 지원, 사용자별 서명 URL 생성이 가능하다.

 

CloudFront와 S3 연동하기

CloudFront 배포를 생성한 후 S3 버킷을 원본(origin)으로 설정한다.

사용자 요청은 CloudFront를 통해 전달되며, 캐싱된 콘텐츠를 제공받거나 S3에서 직접 데이터를 가져온다.

 

 

AWS SDK for Java란

SDK(Software Development Kit)는 소프트웨어 개발을 위한 도구 모음이다.

 

Java 애플리케이션에서 AWS 서비스를 쉽게 사용할 수 있도록 해주는 라이브러리이며,

현재 V1과 V2 두 가지 주요 버전이 존재한다.

 

주요 차이점

  • 패키지 이름
    V1 : 'com.amazonaws'
    V2 : 'software.amazon.awssdk'
  • Java 버전 지원
    V1 : Java 6 이상
    V2 : Java 8 이상
  • 비동기 프로그래밍
    V2는 'CompletableFuture'를 사용한 개선된 비동기 지원 제공
  • 모듈화
    V2는 각 서비스별로 별도의 모듈 제공, 필요한 서비스만 포함 가능하다.
  • 페이지네이션
    V2는 자동 페이지네이션 기능 제공한다.
  • HTTP 클라이언트
    V2는 사용자 정의 HTTP 클라이언트가 구성 가능하다.

 

성능 개선
V2는 Lambda 함수의 SDK 시작 시간 성능이 개선되었다.

 

마이그레이션

V1에서 V2로의 마이그레이션 가이드가 제공되며, 두 버전을 동시에 사용할 수도 있다.

 

V2 사용 시 주의사항

일부 고수준 라이브러리는 아직 V2에서 사용할 수 없다.

문서화가 완벽하지 않을 수 있으므로 소스 코드를 참조해야 할 때도 있다.

 

지원 종료 계획

AWS는 V1 SDK의 지원 종료 계획을 발표했다.

이에 따라 V1 SDK는 2024년 7월 31일부터 유지보수 모드에 들어가며,

2025년 12월 31일에 공식 지원이 종료될 예정이다.

 

 

SpringBoot와 AWS SDK 연동하기

Spring Boot 애플리케이션에서 AWS 서비스를 사용하려면 AWS SDK를 연동해야 한다.

 

1. 의존성 추가

  • Maven이나 Gradle 프로젝트에서 AWS SDK for S3 의존성을 추가한다.
// Maven
<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>s3</artifactId>
    <version>2.20.14</version>
</dependency>

// Gradle
dependencies {
    implementation 'software.amazon.awssdk:s3:2.20.14'
}

 

2. Amazon S3 클라이언트 생성

  • AWS 자격 증명을 활용해 S3 클라이언트를 생성한다.
@Configuration
public class S3Config {

    private final String accessKey;
    private final String secretKey;
    private final String region;

    public S3Config(
            @Value("${cloud.aws.credentials.access-key}") String accessKey,
            @Value("${cloud.aws.credentials.secret-key}") String secretKey,
            @Value("${cloud.aws.region.static}") String region
    ) {
        this.accessKey = accessKey;
        this.secretKey = secretKey;
        this.region = region;
    }

    @Bean
    public AmazonS3Client amazonS3Client() {
        BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);

        return (AmazonS3Client) AmazonS3ClientBuilder
                .standard()
                .withRegion(region)
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .build();
    }

}

 

3. 파일 업로드 & 다운로드 구현

  • Spring Boot와 S3를 연동해 간단한 파일 업로드 및 다운로드 기능을 구현할 수 있다.
// 파일 업로드
public String uploadFile(MultipartFile file) {
    String fileName = file.getOriginalFilename();
    ObjectMetadata metadata = new ObjectMetadata();
    metadata.setContentLength(file.getSize());

    try (InputStream inputStream = file.getInputStream()) {
        amazonS3.putObject(new PutObjectRequest("my-bucket", fileName, inputStream, metadata));
    } catch (IOException e) {
        throw new RuntimeException("파일 업로드 중 에러 발생", e);
    }

    return "File uploaded successfully!";
}

// 파일 다운로드
public S3Object downloadFile(String fileName) {
    return amazonS3.getObject("my-bucket", fileName);
}

 

4. 파일 조회 구현

  • 다음으로는 간단한 조회 기능을 구현할 수 있다.
public boolean isFileExist(String bucketPath, String fileName) {
	return s3Client.doesObjectExist(bucketPath, fileName);
}

 

5. 파일 삭제 & 복사 구현

  • 다음으로는 간단한 파일 이동 및 삭제 기능을 구현할 수 있다.
// 파일 복사
public void copyFile(String sourceKey, String destinationKey) {
    try {
        s3Client.copyObject(sourceBucket, sourceKey, destinationBucket, destinationKey);
        log.info("파일 복사 성공: {} -> {}", sourceKey, destinationKey);
    } catch (Exception e) {
    	log.error("파일 복사 중 에러 발생: {}", e.getMessage(), e);
	}
}

// 파일 삭제
public void deleteFile(String key) {
	try {
    	s3Client.deleteObject(bucketName, key);
        log.info("파일 삭제 성공: {}", key);
	} catch (Exception e) {
		log.error("파일 삭제 중 에러 발생: {}", e.getMessage(), e);
	}
}

 

6. CloudFront 서명 URL과 만료시간을 활용한 보안 강화

  • S3 파일에 대한 퍼블릭 접근을 제한하고, CloudFront 서명 URL 만료 시간 설정으로 보안을 강화할 수 있다.
import com.amazonaws.services.cloudfront.CloudFrontUrlSigner;
import lombok.extern.slf4j.Slf4j;

import java.util.Date;

@Slf4j
public class CloudFrontSignedUrlGenerator {

    private static final String DISTRIBUTION_DOMAIN = "your-distribution.cloudfront.net"; // CloudFront 배포 도메인
    private static final String PRIVATE_KEY_PATH = "/path/to/private/key.pem"; // PEM 형식의 개인 키 파일
    private static final String KEY_PAIR_ID = "your-key-pair-id"; // CloudFront Key Pair ID

    public String generateSignedUrl(String objectKey) {
        try {
            // CloudFront URL
            String resourceUrl = "https://" + DISTRIBUTION_DOMAIN + "/" + objectKey;

            // URL 만료 시간 설정 (예: 현재로부터 1시간 후)
            Date expirationDate = new Date(System.currentTimeMillis() + (60 * 60 * 1000)); // 1시간 후

            // 서명된 URL 생성
            String signedUrl = CloudFrontUrlSigner.getSignedURLWithCannedPolicy(
                    resourceUrl,
                    KEY_PAIR_ID,
                    PRIVATE_KEY_PATH,
                    expirationDate
            );

            log.info("서명된 URL 생성 성공: {}", signedUrl);
            return signedUrl;

        } catch (Exception e) {
            log.error("서명 URL 생성 중 에러 발생: {}", e.getMessage(), e);
            throw new RuntimeException("CloudFront 서명 URL 생성 실패", e);
        }
    }

    public static void main(String[] args) {
        CloudFrontSignedUrlGenerator generator = new CloudFrontSignedUrlGenerator();
        String signedUrl = generator.generateSignedUrl("example-file.jpg");
        log.info("생성된 서명 URL: {}", signedUrl);
    }
    
}

 

 

마무리

Spring Boot 애플리케이션에서 AWS SDK를 사용해 파일을 처리하는 과정은

비교적 직관적이었지만, CloudFront 서명 URL 생성이나 IAM 권한 설정과 같은

세부적인 부분에서는 어려움도 있었다.

 

특히, 서명 URL을 생성할 때 PEM 키 파일과 만료 시간을 설정하는 과정에서

몇 가지 시행착오를 겪었지만, 이를 해결하며 많은 것을 배울 수 있었다.

 

가장 인상 깊었던 점은 AWS 서비스를 활용하면 단순히 파일 관리 기능을

구현하는 데 그치지 않고, 확장성과 보안까지 고려한 설계를

쉽게 적용할 수 있다는 점이다.

 

 

참고 자료 :

AWS SDK for Java v1.x에 대한 지원 종료 계획 발표문

 

Announcing end-of-support for AWS SDK for Java v1.x effective December 31, 2025 | Amazon Web Services

In alignment with our SDKs and Tools Maintenance Policy, the AWS SDK for Java v1.x will enter maintenance mode on July 31, 2024, and reach end-of-support on December 31, 2025. Existing applications that use AWS SDK for Java v1.x will continue to function a

aws.amazon.com

 

Developer Guide - AWS SDK for Java 1.x

 

Developer Guide - AWS SDK for Java 1.x - AWS SDK for Java 1.x

Thanks for letting us know this page needs work. We're sorry we let you down. If you've got a moment, please tell us how we can make the documentation better.

docs.aws.amazon.com

블로그의 정보

코드의 여백

rowing0328

활동하기